2018-06-02 17:55:12 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2018-10-01 21:16:39 +02:00
|
|
|
"bytes"
|
2018-06-02 17:55:12 +02:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2019-04-10 22:15:44 +02:00
|
|
|
"image"
|
2018-10-01 21:16:39 +02:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
2019-04-12 22:13:29 +02:00
|
|
|
"reflect"
|
2018-06-02 17:55:12 +02:00
|
|
|
"strings"
|
|
|
|
|
2019-06-01 22:30:37 +02:00
|
|
|
basereflect "github.com/cosmos72/gomacro/base/reflect"
|
2019-04-12 22:13:29 +02:00
|
|
|
"github.com/cosmos72/gomacro/xreflect"
|
2018-06-02 17:55:12 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Support an interface similar - but not identical - to the IPython (canonical Jupyter kernel).
|
|
|
|
// See http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html#IPython.display.display
|
2019-04-10 22:31:51 +02:00
|
|
|
// for a good overview of the support types.
|
2018-06-02 17:55:12 +02:00
|
|
|
|
|
|
|
const (
|
2018-06-04 21:55:35 +02:00
|
|
|
MIMETypeHTML = "text/html"
|
|
|
|
MIMETypeJavaScript = "application/javascript"
|
|
|
|
MIMETypeJPEG = "image/jpeg"
|
|
|
|
MIMETypeJSON = "application/json"
|
|
|
|
MIMETypeLatex = "text/latex"
|
|
|
|
MIMETypeMarkdown = "text/markdown"
|
|
|
|
MIMETypePNG = "image/png"
|
|
|
|
MIMETypePDF = "application/pdf"
|
|
|
|
MIMETypeSVG = "image/svg+xml"
|
2019-04-11 22:22:37 +02:00
|
|
|
MIMETypeText = "text/plain"
|
2018-06-02 17:55:12 +02:00
|
|
|
)
|
|
|
|
|
2019-04-10 22:31:51 +02:00
|
|
|
/**
|
2019-04-11 22:22:37 +02:00
|
|
|
* general interface, allows libraries to fully specify
|
|
|
|
* how their data is displayed by Jupyter.
|
2019-04-10 22:31:51 +02:00
|
|
|
* Supports multiple MIME formats.
|
|
|
|
*
|
2019-04-11 22:22:37 +02:00
|
|
|
* Note that Data defined above is an alias:
|
|
|
|
* libraries can implement Renderer without importing gophernotes
|
2019-04-10 22:31:51 +02:00
|
|
|
*/
|
|
|
|
type Renderer = interface {
|
|
|
|
Render() Data
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-04-11 22:22:37 +02:00
|
|
|
* simplified interface, allows libraries to specify
|
|
|
|
* how their data is displayed by Jupyter.
|
2019-04-12 22:13:29 +02:00
|
|
|
* Supports multiple MIME formats.
|
2019-04-10 22:31:51 +02:00
|
|
|
*
|
2019-04-11 22:22:37 +02:00
|
|
|
* Note that MIMEMap defined above is an alias:
|
|
|
|
* libraries can implement SimpleRenderer without importing gophernotes
|
2019-04-10 22:31:51 +02:00
|
|
|
*/
|
|
|
|
type SimpleRenderer = interface {
|
2019-04-12 22:13:29 +02:00
|
|
|
SimpleRender() MIMEMap
|
2019-04-10 22:31:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* specialized interfaces, each is dedicated to a specific MIME type.
|
|
|
|
*
|
|
|
|
* They are type aliases to emphasize that method signatures
|
|
|
|
* are the only important thing, not the interface names.
|
|
|
|
* Thus libraries can implement them without importing gophernotes
|
|
|
|
*/
|
|
|
|
type HTMLer = interface {
|
|
|
|
HTML() string
|
|
|
|
}
|
2019-04-11 22:22:37 +02:00
|
|
|
type JavaScripter = interface {
|
2019-04-10 22:31:51 +02:00
|
|
|
JavaScript() string
|
|
|
|
}
|
|
|
|
type JPEGer = interface {
|
|
|
|
JPEG() []byte
|
|
|
|
}
|
|
|
|
type JSONer = interface {
|
|
|
|
JSON() map[string]interface{}
|
|
|
|
}
|
|
|
|
type Latexer = interface {
|
|
|
|
Latex() string
|
|
|
|
}
|
|
|
|
type Markdowner = interface {
|
|
|
|
Markdown() string
|
|
|
|
}
|
|
|
|
type PNGer = interface {
|
|
|
|
PNG() []byte
|
|
|
|
}
|
|
|
|
type PDFer = interface {
|
|
|
|
PDF() []byte
|
|
|
|
}
|
|
|
|
type SVGer = interface {
|
|
|
|
SVG() string
|
|
|
|
}
|
|
|
|
|
2018-06-04 21:55:35 +02:00
|
|
|
// injected as placeholder in the interpreter, it's then replaced at runtime
|
|
|
|
// by a closure that knows how to talk with Jupyter
|
2018-06-10 23:09:07 +02:00
|
|
|
func stubDisplay(Data) error {
|
|
|
|
return errors.New("cannot display: connection with Jupyter not available")
|
2018-06-04 21:55:35 +02:00
|
|
|
}
|
|
|
|
|
2019-04-12 22:13:29 +02:00
|
|
|
// fill kernel.renderer map used to convert interpreted types
|
|
|
|
// to known rendering interfaces
|
|
|
|
func (kernel *Kernel) initRenderers() {
|
2019-04-13 22:57:40 +02:00
|
|
|
kernel.render = make(map[string]xreflect.Type)
|
2019-04-12 22:13:29 +02:00
|
|
|
for name, typ := range kernel.display.Types {
|
|
|
|
if typ.Kind() == reflect.Interface {
|
2019-04-13 22:57:40 +02:00
|
|
|
kernel.render[name] = typ
|
2019-04-12 22:13:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if vals[] contain a single non-nil value which is auto-renderable,
|
|
|
|
// convert it to Data and return it.
|
|
|
|
// otherwise return MakeData("text/plain", fmt.Sprint(vals...))
|
|
|
|
func (kernel *Kernel) autoRenderResults(vals []interface{}, types []xreflect.Type) Data {
|
|
|
|
var nilcount int
|
|
|
|
var obj interface{}
|
|
|
|
var typ xreflect.Type
|
|
|
|
for i, val := range vals {
|
|
|
|
if kernel.canAutoRender(val, types[i]) {
|
|
|
|
obj = val
|
|
|
|
typ = types[i]
|
|
|
|
} else if val == nil {
|
|
|
|
nilcount++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if obj != nil && nilcount == len(vals)-1 {
|
|
|
|
return kernel.autoRender("", obj, typ)
|
|
|
|
}
|
|
|
|
if nilcount == len(vals) {
|
|
|
|
// if all values are nil, return empty Data
|
|
|
|
return Data{}
|
|
|
|
}
|
|
|
|
return MakeData(MIMETypeText, fmt.Sprint(vals...))
|
|
|
|
}
|
|
|
|
|
2019-04-11 22:22:37 +02:00
|
|
|
// return true if data type should be auto-rendered graphically
|
2019-04-12 22:13:29 +02:00
|
|
|
func (kernel *Kernel) canAutoRender(data interface{}, typ xreflect.Type) bool {
|
2019-04-11 22:22:37 +02:00
|
|
|
switch data.(type) {
|
|
|
|
case Data, Renderer, SimpleRenderer, HTMLer, JavaScripter, JPEGer, JSONer,
|
|
|
|
Latexer, Markdowner, PNGer, PDFer, SVGer, image.Image:
|
|
|
|
return true
|
2019-04-12 22:13:29 +02:00
|
|
|
}
|
|
|
|
if kernel == nil || typ == nil {
|
2019-04-11 22:22:37 +02:00
|
|
|
return false
|
|
|
|
}
|
2019-04-12 22:13:29 +02:00
|
|
|
// in gomacro, methods of interpreted types are emulated,
|
|
|
|
// thus type-asserting them to interface types as done above cannot succeed.
|
|
|
|
// Manually check if emulated type "pretends" to implement
|
2019-04-13 22:57:40 +02:00
|
|
|
// at least one of the interfaces above
|
2019-04-12 22:13:29 +02:00
|
|
|
for _, xtyp := range kernel.render {
|
|
|
|
if typ.Implements(xtyp) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
2018-06-02 17:55:12 +02:00
|
|
|
|
2019-04-13 22:57:40 +02:00
|
|
|
var autoRenderers = map[string]func(Data, interface{}) Data{
|
|
|
|
"Renderer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(Renderer); ok {
|
|
|
|
x := r.Render()
|
|
|
|
d.Data = merge(d.Data, x.Data)
|
|
|
|
d.Metadata = merge(d.Metadata, x.Metadata)
|
|
|
|
d.Transient = merge(d.Transient, x.Transient)
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"SimpleRenderer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(SimpleRenderer); ok {
|
|
|
|
x := r.SimpleRender()
|
|
|
|
d.Data = merge(d.Data, x)
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"HTMLer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(HTMLer); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypeHTML] = r.HTML()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"JavaScripter": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(JavaScripter); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypeJavaScript] = r.JavaScript()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"JPEGer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(JPEGer); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypeJPEG] = r.JPEG()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"JSONer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(JSONer); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypeJSON] = r.JSON()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"Latexer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(Latexer); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypeLatex] = r.Latex()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"Markdowner": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(Markdowner); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypeMarkdown] = r.Markdown()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"PNGer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(PNGer); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypePNG] = r.PNG()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"PDFer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(PDFer); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypePDF] = r.PDF()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"SVGer": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(SVGer); ok {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[MIMETypeSVG] = r.SVG()
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
"Image": func(d Data, i interface{}) Data {
|
|
|
|
if r, ok := i.(image.Image); ok {
|
|
|
|
b, mimeType, err := encodePng(r)
|
|
|
|
if err != nil {
|
|
|
|
d = makeDataErr(err)
|
|
|
|
} else {
|
|
|
|
d.Data = ensure(d.Data)
|
|
|
|
d.Data[mimeType] = b
|
|
|
|
d.Metadata = merge(d.Metadata, imageMetadata(r))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2019-04-11 22:22:37 +02:00
|
|
|
// detect and render data types that should be auto-rendered graphically
|
2019-04-12 22:13:29 +02:00
|
|
|
func (kernel *Kernel) autoRender(mimeType string, arg interface{}, typ xreflect.Type) Data {
|
2019-04-13 22:57:40 +02:00
|
|
|
var data Data
|
2019-04-14 15:22:36 +02:00
|
|
|
// try Data
|
|
|
|
if x, ok := arg.(Data); ok {
|
|
|
|
data = x
|
2019-04-13 22:57:40 +02:00
|
|
|
}
|
|
|
|
|
2019-04-14 15:22:36 +02:00
|
|
|
if kernel == nil || typ == nil {
|
|
|
|
// try all autoRenderers
|
|
|
|
for _, fun := range autoRenderers {
|
|
|
|
data = fun(data, arg)
|
|
|
|
}
|
|
|
|
} else {
|
2019-04-13 22:57:40 +02:00
|
|
|
// in gomacro, methods of interpreted types are emulated.
|
|
|
|
// Thus type-asserting them to interface types as done by autoRenderer functions above cannot succeed.
|
2019-04-14 15:22:36 +02:00
|
|
|
// Manually check if emulated type "pretends" to implement one or more of the above interfaces
|
2019-04-13 22:57:40 +02:00
|
|
|
// and, in case, tell the interpreter to convert to them
|
|
|
|
for name, xtyp := range kernel.render {
|
|
|
|
fun := autoRenderers[name]
|
2019-04-14 15:22:36 +02:00
|
|
|
if fun == nil || !typ.Implements(xtyp) {
|
2019-04-13 22:57:40 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
conv := kernel.ir.Comp.Converter(typ, xtyp)
|
2019-05-18 20:50:12 +09:00
|
|
|
x := arg
|
|
|
|
if conv != nil {
|
2019-06-01 22:30:37 +02:00
|
|
|
x = basereflect.Interface(conv(reflect.ValueOf(x)))
|
2019-05-18 20:50:12 +09:00
|
|
|
if x == nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-04-12 22:13:29 +02:00
|
|
|
}
|
2019-04-13 22:57:40 +02:00
|
|
|
data = fun(data, x)
|
2019-04-12 22:13:29 +02:00
|
|
|
}
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
2019-04-13 22:57:40 +02:00
|
|
|
return fillDefaults(data, arg, "", nil, "", nil)
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
|
|
|
|
2019-04-13 22:57:40 +02:00
|
|
|
func fillDefaults(data Data, arg interface{}, s string, b []byte, mimeType string, err error) Data {
|
2019-04-11 22:22:37 +02:00
|
|
|
if err != nil {
|
|
|
|
return makeDataErr(err)
|
|
|
|
}
|
2019-04-13 22:57:40 +02:00
|
|
|
if data.Data == nil {
|
|
|
|
data.Data = make(MIMEMap)
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
2019-04-12 22:13:29 +02:00
|
|
|
// cannot autodetect the mime type of a string
|
|
|
|
if len(s) != 0 && len(mimeType) != 0 {
|
2019-04-13 22:57:40 +02:00
|
|
|
data.Data[mimeType] = s
|
2019-04-12 22:13:29 +02:00
|
|
|
}
|
|
|
|
// ensure plain text is set
|
2019-04-13 22:57:40 +02:00
|
|
|
if data.Data[MIMETypeText] == "" {
|
2019-04-11 22:22:37 +02:00
|
|
|
if len(s) == 0 {
|
2019-04-13 22:57:40 +02:00
|
|
|
s = fmt.Sprint(arg)
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
2019-04-13 22:57:40 +02:00
|
|
|
data.Data[MIMETypeText] = s
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
2019-04-12 22:13:29 +02:00
|
|
|
// if []byte is available, use it
|
2019-04-11 22:22:37 +02:00
|
|
|
if len(b) != 0 {
|
|
|
|
if len(mimeType) == 0 {
|
|
|
|
mimeType = http.DetectContentType(b)
|
|
|
|
}
|
|
|
|
if len(mimeType) != 0 && mimeType != MIMETypeText {
|
2019-04-13 22:57:40 +02:00
|
|
|
data.Data[mimeType] = b
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
|
|
|
}
|
2019-04-13 22:57:40 +02:00
|
|
|
return data
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// do our best to render data graphically
|
|
|
|
func render(mimeType string, data interface{}) Data {
|
2019-04-12 22:13:29 +02:00
|
|
|
var kernel *Kernel // intentionally nil
|
|
|
|
if kernel.canAutoRender(data, nil) {
|
|
|
|
return kernel.autoRender(mimeType, data, nil)
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
2018-10-01 21:16:39 +02:00
|
|
|
var s string
|
2019-04-11 22:22:37 +02:00
|
|
|
var b []byte
|
|
|
|
var err error
|
|
|
|
switch data := data.(type) {
|
2018-10-01 21:16:39 +02:00
|
|
|
case string:
|
2019-04-11 22:22:37 +02:00
|
|
|
s = data
|
2018-10-01 21:16:39 +02:00
|
|
|
case []byte:
|
2019-04-11 22:22:37 +02:00
|
|
|
b = data
|
2018-10-01 21:16:39 +02:00
|
|
|
case io.Reader:
|
2019-04-11 22:22:37 +02:00
|
|
|
b, err = ioutil.ReadAll(data)
|
2018-10-01 21:16:39 +02:00
|
|
|
case io.WriterTo:
|
|
|
|
var buf bytes.Buffer
|
2019-04-11 22:22:37 +02:00
|
|
|
data.WriteTo(&buf)
|
2018-10-01 21:16:39 +02:00
|
|
|
b = buf.Bytes()
|
|
|
|
default:
|
2019-04-11 22:22:37 +02:00
|
|
|
panic(fmt.Errorf("unsupported type, cannot render: %T", data))
|
2018-10-01 21:16:39 +02:00
|
|
|
}
|
2019-04-11 22:22:37 +02:00
|
|
|
return fillDefaults(Data{}, data, s, b, mimeType, err)
|
2018-10-01 21:16:39 +02:00
|
|
|
}
|
|
|
|
|
2019-04-11 22:22:37 +02:00
|
|
|
func makeDataErr(err error) Data {
|
|
|
|
return Data{
|
2019-04-10 22:15:44 +02:00
|
|
|
Data: MIMEMap{
|
2019-04-11 22:22:37 +02:00
|
|
|
"ename": "ERROR",
|
|
|
|
"evalue": err.Error(),
|
|
|
|
"traceback": nil,
|
|
|
|
"status": "error",
|
2018-10-01 21:16:39 +02:00
|
|
|
},
|
|
|
|
}
|
2019-04-11 22:22:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func Any(mimeType string, data interface{}) Data {
|
|
|
|
return render(mimeType, data)
|
2018-10-01 21:16:39 +02:00
|
|
|
}
|
|
|
|
|
2019-04-10 22:15:44 +02:00
|
|
|
// same as Any("", data), autodetects MIME type
|
|
|
|
func Auto(data interface{}) Data {
|
2019-04-11 22:22:37 +02:00
|
|
|
return render("", data)
|
2019-04-10 22:15:44 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func MakeData(mimeType string, data interface{}) Data {
|
2018-10-01 21:16:39 +02:00
|
|
|
d := Data{
|
2019-04-10 22:15:44 +02:00
|
|
|
Data: MIMEMap{
|
2018-10-01 21:16:39 +02:00
|
|
|
mimeType: data,
|
2018-06-04 21:55:35 +02:00
|
|
|
},
|
|
|
|
}
|
2019-04-11 22:22:37 +02:00
|
|
|
if mimeType != MIMETypeText {
|
|
|
|
d.Data[MIMETypeText] = fmt.Sprint(data)
|
2018-10-01 21:16:39 +02:00
|
|
|
}
|
|
|
|
return d
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func MakeData3(mimeType string, plaintext string, data interface{}) Data {
|
|
|
|
return Data{
|
2019-04-10 22:15:44 +02:00
|
|
|
Data: MIMEMap{
|
2019-04-11 22:22:37 +02:00
|
|
|
MIMETypeText: plaintext,
|
2018-06-02 17:55:12 +02:00
|
|
|
mimeType: data,
|
2018-06-04 21:55:35 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-01 21:16:39 +02:00
|
|
|
func File(mimeType string, path string) Data {
|
|
|
|
bytes, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-04-10 22:15:44 +02:00
|
|
|
return Any(mimeType, bytes)
|
2018-10-01 21:16:39 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func HTML(html string) Data {
|
2019-04-10 22:15:44 +02:00
|
|
|
return MakeData(MIMETypeHTML, html)
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func JavaScript(javascript string) Data {
|
2019-04-10 22:15:44 +02:00
|
|
|
return MakeData(MIMETypeJavaScript, javascript)
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func JPEG(jpeg []byte) Data {
|
2019-04-10 22:15:44 +02:00
|
|
|
return MakeData(MIMETypeJPEG, jpeg)
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2019-04-10 22:31:51 +02:00
|
|
|
func JSON(json map[string]interface{}) Data {
|
|
|
|
return MakeData(MIMETypeJSON, json)
|
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func Latex(latex string) Data {
|
|
|
|
return MakeData3(MIMETypeLatex, latex, "$"+strings.Trim(latex, "$")+"$")
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func Markdown(markdown string) Data {
|
2019-04-10 22:15:44 +02:00
|
|
|
return MakeData(MIMETypeMarkdown, markdown)
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func Math(latex string) Data {
|
|
|
|
return MakeData3(MIMETypeLatex, latex, "$$"+strings.Trim(latex, "$")+"$$")
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func PDF(pdf []byte) Data {
|
2019-04-10 22:15:44 +02:00
|
|
|
return MakeData(MIMETypePDF, pdf)
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func PNG(png []byte) Data {
|
2019-04-10 22:15:44 +02:00
|
|
|
return MakeData(MIMETypePNG, png)
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
func SVG(svg string) Data {
|
2019-04-10 22:15:44 +02:00
|
|
|
return MakeData(MIMETypeSVG, svg)
|
2018-06-04 21:55:35 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 23:09:07 +02:00
|
|
|
// MIME encapsulates the data and metadata into a Data.
|
2018-06-04 21:55:35 +02:00
|
|
|
// The 'data' map is expected to contain at least one {key,value} pair,
|
|
|
|
// with value being a string, []byte or some other JSON serializable representation,
|
|
|
|
// and key equal to the MIME type of such value.
|
|
|
|
// The exact structure of value is determined by what the frontend expects.
|
|
|
|
// Some easier-to-use functions for common formats supported by the Jupyter frontend
|
2018-06-02 17:55:12 +02:00
|
|
|
// are provided by the various functions above.
|
2019-04-10 22:15:44 +02:00
|
|
|
func MIME(data, metadata MIMEMap) Data {
|
2018-06-10 23:09:07 +02:00
|
|
|
return Data{data, metadata, nil}
|
2018-06-02 17:55:12 +02:00
|
|
|
}
|