mirror of
https://github.com/golang/go
synced 2024-11-05 17:46:16 -07:00
go.tools/cmd/present: add present tool
LGTM=bradfitz R=golang-codereviews, bradfitz CC=golang-codereviews https://golang.org/cl/97030045
This commit is contained in:
parent
9f1412014f
commit
ca18093fea
24
cmd/present/appengine.go
Normal file
24
cmd/present/appengine.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/present"
|
||||
|
||||
_ "code.google.com/p/go.tools/playground"
|
||||
)
|
||||
|
||||
var basePath = "./present/"
|
||||
|
||||
func init() {
|
||||
playScript(basePath, "HTTPTransport")
|
||||
present.PlayEnabled = true
|
||||
}
|
||||
|
||||
func playable(c present.Code) bool {
|
||||
return present.PlayEnabled && c.Play && c.Ext == ".go"
|
||||
}
|
281
cmd/present/dir.go
Normal file
281
cmd/present/dir.go
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"code.google.com/p/go.tools/present"
|
||||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/", dirHandler)
|
||||
}
|
||||
|
||||
// dirHandler serves a directory listing for the requested path, rooted at basePath.
|
||||
func dirHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/favicon.ico" {
|
||||
http.Error(w, "not found", 404)
|
||||
return
|
||||
}
|
||||
const base = "."
|
||||
name := filepath.Join(base, r.URL.Path)
|
||||
if isDoc(name) {
|
||||
err := renderDoc(w, basePath, name)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
return
|
||||
}
|
||||
if isDir, err := dirList(w, name); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
} else if isDir {
|
||||
return
|
||||
}
|
||||
http.FileServer(http.Dir(base)).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// extensions maps the presentable file extensions to the name of the
|
||||
// template to be executed.
|
||||
var extensions = map[string]string{
|
||||
".slide": "slides.tmpl",
|
||||
".article": "article.tmpl",
|
||||
}
|
||||
|
||||
func isDoc(path string) bool {
|
||||
_, ok := extensions[filepath.Ext(path)]
|
||||
return ok
|
||||
}
|
||||
|
||||
// renderDoc reads the present file, builds its template representation,
|
||||
// and executes the template, sending output to w.
|
||||
func renderDoc(w io.Writer, base, docFile string) error {
|
||||
// Read the input and build the doc structure.
|
||||
doc, err := parse(docFile, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find which template should be executed.
|
||||
ext := filepath.Ext(docFile)
|
||||
contentTmpl, ok := extensions[ext]
|
||||
if !ok {
|
||||
return fmt.Errorf("no template for extension %v", ext)
|
||||
}
|
||||
|
||||
// Locate the template file.
|
||||
actionTmpl := filepath.Join(base, "templates/action.tmpl")
|
||||
contentTmpl = filepath.Join(base, "templates", contentTmpl)
|
||||
|
||||
// Read and parse the input.
|
||||
tmpl := present.Template()
|
||||
tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
|
||||
if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute the template.
|
||||
return doc.Render(w, tmpl)
|
||||
}
|
||||
|
||||
func parse(name string, mode present.ParseMode) (*present.Doc, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return present.Parse(f, name, 0)
|
||||
}
|
||||
|
||||
// dirList scans the given path and writes a directory listing to w.
|
||||
// It parses the first part of each .slide file it encounters to display the
|
||||
// presentation title in the listing.
|
||||
// If the given path is not a directory, it returns (isDir == false, err == nil)
|
||||
// and writes nothing to w.
|
||||
func dirList(w io.Writer, name string) (isDir bool, err error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if isDir = fi.IsDir(); !isDir {
|
||||
return false, nil
|
||||
}
|
||||
fis, err := f.Readdir(0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
d := &dirListData{Path: name}
|
||||
for _, fi := range fis {
|
||||
// skip the pkg directory
|
||||
if name == "." && fi.Name() == "pkg" {
|
||||
continue
|
||||
}
|
||||
e := dirEntry{
|
||||
Name: fi.Name(),
|
||||
Path: filepath.ToSlash(filepath.Join(name, fi.Name())),
|
||||
}
|
||||
if fi.IsDir() && showDir(e.Name) {
|
||||
d.Dirs = append(d.Dirs, e)
|
||||
continue
|
||||
}
|
||||
if isDoc(e.Name) {
|
||||
if p, err := parse(e.Path, present.TitlesOnly); err != nil {
|
||||
log.Println(err)
|
||||
} else {
|
||||
e.Title = p.Title
|
||||
}
|
||||
switch filepath.Ext(e.Path) {
|
||||
case ".article":
|
||||
d.Articles = append(d.Articles, e)
|
||||
case ".slide":
|
||||
d.Slides = append(d.Slides, e)
|
||||
}
|
||||
} else if showFile(e.Name) {
|
||||
d.Other = append(d.Other, e)
|
||||
}
|
||||
}
|
||||
if d.Path == "." {
|
||||
d.Path = ""
|
||||
}
|
||||
sort.Sort(d.Dirs)
|
||||
sort.Sort(d.Slides)
|
||||
sort.Sort(d.Articles)
|
||||
sort.Sort(d.Other)
|
||||
return true, dirListTemplate.Execute(w, d)
|
||||
}
|
||||
|
||||
// showFile reports whether the given file should be displayed in the list.
|
||||
func showFile(n string) bool {
|
||||
switch filepath.Ext(n) {
|
||||
case ".pdf":
|
||||
case ".html":
|
||||
case ".go":
|
||||
default:
|
||||
return isDoc(n)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// showDir reports whether the given directory should be displayed in the list.
|
||||
func showDir(n string) bool {
|
||||
if len(n) > 0 && (n[0] == '.' || n[0] == '_') || n == "present" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type dirListData struct {
|
||||
Path string
|
||||
Dirs, Slides, Articles, Other dirEntrySlice
|
||||
}
|
||||
|
||||
type dirEntry struct {
|
||||
Name, Path, Title string
|
||||
}
|
||||
|
||||
type dirEntrySlice []dirEntry
|
||||
|
||||
func (s dirEntrySlice) Len() int { return len(s) }
|
||||
func (s dirEntrySlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s dirEntrySlice) Less(i, j int) bool { return s[i].Name < s[j].Name }
|
||||
|
||||
var dirListTemplate = template.Must(template.New("").Parse(dirListHTML))
|
||||
|
||||
const dirListHTML = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Talks - The Go Programming Language</title>
|
||||
<link type="text/css" rel="stylesheet" href="/static/dir.css">
|
||||
<script src="/static/dir.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="topbar"><div class="container">
|
||||
|
||||
<form method="GET" action="http://golang.org/search">
|
||||
<div id="menu">
|
||||
<a href="http://golang.org/doc/">Documents</a>
|
||||
<a href="http://golang.org/ref/">References</a>
|
||||
<a href="http://golang.org/pkg/">Packages</a>
|
||||
<a href="http://golang.org/project/">The Project</a>
|
||||
<a href="http://golang.org/help/">Help</a>
|
||||
<input type="text" id="search" name="q" class="inactive" value="Search">
|
||||
</div>
|
||||
<div id="heading"><a href="/">The Go Programming Language</a></div>
|
||||
</form>
|
||||
|
||||
</div></div>
|
||||
|
||||
<div id="page">
|
||||
|
||||
<h1>Go talks</h1>
|
||||
|
||||
{{with .Path}}<h2>{{.}}</h2>{{end}}
|
||||
|
||||
{{with .Articles}}
|
||||
<h4>Articles:</h4>
|
||||
<dl>
|
||||
{{range .}}
|
||||
<dd><a href="/{{.Path}}">{{.Name}}</a>: {{.Title}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
{{end}}
|
||||
|
||||
{{with .Slides}}
|
||||
<h4>Slide decks:</h4>
|
||||
<dl>
|
||||
{{range .}}
|
||||
<dd><a href="/{{.Path}}">{{.Name}}</a>: {{.Title}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
{{end}}
|
||||
|
||||
{{with .Other}}
|
||||
<h4>Files:</h4>
|
||||
<dl>
|
||||
{{range .}}
|
||||
<dd><a href="/{{.Path}}">{{.Name}}</a></dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
{{end}}
|
||||
|
||||
{{with .Dirs}}
|
||||
<h4>Sub-directories:</h4>
|
||||
<dl>
|
||||
{{range .}}
|
||||
<dd><a href="/{{.Path}}">{{.Name}}</a></dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
Except as <a href="https://developers.google.com/site-policies#restrictions">noted</a>,
|
||||
the content of this page is licensed under the
|
||||
Creative Commons Attribution 3.0 License,
|
||||
and code is licensed under a <a href="http://golang.org/LICENSE">BSD license</a>.<br>
|
||||
<a href="http://golang.org/doc/tos.html">Terms of Service</a> |
|
||||
<a href="http://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>`
|
29
cmd/present/doc.go
Normal file
29
cmd/present/doc.go
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
/*
|
||||
Present displays slide presentations and articles. It runs a web server that
|
||||
presents slide and article files from the current directory.
|
||||
|
||||
It may be run as a stand-alone command or an App Engine app.
|
||||
The stand-alone version permits the execution of programs from within a
|
||||
presentation. The App Engine version does not provide this functionality.
|
||||
|
||||
Usage of present:
|
||||
-base="": base path for slide template and static resources
|
||||
-http="127.0.0.1:3999": host:port to listen on
|
||||
|
||||
You may use the app.yaml file provided in the root of the go.talks repository
|
||||
to deploy present to App Engine:
|
||||
appcfg.py update -A your-app-id -V your-app-version /path/to/go.talks
|
||||
|
||||
Input files are named foo.extension, where "extension" defines the format of
|
||||
the generated output. The supported formats are:
|
||||
.slide // HTML5 slide presentation
|
||||
.article // article format, such as a blog post
|
||||
|
||||
The present file format is documented by the present package:
|
||||
http://godoc.org/code.google.com/p/go.tools/present
|
||||
*/
|
||||
package main
|
6
cmd/present/js/jquery-ui.js
vendored
Normal file
6
cmd/present/js/jquery-ui.js
vendored
Normal file
File diff suppressed because one or more lines are too long
122
cmd/present/local.go
Normal file
122
cmd/present/local.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.tools/playground/socket"
|
||||
"code.google.com/p/go.tools/present"
|
||||
)
|
||||
|
||||
const basePkg = "code.google.com/p/go.tools/cmd/present"
|
||||
|
||||
var (
|
||||
basePath string
|
||||
nativeClient bool
|
||||
)
|
||||
|
||||
func main() {
|
||||
httpListen := flag.String("http", "127.0.0.1:3999", "host:port to listen on")
|
||||
flag.StringVar(&basePath, "base", "", "base path for slide template and static resources")
|
||||
flag.BoolVar(&present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")
|
||||
flag.BoolVar(&nativeClient, "nacl", false, "use Native Client environment playground (prevents non-Go code execution)")
|
||||
flag.Parse()
|
||||
|
||||
if basePath == "" {
|
||||
p, err := build.Default.Import(basePkg, "", build.FindOnly)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Couldn't find gopresent files: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, basePathMessage, basePkg)
|
||||
os.Exit(1)
|
||||
}
|
||||
basePath = p.Dir
|
||||
}
|
||||
|
||||
if present.PlayEnabled {
|
||||
if nativeClient {
|
||||
socket.RunScripts = false
|
||||
socket.Environ = func() []string {
|
||||
if runtime.GOARCH == "amd64" {
|
||||
return environ("GOOS=nacl", "GOARCH=amd64p32")
|
||||
}
|
||||
return environ("GOOS=nacl")
|
||||
}
|
||||
}
|
||||
playScript(basePath, "SocketTransport")
|
||||
|
||||
host, port, err := net.SplitHostPort(*httpListen)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
origin := &url.URL{Scheme: "http", Host: host + ":" + port}
|
||||
http.Handle("/socket", socket.NewHandler(origin))
|
||||
}
|
||||
http.Handle("/static/", http.FileServer(http.Dir(basePath)))
|
||||
|
||||
if !strings.HasPrefix(*httpListen, "127.0.0.1") &&
|
||||
!strings.HasPrefix(*httpListen, "localhost") &&
|
||||
present.PlayEnabled && !nativeClient {
|
||||
log.Print(localhostWarning)
|
||||
}
|
||||
|
||||
log.Printf("Open your web browser and visit http://%s/", *httpListen)
|
||||
log.Fatal(http.ListenAndServe(*httpListen, nil))
|
||||
}
|
||||
|
||||
func playable(c present.Code) bool {
|
||||
return present.PlayEnabled && c.Play
|
||||
}
|
||||
|
||||
func environ(vars ...string) []string {
|
||||
env := os.Environ()
|
||||
for _, r := range vars {
|
||||
k := strings.SplitAfter(r, "=")[0]
|
||||
var found bool
|
||||
for i, v := range env {
|
||||
if strings.HasPrefix(v, k) {
|
||||
env[i] = r
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
env = append(env, r)
|
||||
}
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
const basePathMessage = `
|
||||
By default, gopresent locates the slide template files and associated
|
||||
static content by looking for a %q package
|
||||
in your Go workspaces (GOPATH).
|
||||
|
||||
You may use the -base flag to specify an alternate location.
|
||||
`
|
||||
|
||||
const localhostWarning = `
|
||||
WARNING! WARNING! WARNING!
|
||||
|
||||
The present server appears to be listening on an address that is not localhost.
|
||||
Anyone with access to this address and port will have access to this machine as
|
||||
the user running present.
|
||||
|
||||
To avoid this message, listen on localhost or run with -play=false.
|
||||
|
||||
If you don't understand this message, hit Control-C to terminate this process.
|
||||
|
||||
WARNING! WARNING! WARNING!
|
||||
`
|
43
cmd/present/play.go
Normal file
43
cmd/present/play.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/go.tools/godoc/static"
|
||||
)
|
||||
|
||||
var scripts = []string{"jquery.js", "jquery-ui.js", "playground.js", "play.js"}
|
||||
|
||||
// playScript registers an HTTP handler at /play.js that serves all the
|
||||
// scripts specified by the variable above, and appends a line that
|
||||
// initializes the playground with the specified transport.
|
||||
func playScript(root, transport string) {
|
||||
modTime := time.Now()
|
||||
var buf bytes.Buffer
|
||||
for _, p := range scripts {
|
||||
if s, ok := static.Files[p]; ok {
|
||||
buf.WriteString(s)
|
||||
continue
|
||||
}
|
||||
b, err := ioutil.ReadFile(filepath.Join(root, "js", p))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf.Write(b)
|
||||
}
|
||||
fmt.Fprintf(&buf, "\ninitPlayground(new %v());\n", transport)
|
||||
b := buf.Bytes()
|
||||
http.HandleFunc("/play.js", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-type", "application/javascript")
|
||||
http.ServeContent(w, r, "", modTime, bytes.NewReader(b))
|
||||
})
|
||||
}
|
136
cmd/present/static/article.css
Normal file
136
cmd/present/static/article.css
Normal file
@ -0,0 +1,136 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
pre,
|
||||
code {
|
||||
font-family: Menlo, monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
pre {
|
||||
line-height: 18px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
a {
|
||||
color: #375EAB;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
p, ul, ol {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
margin: 20px 0;
|
||||
padding: 0;
|
||||
color: #375EAB;
|
||||
font-weight: bold;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
background: #E0EBF5;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
h3, h4 {
|
||||
margin: 20px 5px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
div#heading {
|
||||
float: left;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 21px 0;
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
div#topbar {
|
||||
background: #E0EBF5;
|
||||
height: 64px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
text-align: center;
|
||||
}
|
||||
div#page {
|
||||
width: 100%;
|
||||
}
|
||||
div#page > .container,
|
||||
div#topbar > .container {
|
||||
text-align: left;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 0 20px;
|
||||
width: 900px;
|
||||
}
|
||||
div#page.wide > .container,
|
||||
div#topbar.wide > .container {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div#footer {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.author p {
|
||||
margin: 20, 0, 0, 0px;
|
||||
}
|
||||
|
||||
div.code,
|
||||
div.output {
|
||||
margin: 20px;
|
||||
padding: 10px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
div.code { background: #e9e9e9; }
|
||||
div.output { background: black; }
|
||||
div.output .stdout { color: #e6e6e6; }
|
||||
div.output .stderr { color: rgb(244, 74, 63); }
|
||||
div.output .system { color: rgb(255, 209, 77) }
|
||||
|
||||
.buttons {
|
||||
margin-left: 20px;
|
||||
}
|
||||
div.output .buttons {
|
||||
margin-left: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#toc {
|
||||
float: right;
|
||||
margin: 0px 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #e5ecf9;
|
||||
background-color: white;
|
||||
max-width: 33%;
|
||||
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#toc ul, #toc a {
|
||||
list-style-type: none;
|
||||
padding-left: 10px;
|
||||
color: black;
|
||||
margin: 0px;
|
||||
}
|
186
cmd/present/static/dir.css
Normal file
186
cmd/present/static/dir.css
Normal file
@ -0,0 +1,186 @@
|
||||
/* copied from $GOROOT/doc/style.css */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
pre,
|
||||
code {
|
||||
font-family: Menlo, monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
pre {
|
||||
line-height: 18px;
|
||||
}
|
||||
pre .comment {
|
||||
color: #375EAB;
|
||||
}
|
||||
pre .highlight,
|
||||
pre .highlight-comment,
|
||||
pre .selection-highlight,
|
||||
pre .selection-highlight-comment {
|
||||
background: #FFFF00;
|
||||
}
|
||||
pre .selection,
|
||||
pre .selection-comment {
|
||||
background: #FF9632;
|
||||
}
|
||||
pre .ln {
|
||||
color: #999;
|
||||
}
|
||||
body {
|
||||
color: #222;
|
||||
}
|
||||
a,
|
||||
.exampleHeading .text {
|
||||
color: #375EAB;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover,
|
||||
.exampleHeading .text:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
p,
|
||||
pre,
|
||||
ul,
|
||||
ol {
|
||||
margin: 20px;
|
||||
}
|
||||
pre {
|
||||
background: #e9e9e9;
|
||||
padding: 10px;
|
||||
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
.rootHeading {
|
||||
margin: 20px 0;
|
||||
padding: 0;
|
||||
color: #375EAB;
|
||||
font-weight: bold;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
background: #E0EBF5;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
h3,
|
||||
h4 {
|
||||
margin: 20px 5px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 20px;
|
||||
}
|
||||
dd {
|
||||
margin: 2px 20px;
|
||||
}
|
||||
dl,
|
||||
dd {
|
||||
font-size: 14px;
|
||||
}
|
||||
div#nav table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
div#heading {
|
||||
float: left;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 21px 0;
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
}
|
||||
div#heading a {
|
||||
color: #222;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div#topbar {
|
||||
background: #E0EBF5;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
body {
|
||||
text-align: center;
|
||||
}
|
||||
div#page,
|
||||
div#topbar > .container {
|
||||
clear: both;
|
||||
text-align: left;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 0 20px;
|
||||
width: 900px;
|
||||
}
|
||||
div#page.wide,
|
||||
div#topbar > .wide {
|
||||
width: auto;
|
||||
}
|
||||
div#plusone {
|
||||
float: right;
|
||||
}
|
||||
|
||||
div#footer {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
div#menu > a,
|
||||
div#menu > input {
|
||||
padding: 10px;
|
||||
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
div#menu > a,
|
||||
div#menu > input {
|
||||
border: 1px solid #375EAB;
|
||||
}
|
||||
div#menu > a {
|
||||
color: white;
|
||||
background: #375EAB;
|
||||
}
|
||||
|
||||
div#menu {
|
||||
float: right;
|
||||
min-width: 590px;
|
||||
padding: 10px 0;
|
||||
text-align: right;
|
||||
}
|
||||
div#menu > a {
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
div#menu > input {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
width: 60px;
|
||||
background: white;
|
||||
color: #222;
|
||||
}
|
||||
div#menu > input.inactive {
|
||||
color: #999;
|
||||
}
|
41
cmd/present/static/dir.js
Normal file
41
cmd/present/static/dir.js
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// copied from $GOROOT/doc/godocs.js
|
||||
|
||||
function bindEvent(el, e, fn) {
|
||||
if (el.addEventListener){
|
||||
el.addEventListener(e, fn, false);
|
||||
} else if (el.attachEvent){
|
||||
el.attachEvent('on'+e, fn);
|
||||
}
|
||||
}
|
||||
|
||||
function godocs_bindSearchEvents() {
|
||||
var search = document.getElementById('search');
|
||||
if (!search) {
|
||||
// no search box (index disabled)
|
||||
return;
|
||||
}
|
||||
function clearInactive() {
|
||||
if (search.className == "inactive") {
|
||||
search.value = "";
|
||||
search.className = "";
|
||||
}
|
||||
}
|
||||
function restoreInactive() {
|
||||
if (search.value !== "") {
|
||||
return;
|
||||
}
|
||||
if (search.type != "search") {
|
||||
search.value = search.getAttribute("placeholder");
|
||||
}
|
||||
search.className = "inactive";
|
||||
}
|
||||
restoreInactive();
|
||||
bindEvent(search, 'focus', clearInactive);
|
||||
bindEvent(search, 'blur', restoreInactive);
|
||||
}
|
||||
|
||||
bindEvent(window, 'load', godocs_bindSearchEvents);
|
BIN
cmd/present/static/favicon.ico
Normal file
BIN
cmd/present/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 785 B |
51
cmd/present/static/print.css
Normal file
51
cmd/present/static/print.css
Normal file
@ -0,0 +1,51 @@
|
||||
/* set page layout */
|
||||
@page {
|
||||
size: A4 landscape;
|
||||
}
|
||||
|
||||
body {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.slides {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.slides > article {
|
||||
position: relative;
|
||||
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
margin: 0 !important;
|
||||
page-break-inside: avoid;
|
||||
|
||||
text-shadow: none; /* disable shadow */
|
||||
|
||||
display: block !important;
|
||||
transform: translate(0) !important;
|
||||
-o-transform: translate(0) !important;
|
||||
-moz-transform: translate(0) !important;
|
||||
-webkit-transform: translate3d(0, 0, 0) !important;
|
||||
}
|
||||
|
||||
div.code {
|
||||
background: rgb(240, 240, 240);
|
||||
}
|
||||
|
||||
/* hide click areas */
|
||||
.slide-area, #prev-slide-area, #next-slide-area {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* add explicit links */
|
||||
a:link:after, a:visited:after {
|
||||
content: " (" attr(href) ") ";
|
||||
font-size: 50%;
|
||||
}
|
||||
|
||||
/* white background */
|
||||
body {
|
||||
background: rgb(255,255,255) !important;
|
||||
}
|
516
cmd/present/static/slides.js
Normal file
516
cmd/present/static/slides.js
Normal file
@ -0,0 +1,516 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
var PERMANENT_URL_PREFIX = '/static/';
|
||||
|
||||
var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next'];
|
||||
|
||||
var PM_TOUCH_SENSITIVITY = 15;
|
||||
|
||||
var curSlide;
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* classList polyfill by Eli Grey
|
||||
* (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */
|
||||
|
||||
if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
|
||||
|
||||
(function (view) {
|
||||
|
||||
var
|
||||
classListProp = "classList"
|
||||
, protoProp = "prototype"
|
||||
, elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
|
||||
, objCtr = Object
|
||||
strTrim = String[protoProp].trim || function () {
|
||||
return this.replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
, arrIndexOf = Array[protoProp].indexOf || function (item) {
|
||||
for (var i = 0, len = this.length; i < len; i++) {
|
||||
if (i in this && this[i] === item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
// Vendors: please allow content code to instantiate DOMExceptions
|
||||
, DOMEx = function (type, message) {
|
||||
this.name = type;
|
||||
this.code = DOMException[type];
|
||||
this.message = message;
|
||||
}
|
||||
, checkTokenAndGetIndex = function (classList, token) {
|
||||
if (token === "") {
|
||||
throw new DOMEx(
|
||||
"SYNTAX_ERR"
|
||||
, "An invalid or illegal string was specified"
|
||||
);
|
||||
}
|
||||
if (/\s/.test(token)) {
|
||||
throw new DOMEx(
|
||||
"INVALID_CHARACTER_ERR"
|
||||
, "String contains an invalid character"
|
||||
);
|
||||
}
|
||||
return arrIndexOf.call(classList, token);
|
||||
}
|
||||
, ClassList = function (elem) {
|
||||
var
|
||||
trimmedClasses = strTrim.call(elem.className)
|
||||
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
|
||||
;
|
||||
for (var i = 0, len = classes.length; i < len; i++) {
|
||||
this.push(classes[i]);
|
||||
}
|
||||
this._updateClassName = function () {
|
||||
elem.className = this.toString();
|
||||
};
|
||||
}
|
||||
, classListProto = ClassList[protoProp] = []
|
||||
, classListGetter = function () {
|
||||
return new ClassList(this);
|
||||
}
|
||||
;
|
||||
// Most DOMException implementations don't allow calling DOMException's toString()
|
||||
// on non-DOMExceptions. Error's toString() is sufficient here.
|
||||
DOMEx[protoProp] = Error[protoProp];
|
||||
classListProto.item = function (i) {
|
||||
return this[i] || null;
|
||||
};
|
||||
classListProto.contains = function (token) {
|
||||
token += "";
|
||||
return checkTokenAndGetIndex(this, token) !== -1;
|
||||
};
|
||||
classListProto.add = function (token) {
|
||||
token += "";
|
||||
if (checkTokenAndGetIndex(this, token) === -1) {
|
||||
this.push(token);
|
||||
this._updateClassName();
|
||||
}
|
||||
};
|
||||
classListProto.remove = function (token) {
|
||||
token += "";
|
||||
var index = checkTokenAndGetIndex(this, token);
|
||||
if (index !== -1) {
|
||||
this.splice(index, 1);
|
||||
this._updateClassName();
|
||||
}
|
||||
};
|
||||
classListProto.toggle = function (token) {
|
||||
token += "";
|
||||
if (checkTokenAndGetIndex(this, token) === -1) {
|
||||
this.add(token);
|
||||
} else {
|
||||
this.remove(token);
|
||||
}
|
||||
};
|
||||
classListProto.toString = function () {
|
||||
return this.join(" ");
|
||||
};
|
||||
|
||||
if (objCtr.defineProperty) {
|
||||
var classListPropDesc = {
|
||||
get: classListGetter
|
||||
, enumerable: true
|
||||
, configurable: true
|
||||
};
|
||||
try {
|
||||
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
||||
} catch (ex) { // IE 8 doesn't support enumerable:true
|
||||
if (ex.number === -0x7FF5EC54) {
|
||||
classListPropDesc.enumerable = false;
|
||||
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
||||
}
|
||||
}
|
||||
} else if (objCtr[protoProp].__defineGetter__) {
|
||||
elemCtrProto.__defineGetter__(classListProp, classListGetter);
|
||||
}
|
||||
|
||||
}(self));
|
||||
|
||||
}
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* Slide movement */
|
||||
|
||||
function getSlideEl(no) {
|
||||
if ((no < 0) || (no >= slideEls.length)) {
|
||||
return null;
|
||||
} else {
|
||||
return slideEls[no];
|
||||
}
|
||||
};
|
||||
|
||||
function updateSlideClass(slideNo, className) {
|
||||
var el = getSlideEl(slideNo);
|
||||
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (className) {
|
||||
el.classList.add(className);
|
||||
}
|
||||
|
||||
for (var i in SLIDE_CLASSES) {
|
||||
if (className != SLIDE_CLASSES[i]) {
|
||||
el.classList.remove(SLIDE_CLASSES[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function updateSlides() {
|
||||
for (var i = 0; i < slideEls.length; i++) {
|
||||
switch (i) {
|
||||
case curSlide - 2:
|
||||
updateSlideClass(i, 'far-past');
|
||||
break;
|
||||
case curSlide - 1:
|
||||
updateSlideClass(i, 'past');
|
||||
break;
|
||||
case curSlide:
|
||||
updateSlideClass(i, 'current');
|
||||
break;
|
||||
case curSlide + 1:
|
||||
updateSlideClass(i, 'next');
|
||||
break;
|
||||
case curSlide + 2:
|
||||
updateSlideClass(i, 'far-next');
|
||||
break;
|
||||
default:
|
||||
updateSlideClass(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
triggerLeaveEvent(curSlide - 1);
|
||||
triggerEnterEvent(curSlide);
|
||||
|
||||
window.setTimeout(function() {
|
||||
// Hide after the slide
|
||||
disableSlideFrames(curSlide - 2);
|
||||
}, 301);
|
||||
|
||||
enableSlideFrames(curSlide - 1);
|
||||
enableSlideFrames(curSlide + 2);
|
||||
|
||||
updateHash();
|
||||
};
|
||||
|
||||
function prevSlide() {
|
||||
if (curSlide > 0) {
|
||||
curSlide--;
|
||||
|
||||
updateSlides();
|
||||
}
|
||||
};
|
||||
|
||||
function nextSlide() {
|
||||
if (curSlide < slideEls.length - 1) {
|
||||
curSlide++;
|
||||
|
||||
updateSlides();
|
||||
}
|
||||
};
|
||||
|
||||
/* Slide events */
|
||||
|
||||
function triggerEnterEvent(no) {
|
||||
var el = getSlideEl(no);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
var onEnter = el.getAttribute('onslideenter');
|
||||
if (onEnter) {
|
||||
new Function(onEnter).call(el);
|
||||
}
|
||||
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('slideenter', true, true);
|
||||
evt.slideNumber = no + 1; // Make it readable
|
||||
|
||||
el.dispatchEvent(evt);
|
||||
};
|
||||
|
||||
function triggerLeaveEvent(no) {
|
||||
var el = getSlideEl(no);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
var onLeave = el.getAttribute('onslideleave');
|
||||
if (onLeave) {
|
||||
new Function(onLeave).call(el);
|
||||
}
|
||||
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('slideleave', true, true);
|
||||
evt.slideNumber = no + 1; // Make it readable
|
||||
|
||||
el.dispatchEvent(evt);
|
||||
};
|
||||
|
||||
/* Touch events */
|
||||
|
||||
function handleTouchStart(event) {
|
||||
if (event.touches.length == 1) {
|
||||
touchDX = 0;
|
||||
touchDY = 0;
|
||||
|
||||
touchStartX = event.touches[0].pageX;
|
||||
touchStartY = event.touches[0].pageY;
|
||||
|
||||
document.body.addEventListener('touchmove', handleTouchMove, true);
|
||||
document.body.addEventListener('touchend', handleTouchEnd, true);
|
||||
}
|
||||
};
|
||||
|
||||
function handleTouchMove(event) {
|
||||
if (event.touches.length > 1) {
|
||||
cancelTouch();
|
||||
} else {
|
||||
touchDX = event.touches[0].pageX - touchStartX;
|
||||
touchDY = event.touches[0].pageY - touchStartY;
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
function handleTouchEnd(event) {
|
||||
var dx = Math.abs(touchDX);
|
||||
var dy = Math.abs(touchDY);
|
||||
|
||||
if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {
|
||||
if (touchDX > 0) {
|
||||
prevSlide();
|
||||
} else {
|
||||
nextSlide();
|
||||
}
|
||||
}
|
||||
|
||||
cancelTouch();
|
||||
};
|
||||
|
||||
function cancelTouch() {
|
||||
document.body.removeEventListener('touchmove', handleTouchMove, true);
|
||||
document.body.removeEventListener('touchend', handleTouchEnd, true);
|
||||
};
|
||||
|
||||
/* Preloading frames */
|
||||
|
||||
function disableSlideFrames(no) {
|
||||
var el = getSlideEl(no);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
var frames = el.getElementsByTagName('iframe');
|
||||
for (var i = 0, frame; frame = frames[i]; i++) {
|
||||
disableFrame(frame);
|
||||
}
|
||||
};
|
||||
|
||||
function enableSlideFrames(no) {
|
||||
var el = getSlideEl(no);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
var frames = el.getElementsByTagName('iframe');
|
||||
for (var i = 0, frame; frame = frames[i]; i++) {
|
||||
enableFrame(frame);
|
||||
}
|
||||
};
|
||||
|
||||
function disableFrame(frame) {
|
||||
frame.src = 'about:blank';
|
||||
};
|
||||
|
||||
function enableFrame(frame) {
|
||||
var src = frame._src;
|
||||
|
||||
if (frame.src != src && src != 'about:blank') {
|
||||
frame.src = src;
|
||||
}
|
||||
};
|
||||
|
||||
function setupFrames() {
|
||||
var frames = document.querySelectorAll('iframe');
|
||||
for (var i = 0, frame; frame = frames[i]; i++) {
|
||||
frame._src = frame.src;
|
||||
disableFrame(frame);
|
||||
}
|
||||
|
||||
enableSlideFrames(curSlide);
|
||||
enableSlideFrames(curSlide + 1);
|
||||
enableSlideFrames(curSlide + 2);
|
||||
};
|
||||
|
||||
function setupInteraction() {
|
||||
/* Clicking and tapping */
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.className = 'slide-area';
|
||||
el.id = 'prev-slide-area';
|
||||
el.addEventListener('click', prevSlide, false);
|
||||
document.querySelector('section.slides').appendChild(el);
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.className = 'slide-area';
|
||||
el.id = 'next-slide-area';
|
||||
el.addEventListener('click', nextSlide, false);
|
||||
document.querySelector('section.slides').appendChild(el);
|
||||
|
||||
/* Swiping */
|
||||
|
||||
document.body.addEventListener('touchstart', handleTouchStart, false);
|
||||
}
|
||||
|
||||
/* Hash functions */
|
||||
|
||||
function getCurSlideFromHash() {
|
||||
var slideNo = parseInt(location.hash.substr(1));
|
||||
|
||||
if (slideNo) {
|
||||
curSlide = slideNo - 1;
|
||||
} else {
|
||||
curSlide = 0;
|
||||
}
|
||||
};
|
||||
|
||||
function updateHash() {
|
||||
location.replace('#' + (curSlide + 1));
|
||||
};
|
||||
|
||||
/* Event listeners */
|
||||
|
||||
function handleBodyKeyDown(event) {
|
||||
// If we're in a code element, only handle pgup/down.
|
||||
var inCode = event.target.classList.contains("code");
|
||||
|
||||
switch (event.keyCode) {
|
||||
case 39: // right arrow
|
||||
case 13: // Enter
|
||||
case 32: // space
|
||||
if (inCode) break;
|
||||
case 34: // PgDn
|
||||
nextSlide();
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case 37: // left arrow
|
||||
case 8: // Backspace
|
||||
if (inCode) break;
|
||||
case 33: // PgUp
|
||||
prevSlide();
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case 40: // down arrow
|
||||
if (inCode) break;
|
||||
nextSlide();
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case 38: // up arrow
|
||||
if (inCode) break;
|
||||
prevSlide();
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
function addEventListeners() {
|
||||
document.addEventListener('keydown', handleBodyKeyDown, false);
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
function addFontStyle() {
|
||||
var el = document.createElement('link');
|
||||
el.rel = 'stylesheet';
|
||||
el.type = 'text/css';
|
||||
el.href = '//fonts.googleapis.com/css?family=' +
|
||||
'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
|
||||
|
||||
document.body.appendChild(el);
|
||||
};
|
||||
|
||||
function addGeneralStyle() {
|
||||
var el = document.createElement('link');
|
||||
el.rel = 'stylesheet';
|
||||
el.type = 'text/css';
|
||||
el.href = PERMANENT_URL_PREFIX + 'styles.css';
|
||||
document.body.appendChild(el);
|
||||
|
||||
var el = document.createElement('meta');
|
||||
el.name = 'viewport';
|
||||
el.content = 'width=1100,height=750';
|
||||
document.querySelector('head').appendChild(el);
|
||||
|
||||
var el = document.createElement('meta');
|
||||
el.name = 'apple-mobile-web-app-capable';
|
||||
el.content = 'yes';
|
||||
document.querySelector('head').appendChild(el);
|
||||
};
|
||||
|
||||
function addPrintStyle() {
|
||||
var el = document.createElement('link');
|
||||
el.rel = 'stylesheet';
|
||||
el.type = 'text/css';
|
||||
el.media = "print";
|
||||
el.href = PERMANENT_URL_PREFIX + 'print.css';
|
||||
document.body.appendChild(el);
|
||||
};
|
||||
|
||||
function handleDomLoaded() {
|
||||
slideEls = document.querySelectorAll('section.slides > article');
|
||||
|
||||
setupFrames();
|
||||
|
||||
addFontStyle();
|
||||
addGeneralStyle();
|
||||
addPrintStyle();
|
||||
addEventListeners();
|
||||
|
||||
updateSlides();
|
||||
|
||||
setupInteraction();
|
||||
|
||||
document.body.classList.add('loaded');
|
||||
};
|
||||
|
||||
function initialize() {
|
||||
getCurSlideFromHash();
|
||||
|
||||
if (window['_DEBUG']) {
|
||||
PERMANENT_URL_PREFIX = '../';
|
||||
}
|
||||
|
||||
if (window['_DCL']) {
|
||||
handleDomLoaded();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', handleDomLoaded, false);
|
||||
}
|
||||
}
|
||||
|
||||
// If ?debug exists then load the script relative instead of absolute
|
||||
if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Avoid missing the DomContentLoaded event
|
||||
window['_DCL'] = true
|
||||
}, false);
|
||||
|
||||
window['_DEBUG'] = true;
|
||||
var script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = '../slides.js';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(script, s);
|
||||
|
||||
// Remove this script
|
||||
s.parentNode.removeChild(s);
|
||||
} else {
|
||||
initialize();
|
||||
}
|
455
cmd/present/static/styles.css
Normal file
455
cmd/present/static/styles.css
Normal file
@ -0,0 +1,455 @@
|
||||
/* Framework */
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
display: block !important;
|
||||
|
||||
height: 100%;
|
||||
min-height: 740px;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
background: rgb(215, 215, 215);
|
||||
background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
|
||||
background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
|
||||
background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
|
||||
background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.slides {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
position: absolute;
|
||||
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
.slides > article {
|
||||
display: block;
|
||||
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
|
||||
width: 900px;
|
||||
height: 700px;
|
||||
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
|
||||
margin-left: -450px;
|
||||
margin-top: -350px;
|
||||
|
||||
padding: 40px 60px;
|
||||
|
||||
box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
|
||||
border-radius: 10px;
|
||||
-o-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
-webkit-border-radius: 10px;
|
||||
|
||||
background-color: white;
|
||||
|
||||
border: 1px solid rgba(0, 0, 0, .3);
|
||||
|
||||
/*
|
||||
transition: transform .3s ease-out;
|
||||
-o-transition: -o-transform .3s ease-out;
|
||||
-moz-transition: -moz-transform .3s ease-out;
|
||||
-webkit-transition: -webkit-transform .3s ease-out;
|
||||
*/
|
||||
}
|
||||
.slides.layout-widescreen > article {
|
||||
margin-left: -550px;
|
||||
width: 1100px;
|
||||
}
|
||||
.slides.layout-faux-widescreen > article {
|
||||
margin-left: -550px;
|
||||
width: 1100px;
|
||||
|
||||
padding: 40px 160px;
|
||||
}
|
||||
|
||||
.slides.layout-widescreen > article:not(.nobackground):not(.biglogo),
|
||||
.slides.layout-faux-widescreen > article:not(.nobackground):not(.biglogo) {
|
||||
background-position-x: 0, 840px;
|
||||
}
|
||||
|
||||
/* Clickable/tappable areas */
|
||||
|
||||
.slide-area {
|
||||
z-index: 1000;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 150px;
|
||||
height: 700px;
|
||||
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
|
||||
cursor: pointer;
|
||||
margin-top: -350px;
|
||||
|
||||
tap-highlight-color: transparent;
|
||||
-o-tap-highlight-color: transparent;
|
||||
-moz-tap-highlight-color: transparent;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
#prev-slide-area {
|
||||
margin-left: -550px;
|
||||
}
|
||||
#next-slide-area {
|
||||
margin-left: 400px;
|
||||
}
|
||||
.slides.layout-widescreen #prev-slide-area,
|
||||
.slides.layout-faux-widescreen #prev-slide-area {
|
||||
margin-left: -650px;
|
||||
}
|
||||
.slides.layout-widescreen #next-slide-area,
|
||||
.slides.layout-faux-widescreen #next-slide-area {
|
||||
margin-left: 500px;
|
||||
}
|
||||
|
||||
/* Slides */
|
||||
|
||||
.slides > article {
|
||||
display: none;
|
||||
}
|
||||
.slides > article.far-past {
|
||||
display: block;
|
||||
transform: translate(-2040px);
|
||||
-o-transform: translate(-2040px);
|
||||
-moz-transform: translate(-2040px);
|
||||
-webkit-transform: translate3d(-2040px, 0, 0);
|
||||
}
|
||||
.slides > article.past {
|
||||
display: block;
|
||||
transform: translate(-1020px);
|
||||
-o-transform: translate(-1020px);
|
||||
-moz-transform: translate(-1020px);
|
||||
-webkit-transform: translate3d(-1020px, 0, 0);
|
||||
}
|
||||
.slides > article.current {
|
||||
display: block;
|
||||
transform: translate(0);
|
||||
-o-transform: translate(0);
|
||||
-moz-transform: translate(0);
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
}
|
||||
.slides > article.next {
|
||||
display: block;
|
||||
transform: translate(1020px);
|
||||
-o-transform: translate(1020px);
|
||||
-moz-transform: translate(1020px);
|
||||
-webkit-transform: translate3d(1020px, 0, 0);
|
||||
}
|
||||
.slides > article.far-next {
|
||||
display: block;
|
||||
transform: translate(2040px);
|
||||
-o-transform: translate(2040px);
|
||||
-moz-transform: translate(2040px);
|
||||
-webkit-transform: translate3d(2040px, 0, 0);
|
||||
}
|
||||
|
||||
.slides.layout-widescreen > article.far-past,
|
||||
.slides.layout-faux-widescreen > article.far-past {
|
||||
display: block;
|
||||
transform: translate(-2260px);
|
||||
-o-transform: translate(-2260px);
|
||||
-moz-transform: translate(-2260px);
|
||||
-webkit-transform: translate3d(-2260px, 0, 0);
|
||||
}
|
||||
.slides.layout-widescreen > article.past,
|
||||
.slides.layout-faux-widescreen > article.past {
|
||||
display: block;
|
||||
transform: translate(-1130px);
|
||||
-o-transform: translate(-1130px);
|
||||
-moz-transform: translate(-1130px);
|
||||
-webkit-transform: translate3d(-1130px, 0, 0);
|
||||
}
|
||||
.slides.layout-widescreen > article.current,
|
||||
.slides.layout-faux-widescreen > article.current {
|
||||
display: block;
|
||||
transform: translate(0);
|
||||
-o-transform: translate(0);
|
||||
-moz-transform: translate(0);
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
}
|
||||
.slides.layout-widescreen > article.next,
|
||||
.slides.layout-faux-widescreen > article.next {
|
||||
display: block;
|
||||
transform: translate(1130px);
|
||||
-o-transform: translate(1130px);
|
||||
-moz-transform: translate(1130px);
|
||||
-webkit-transform: translate3d(1130px, 0, 0);
|
||||
}
|
||||
.slides.layout-widescreen > article.far-next,
|
||||
.slides.layout-faux-widescreen > article.far-next {
|
||||
display: block;
|
||||
transform: translate(2260px);
|
||||
-o-transform: translate(2260px);
|
||||
-moz-transform: translate(2260px);
|
||||
-webkit-transform: translate3d(2260px, 0, 0);
|
||||
}
|
||||
|
||||
/* Styles for slides */
|
||||
|
||||
.slides > article {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
|
||||
color: black;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, .1);
|
||||
|
||||
font-size: 26px;
|
||||
line-height: 36px;
|
||||
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgb(0, 102, 204);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:visited {
|
||||
color: rgba(0, 102, 204, .75);
|
||||
}
|
||||
a:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
margin-top: 20px;
|
||||
}
|
||||
p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 60px;
|
||||
line-height: 60px;
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: 200px;
|
||||
margin-bottom: 5px;
|
||||
padding-right: 40px;
|
||||
|
||||
font-weight: 600;
|
||||
|
||||
letter-spacing: -3px;
|
||||
|
||||
color: rgb(51, 51, 51);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 45px;
|
||||
line-height: 45px;
|
||||
|
||||
position: absolute;
|
||||
bottom: 150px;
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding-right: 40px;
|
||||
|
||||
font-weight: 600;
|
||||
|
||||
letter-spacing: -2px;
|
||||
|
||||
color: rgb(51, 51, 51);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 30px;
|
||||
line-height: 36px;
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding-right: 40px;
|
||||
|
||||
font-weight: 600;
|
||||
|
||||
letter-spacing: -1px;
|
||||
|
||||
color: rgb(51, 51, 51);
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: 20px;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0 0 .5em 0;
|
||||
}
|
||||
|
||||
div.code {
|
||||
padding: 5px 10px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
background: rgb(240, 240, 240);
|
||||
border: 1px solid rgb(224, 224, 224);
|
||||
}
|
||||
pre {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
font-family: 'Droid Sans Mono', 'Courier New', monospace;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
letter-spacing: -1px;
|
||||
|
||||
color: black;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 95%;
|
||||
font-family: 'Droid Sans Mono', 'Courier New', monospace;
|
||||
|
||||
color: black;
|
||||
}
|
||||
|
||||
article > .image {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 40px;
|
||||
}
|
||||
th {
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
border: 1px solid rgb(224, 224, 224);
|
||||
padding: 5px 10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
p.link {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
/* Code */
|
||||
div.code {
|
||||
outline: 0px solid transparent;
|
||||
}
|
||||
div.playground {
|
||||
position: relative;
|
||||
}
|
||||
div.output {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
right: 40px;
|
||||
bottom: 40px;
|
||||
background: #202020;
|
||||
padding: 5px 10px;
|
||||
z-index: 2;
|
||||
|
||||
border-radius: 10px;
|
||||
-o-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
-webkit-border-radius: 10px;
|
||||
|
||||
}
|
||||
div.output pre {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
div.output .stdout, div.output pre {
|
||||
color: #e6e6e6;
|
||||
}
|
||||
div.output .stderr, div.output .error {
|
||||
color: rgb(255, 200, 200);
|
||||
}
|
||||
div.output .system, div.output .exit {
|
||||
color: rgb(255, 230, 120)
|
||||
}
|
||||
.buttons {
|
||||
position: relative;
|
||||
float: right;
|
||||
top: -60px;
|
||||
right: 10px;
|
||||
}
|
||||
div.output .buttons {
|
||||
position: absolute;
|
||||
float: none;
|
||||
top: auto;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
/* Presenter details */
|
||||
.presenter {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.presenter p,
|
||||
.presenter .link {
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
/* Output resize details */
|
||||
.ui-resizable-handle {
|
||||
position: absolute;
|
||||
}
|
||||
.ui-resizable-n {
|
||||
cursor: n-resize;
|
||||
height: 7px;
|
||||
width: 100%;
|
||||
top: -5px;
|
||||
left: 0;
|
||||
}
|
||||
.ui-resizable-w {
|
||||
cursor: w-resize;
|
||||
width: 7px;
|
||||
left: -5px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-resizable-nw {
|
||||
cursor: nw-resize;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
left: -5px;
|
||||
top: -5px;
|
||||
}
|
46
cmd/present/templates/action.tmpl
Normal file
46
cmd/present/templates/action.tmpl
Normal file
@ -0,0 +1,46 @@
|
||||
{/*
|
||||
This is the action template.
|
||||
It determines how the formatting actions are rendered.
|
||||
*/}
|
||||
|
||||
{{define "section"}}
|
||||
<h{{len .Number}} id="TOC_{{.FormattedNumber}}">{{.FormattedNumber}} {{.Title}}</h{{len .Number}}>
|
||||
{{range .Elem}}{{elem $.Template .}}{{end}}
|
||||
{{end}}
|
||||
|
||||
{{define "list"}}
|
||||
<ul>
|
||||
{{range .Bullet}}
|
||||
<li>{{style .}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
{{define "text"}}
|
||||
{{if .Pre}}
|
||||
<div class="code"><pre>{{range .Lines}}{{.}}{{end}}</pre></div>
|
||||
{{else}}
|
||||
<p>
|
||||
{{range $i, $l := .Lines}}{{if $i}}{{template "newline"}}
|
||||
{{end}}{{style $l}}{{end}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{define "code"}}
|
||||
<div class="code{{if playable .}} playground{{end}}" contenteditable="true" spellcheck="false">{{.Text}}</div>
|
||||
{{end}}
|
||||
|
||||
{{define "image"}}
|
||||
<div class="image">
|
||||
<img src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{define "iframe"}}
|
||||
<iframe src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}></iframe>
|
||||
{{end}}
|
||||
|
||||
{{define "link"}}<p class="link"><a href="{{.URL}}" target="_blank">{{style .Label}}</a></p>{{end}}
|
||||
|
||||
{{define "html"}}{{.HTML}}{{end}}
|
58
cmd/present/templates/article.tmpl
Normal file
58
cmd/present/templates/article.tmpl
Normal file
@ -0,0 +1,58 @@
|
||||
{/* This is the article template. It defines how articles are formatted. */}
|
||||
|
||||
{{define "root"}}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.Title}}</title>
|
||||
<link type="text/css" rel="stylesheet" href="/static/article.css">
|
||||
<meta charset='utf-8'>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="topbar" class="wide">
|
||||
<div class="container">
|
||||
<div id="heading">{{.Title}}
|
||||
{{with .Subtitle}}{{.}}{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="page" class="wide">
|
||||
<div class="container">
|
||||
{{with .Sections}}
|
||||
<div id="toc">
|
||||
{{template "TOC" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{range .Sections}}
|
||||
{{elem $.Template .}}
|
||||
{{end}}{{/* of Section block */}}
|
||||
|
||||
{{if .Authors}}
|
||||
<h2>Authors</h2>
|
||||
{{range .Authors}}
|
||||
<div class="author">
|
||||
{{range .Elem}}{{elem $.Template .}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<script src='/play.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
|
||||
{{define "TOC"}}
|
||||
<ul>
|
||||
{{range .}}
|
||||
<li><a href="#TOC_{{.FormattedNumber}}">{{.Title}}</a></li>
|
||||
{{with .Sections}}{{template "TOC" .}}{{end}}
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
{{define "newline"}}
|
||||
{{/* No automatic line break. Paragraphs are free-form. */}}
|
||||
{{end}}
|
58
cmd/present/templates/slides.tmpl
Normal file
58
cmd/present/templates/slides.tmpl
Normal file
@ -0,0 +1,58 @@
|
||||
{/* This is the slide template. It defines how presentations are formatted. */}
|
||||
|
||||
{{define "root"}}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.Title}}</title>
|
||||
<meta charset='utf-8'>
|
||||
<script src='/static/slides.js'></script>
|
||||
</head>
|
||||
|
||||
<body style='display: none'>
|
||||
|
||||
<section class='slides layout-widescreen'>
|
||||
|
||||
<article>
|
||||
<h1>{{.Title}}</h1>
|
||||
{{with .Subtitle}}<h3>{{.}}</h3>{{end}}
|
||||
{{if not .Time.IsZero}}<h3>{{.Time.Format "2 January 2006"}}</h3>{{end}}
|
||||
{{range .Authors}}
|
||||
<div class="presenter">
|
||||
{{range .TextElem}}{{elem $.Template .}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</article>
|
||||
|
||||
{{range $i, $s := .Sections}}
|
||||
<!-- start of slide {{$s.Number}} -->
|
||||
<article>
|
||||
{{if $s.Elem}}
|
||||
<h3>{{$s.Title}}</h3>
|
||||
{{range $s.Elem}}{{elem $.Template .}}{{end}}
|
||||
{{else}}
|
||||
<h2>{{$s.Title}}</h2>
|
||||
{{end}}
|
||||
</article>
|
||||
<!-- end of slide {{$i}} -->
|
||||
{{end}}{{/* of Slide block */}}
|
||||
|
||||
<article>
|
||||
<h3>Thank you</h1>
|
||||
{{range .Authors}}
|
||||
<div class="presenter">
|
||||
{{range .Elem}}{{elem $.Template .}}{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</article>
|
||||
|
||||
</body>
|
||||
{{if .PlayEnabled}}
|
||||
<script src='/play.js'></script>
|
||||
{{end}}
|
||||
</html>
|
||||
{{end}}
|
||||
|
||||
{{define "newline"}}
|
||||
<br>
|
||||
{{end}}
|
Loading…
Reference in New Issue
Block a user