| // 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. |
| |
| //modify 2013-2014 visualfc |
| |
| package gopresent |
| |
| import ( |
| "fmt" |
| "html/template" |
| "io" |
| "os" |
| "path/filepath" |
| |
| "github.com/visualfc/gotools/command" |
| "golang.org/x/tools/present" |
| ) |
| |
| var Command = &command.Command{ |
| Run: runPresent, |
| UsageLine: "gopresent", |
| Short: "golang present util", |
| Long: `golang present util`, |
| } |
| |
| var presentVerifyOnly bool |
| var presentInput string |
| var presentStdout bool |
| var presentOutput string |
| |
| func init() { |
| Command.Flag.BoolVar(&presentVerifyOnly, "v", false, "verify present only") |
| Command.Flag.BoolVar(&presentStdout, "stdout", false, "output use std output") |
| Command.Flag.StringVar(&presentInput, "i", "", "input golang present file") |
| Command.Flag.StringVar(&presentOutput, "o", "", "output html file name") |
| } |
| |
| func runPresent(cmd *command.Command, args []string) error { |
| if presentInput == "" || !isDoc(presentInput) { |
| cmd.Usage() |
| return os.ErrInvalid |
| } |
| |
| if presentVerifyOnly { |
| err := VerifyDoc(presentInput) |
| if err != nil { |
| fmt.Fprintf(os.Stderr, "present:%s", err) |
| command.SetExitStatus(3) |
| command.Exit() |
| } |
| return nil |
| } |
| w := os.Stdout |
| if !presentStdout { |
| if presentOutput == "" { |
| presentOutput = presentInput + ".html" |
| } |
| ext := filepath.Ext(presentOutput) |
| if ext != ".htm" && ext != ".html" { |
| presentOutput += ".html" |
| } |
| var err error |
| w, err = os.Create(presentOutput) |
| if err != nil { |
| fmt.Fprintf(os.Stderr, "present:%s", err) |
| command.SetExitStatus(3) |
| command.Exit() |
| } |
| } |
| err := RenderDoc(w, presentInput) |
| if err != nil { |
| fmt.Fprintf(os.Stderr, "present:%s", err) |
| command.SetExitStatus(3) |
| command.Exit() |
| } |
| return nil |
| } |
| |
| var extensions = map[string]string{ |
| ".slide": "slides.tmpl", |
| ".article": "article.tmpl", |
| } |
| |
| var extensions_tmpl = map[string]string{ |
| ".slide": slides_tmpl, |
| ".article": article_tmpl, |
| } |
| |
| func isDoc(path string) bool { |
| _, ok := extensions[filepath.Ext(path)] |
| return ok |
| } |
| |
| func VerifyDoc(docFile string) error { |
| doc, err := parse(docFile, 0) |
| if err != nil { |
| return err |
| } |
| dir := filepath.Dir(docFile) |
| return verify_doc(dir, doc) |
| } |
| |
| // 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 RenderDoc(w io.Writer, 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_tmpl[ext] |
| if !ok { |
| return fmt.Errorf("no template for extension %v", ext) |
| } |
| |
| // Locate the template file. |
| actionTmpl := action_tmpl //filepath.Join(base, "templates/action.tmpl") |
| // Read and parse the input. |
| tmpl := present.Template() |
| tmpl = tmpl.Funcs(template.FuncMap{"playable": playable}) |
| if tmpl, err = tmpl.New("action").Parse(actionTmpl); err != nil { |
| return err |
| } |
| if tmpl, err = tmpl.New("content").Parse(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) |
| } |
| |
| func playable(c present.Code) bool { |
| return present.PlayEnabled && c.Play |
| } |
| |
| func isSkipURL(url string) bool { |
| if filepath.HasPrefix(url, "http://") { |
| return true |
| } |
| if filepath.HasPrefix(url, "https://") { |
| return true |
| } |
| return false |
| } |
| |
| func verify_path(root string, url string) error { |
| if isSkipURL(url) { |
| return nil |
| } |
| path := url |
| if !filepath.IsAbs(url) { |
| path = filepath.Join(root, path) |
| } |
| _, err := os.Stat(path) |
| if err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| func verify_doc(root string, doc *present.Doc) error { |
| for _, section := range doc.Sections { |
| for _, elem := range section.Elem { |
| switch i := elem.(type) { |
| case present.Image: |
| if err := verify_path(root, i.URL); err != nil { |
| return fmt.Errorf("! .image %s not exist", i.URL) |
| } |
| } |
| } |
| } |
| return nil |
| } |
| |
| var action_tmpl = ` |
| {/* |
| 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}} |
| ` |
| |
| var article_tmpl = ` |
| {/* 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 */}} |
| |
| <h2>Authors</h2> |
| {{range .Authors}} |
| <div class="author"> |
| {{range .Elem}}{{elem $.Template .}}{{end}} |
| </div> |
| {{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}} |
| ` |
| |
| var slides_tmpl = ` |
| {/* 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}} |
| ` |