140 lines
3.9 KiB
Go
140 lines
3.9 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/revel/revel"
|
|
"github.com/yosssi/ace"
|
|
"html/template"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
const ACE_TEMPLATE = "ace"
|
|
|
|
// Adapter for Go Templates.
|
|
type AceTemplate struct {
|
|
*template.Template
|
|
engine *AceEngine
|
|
*revel.TemplateView
|
|
File *ace.File
|
|
Inner *ace.File
|
|
}
|
|
|
|
// A bit trick of an implementation
|
|
// If the arg contains an ace_inner field then that will be used
|
|
// to fetch a new template
|
|
func (acetmpl AceTemplate) Render(wr io.Writer, arg interface{}) error {
|
|
// We can redirect this render to another template if the arguments contain ace_content in them
|
|
if argmap, ok := arg.(map[string]interface{}); ok {
|
|
if acecontentraw, ok := argmap["ace-inner"]; ok {
|
|
acecontent := acecontentraw.(string)
|
|
newtemplatename := acetmpl.TemplateName + "-" + acecontent
|
|
// Now lookup the template again
|
|
if _, ok := acetmpl.engine.templatesByName[newtemplatename]; !ok {
|
|
if inner, ok := acetmpl.engine.templatesByName[acecontent]; !ok {
|
|
return fmt.Errorf("Inner content %s not found in ace templates", acecontent)
|
|
} else {
|
|
acetmpl.engine.templatesByName[newtemplatename] = &AceTemplate{
|
|
File: acetmpl.File,
|
|
Inner: inner.File,
|
|
engine: acetmpl.engine,
|
|
TemplateView: acetmpl.TemplateView}
|
|
}
|
|
|
|
}
|
|
return acetmpl.engine.templatesByName[newtemplatename].renderInternal(wr, arg)
|
|
}
|
|
}
|
|
return acetmpl.renderInternal(wr, arg)
|
|
}
|
|
|
|
func (acetmpl AceTemplate) renderInternal(wr io.Writer, arg interface{}) error {
|
|
if acetmpl.Template == nil {
|
|
// Compile the template first
|
|
if acetmpl.Inner == nil {
|
|
acetmpl.Inner = ace.NewFile("", nil)
|
|
}
|
|
source := ace.NewSource(acetmpl.File, acetmpl.Inner, acetmpl.engine.files)
|
|
result, err := ace.ParseSource(source, acetmpl.engine.Options)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if gtemplate, err := ace.CompileResult(acetmpl.TemplateName, result, acetmpl.engine.Options); err != nil {
|
|
return err
|
|
} else {
|
|
acetmpl.Template = gtemplate
|
|
}
|
|
}
|
|
return acetmpl.Execute(wr, arg)
|
|
}
|
|
|
|
type AceEngine struct {
|
|
loader *revel.TemplateLoader
|
|
templatesByName map[string]*AceTemplate
|
|
files []*ace.File
|
|
Options *ace.Options
|
|
CaseInsensitive bool
|
|
}
|
|
|
|
func (i *AceEngine) ConvertPath(path string) string {
|
|
if i.CaseInsensitive {
|
|
return strings.ToLower(path)
|
|
}
|
|
return path
|
|
}
|
|
|
|
func (i *AceEngine) Handles(templateView *revel.TemplateView) bool {
|
|
return revel.EngineHandles(i, templateView)
|
|
}
|
|
|
|
func (engine *AceEngine) ParseAndAdd(baseTemplate *revel.TemplateView) error {
|
|
|
|
// Ace templates must only render views specified for it (no trial and error)
|
|
if baseTemplate.EngineType != ACE_TEMPLATE {
|
|
return &revel.Error{
|
|
Title: "Template Compilation Error",
|
|
Path: baseTemplate.FilePath,
|
|
Description: "Not correct template for engine",
|
|
Line: 1,
|
|
SourceLines: baseTemplate.Content(),
|
|
}
|
|
}
|
|
|
|
baseTemplate.TemplateName = engine.ConvertPath(baseTemplate.TemplateName)
|
|
file := ace.NewFile(baseTemplate.TemplateName, baseTemplate.FileBytes)
|
|
engine.files = append(engine.files, file)
|
|
engine.templatesByName[baseTemplate.TemplateName] = &AceTemplate{File: file, engine: engine, TemplateView: baseTemplate}
|
|
return nil
|
|
}
|
|
|
|
func (engine *AceEngine) Lookup(templateName string) revel.Template {
|
|
if tpl, found := engine.templatesByName[engine.ConvertPath(templateName)]; found {
|
|
|
|
return tpl
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (engine *AceEngine) Name() string {
|
|
return ACE_TEMPLATE
|
|
}
|
|
|
|
func (engine *AceEngine) Event(action int, i interface{}) {
|
|
if action == revel.TEMPLATE_REFRESH_REQUESTED {
|
|
engine.templatesByName = map[string]*AceTemplate{}
|
|
engine.CaseInsensitive = revel.Config.BoolDefault("ace.template.caseinsensitive", true)
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
revel.RegisterTemplateLoader(ACE_TEMPLATE, func(loader *revel.TemplateLoader) (revel.TemplateEngine, error) {
|
|
|
|
return &AceEngine{
|
|
loader: loader,
|
|
templatesByName: map[string]*AceTemplate{},
|
|
Options: &ace.Options{FuncMap: revel.TemplateFuncs},
|
|
}, nil
|
|
})
|
|
}
|