1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

merge/fix

This commit is contained in:
Craig Peterson
2017-06-05 13:58:02 -04:00
175 changed files with 33628 additions and 326 deletions

323
pkg/js/helpers.js Normal file
View File

@@ -0,0 +1,323 @@
"use strict";
var conf = {
registrars: [],
dns_providers: [],
domains: []
};
var defaultArgs = [];
function initialize(){
conf = {
registrars: [],
dns_providers: [],
domains: []
};
defaultArgs = [];
}
function NewRegistrar(name,type,meta) {
if (type) {
type == "MANUAL";
}
var reg = {name: name, type: type, meta: meta};
conf.registrars.push(reg);
return name;
}
function NewDnsProvider(name, type, meta) {
if ((typeof meta === 'object') && ('ip_conversions' in meta)) {
meta.ip_conversions = format_tt(meta.ip_conversions)
}
var dsp = {name: name, type: type, meta: meta};
conf.dns_providers.push(dsp);
return name;
}
function newDomain(name,registrar) {
return {name: name, registrar: registrar, meta:{}, records:[], dnsProviders: {}, defaultTTL: 0, nameservers:[]};
}
function processDargs(m, domain) {
// for each modifier, if it is a...
// function: call it with domain
// array: process recursively
// object: merge it into metadata
if (_.isFunction(m)) {
m(domain);
} else if (_.isArray(m)) {
for (var j in m) {
processDargs(m[j], domain)
}
} else if (_.isObject(m)) {
_.extend(domain.meta,m);
} else {
throw "WARNING: domain modifier type unsupported: "+ typeof m + " Domain: "+ domain.name;
}
}
// D(name,registrar): Create a DNS Domain. Use the parameters as records and mods.
function D(name,registrar) {
var domain = newDomain(name,registrar);
for (var i = 0; i< defaultArgs.length; i++){
processDargs(defaultArgs[i],domain)
}
for (var i = 2; i<arguments.length; i++) {
var m = arguments[i];
processDargs(m, domain)
}
conf.domains.push(domain)
}
// DEFAULTS provides a set of default arguments to apply to all future domains.
// Each call to DEFAULTS will clear any previous values set.
function DEFAULTS(){
defaultArgs = [];
for (var i = 0; i<arguments.length; i++) {
defaultArgs.push(arguments[i]);
}
}
// TTL(v): Set the TTL for a DNS record.
function TTL(v) {
return function(r) {
r.ttl = v;
}
}
// DefaultTTL(v): Set the default TTL for the domain.
function DefaultTTL(v) {
return function(d) {
d.defaultTTL = v;
}
}
// DnsProvider("providerName", 0)
// nsCount of 0 means don't use or register any nameservers.
// nsCount not provider means use all.
function DnsProvider(name, nsCount){
if(typeof nsCount === 'undefined'){
nsCount = -1;
}
return function(d) {
d.dnsProviders[name] = nsCount;
}
}
// A(name,ip, recordModifiers...)
function A(name, ip) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"A",name,ip,mods)
}
}
// AAAA(name,ip, recordModifiers...)
function AAAA(name, ip) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"AAAA",name,ip,mods)
}
}
// ALIAS(name,target, recordModifiers...)
function ALIAS(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"ALIAS",name,target,mods)
}
}
// CNAME(name,target, recordModifiers...)
function CNAME(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"CNAME",name,target,mods)
}
}
// TXT(name,target, recordModifiers...)
function TXT(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"TXT",name,target,mods)
}
}
// MX(name,priority,target, recordModifiers...)
function MX(name, priority, target) {
checkArgs([_.isString, _.isNumber, _.isString], arguments, "MX expects (name, priority, target)")
var mods = getModifiers(arguments,3)
return function(d) {
mods.push(priority);
addRecord(d, "MX", name, target, mods)
}
}
function checkArgs(checks, args, desc){
if (args.length < checks.length){
throw(desc)
}
for (var i = 0; i< checks.length; i++){
if (!checks[i](args[i])){
throw(desc+" - argument "+i+" is not correct type")
}
}
}
// NS(name,target, recordModifiers...)
function NS(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"NS",name,target,mods)
}
}
// NAMESERVER(name,target)
function NAMESERVER(name, target) {
return function(d) {
d.nameservers.push({name: name, target: target})
}
}
function format_tt(transform_table) {
// Turn [[low: 1, high: 2, newBase: 3], [low: 4, high: 5, newIP: 6]]
// into "1 ~ 2 ~ 3 ~; 4 ~ 5 ~ ~ 6"
var lines = []
for (var i=0; i < transform_table.length; i++) {
var ip = transform_table[i];
var newIP = ip.newIP;
if (newIP){
if(_.isArray(newIP)){
newIP = _.map(newIP,function(i){return num2dot(i)}).join(",")
}else{
newIP = num2dot(newIP);
}
}
var newBase = ip.newBase;
if (newBase){
if(_.isArray(newBase)){
newBase = _.map(newBase,function(i){return num2dot(i)}).join(",")
}else{
newBase = num2dot(newBase);
}
}
var row = [
num2dot(ip.low),
num2dot(ip.high),
newBase,
newIP
]
lines.push(row.join(" ~ "))
}
return lines.join(" ; ")
}
// IMPORT_TRANSFORM(translation_table, domain)
function IMPORT_TRANSFORM(translation_table, domain,ttl) {
return function(d) {
var rec = addRecord(d, "IMPORT_TRANSFORM", "@", domain, [
{'transform_table': format_tt(translation_table)}])
if (ttl){
rec.ttl = ttl;
}
}
}
// PURGE()
function PURGE(d) {
d.KeepUnknown = false
}
// NO_PURGE()
function NO_PURGE(d) {
d.KeepUnknown = true
}
function getModifiers(args,start) {
var mods = [];
for (var i = start;i<args.length; i++) {
mods.push(args[i])
}
return mods;
}
function addRecord(d,type,name,target,mods) {
// if target is number, assume ip address. convert it.
if (_.isNumber(target)) {
target = num2dot(target);
}
var rec = {type: type, name: name, target: target, ttl:d.defaultTTL, priority: 0, meta:{}};
// for each modifier, decide based on type:
// - Function: call is with the record as the argument
// - Object: merge it into the metadata
// - Number: IF MX record assume it is priority
if (mods) {
for (var i = 0; i< mods.length; i++) {
var m = mods[i]
if (_.isFunction(m)) {
m(rec);
} else if (_.isObject(m)) {
//convert transforms to strings
if (m.transform && _.isArray(m.transform)){
m.transform = format_tt(m.transform)
}
_.extend(rec.meta,m);
_.extend(rec.meta,m);
} else if (_.isNumber(m) && type == "MX") {
rec.priority = m;
} else {
console.log("WARNING: Modifier type unsupported:", typeof m, "(Skipping!)");
}
}
}
d.records.push(rec);
return rec;
}
//ip conversion functions from http://stackoverflow.com/a/8105740/121660
// via http://javascript.about.com/library/blipconvert.htm
function IP(dot)
{
var d = dot.split('.');
return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
}
function num2dot(num)
{
if(num === undefined){
return "";
}
if (_.isString(num)){
return num
}
var d = num%256;
for (var i = 3; i > 0; i--)
{
num = Math.floor(num/256);
d = num%256 + '.' + d;
}
return d;
}
// CUSTOM, PROVIDER SPECIFIC RECORD TYPES
function CF_REDIRECT(src, dst) {
return function(d) {
if (src.indexOf(",") !== -1 || dst.indexOf(",") !== -1){
throw("redirect src and dst must not have commas")
}
addRecord(d,"CF_REDIRECT","@",src+","+dst)
}
}
function CF_TEMP_REDIRECT(src, dst) {
return function(d) {
if (src.indexOf(",") !== -1 || dst.indexOf(",") !== -1){
throw("redirect src and dst must not have commas")
}
addRecord(d,"CF_TEMP_REDIRECT","@",src+","+dst)
}
}

88
pkg/js/js.go Normal file
View File

@@ -0,0 +1,88 @@
package js
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/StackExchange/dnscontrol/models"
"github.com/StackExchange/dnscontrol/transform"
"github.com/robertkrimen/otto"
//load underscore js into vm by default
_ "github.com/robertkrimen/otto/underscore"
)
//ExecuteJavascript accepts a javascript string and runs it, returning the resulting dnsConfig.
func ExecuteJavascript(script string, devMode bool) (*models.DNSConfig, error) {
vm := otto.New()
vm.Set("require", require)
vm.Set("REVERSE", reverse)
helperJs := GetHelpers(devMode)
// run helper script to prime vm and initialize variables
if _, err := vm.Run(helperJs); err != nil {
return nil, err
}
// run user script
if _, err := vm.Run(script); err != nil {
return nil, err
}
// export conf as string and unmarshal
value, err := vm.Run(`JSON.stringify(conf)`)
if err != nil {
return nil, err
}
str, err := value.ToString()
if err != nil {
return nil, err
}
conf := &models.DNSConfig{}
if err = json.Unmarshal([]byte(str), conf); err != nil {
return nil, err
}
return conf, nil
}
func GetHelpers(devMode bool) string {
return _escFSMustString(devMode, "/helpers.js")
}
func require(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) != 1 {
throw(call.Otto, "require takes exactly one argument")
}
file := call.Argument(0).String()
fmt.Printf("requiring: %s\n", file)
data, err := ioutil.ReadFile(file)
if err != nil {
throw(call.Otto, err.Error())
}
_, err = call.Otto.Run(string(data))
if err != nil {
throw(call.Otto, err.Error())
}
return otto.TrueValue()
}
func throw(vm *otto.Otto, str string) {
panic(vm.MakeCustomError("Error", str))
}
func reverse(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) != 1 {
throw(call.Otto, "REVERSE takes exactly one argument")
}
dom := call.Argument(0).String()
rev, err := transform.ReverseDomainName(dom)
fmt.Println(dom, rev, err)
if err != nil {
throw(call.Otto, err.Error())
}
v, _ := otto.ToValue(rev)
return v
}

87
pkg/js/js_test.go Normal file
View File

@@ -0,0 +1,87 @@
package js
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"testing"
"unicode"
"github.com/StackExchange/dnscontrol/models"
)
const (
testDir = "pkg/js/parse_tests"
errorDir = "pkg/js/error_tests"
)
func init() {
os.Chdir("../..") // go up a directory so we helpers.js is in a consistent place.
}
func TestParsedFiles(t *testing.T) {
files, err := ioutil.ReadDir(testDir)
if err != nil {
t.Fatal(err)
}
for _, f := range files {
//run all js files that start with a number. Skip others.
if filepath.Ext(f.Name()) != ".js" || !unicode.IsNumber(rune(f.Name()[0])) {
continue
}
t.Run(f.Name(), func(t *testing.T) {
content, err := ioutil.ReadFile(filepath.Join(testDir, f.Name()))
if err != nil {
t.Fatal(err)
}
conf, err := ExecuteJavascript(string(content), true)
if err != nil {
t.Fatal(err)
}
actualJSON, err := json.MarshalIndent(conf, "", " ")
if err != nil {
t.Fatal(err)
}
expectedFile := filepath.Join(testDir, f.Name()[:len(f.Name())-3]+".json")
expectedData, err := ioutil.ReadFile(expectedFile)
if err != nil {
t.Fatal(err)
}
conf = &models.DNSConfig{}
//unmarshal and remarshal to not require manual formatting
err = json.Unmarshal(expectedData, conf)
if err != nil {
t.Fatal(err)
}
expectedJSON, err := json.MarshalIndent(conf, "", " ")
if err != nil {
t.Fatal(err)
}
if string(expectedJSON) != string(actualJSON) {
t.Error("Expected and actual json don't match")
t.Log("Expected:", string(expectedJSON))
t.Log("Actual:", string(actualJSON))
}
})
}
}
func TestErrors(t *testing.T) {
tests := []struct{ desc, text string }{
{"old dsp style", `D("foo.com","reg","dsp")`},
{"MX no priority", `D("foo.com","reg",MX("@","test."))`},
{"MX reversed", `D("foo.com","reg",MX("@","test.", 5))`},
{"CF_REDIRECT With comma", `D("foo.com","reg",CF_REDIRECT("foo.com,","baaa"))`},
{"CF_TEMP_REDIRECT With comma", `D("foo.com","reg",CF_TEMP_REDIRECT("foo.com","baa,a"))`},
{"Bad cidr", `D(reverse("foo.com"), "reg")`},
}
for _, tst := range tests {
t.Run(tst.desc, func(t *testing.T) {
if _, err := ExecuteJavascript(tst.text, true); err == nil {
t.Fatal("Expected error but found none")
}
})
}
}

View File

@@ -0,0 +1,6 @@
var REG = NewRegistrar("Third-Party","NONE");
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI")
D("foo.com",REG,DnsProvider(CF),
A("@","1.2.3.4")
);

View File

@@ -0,0 +1,30 @@
{
"registrars": [
{
"name": "Third-Party",
"type": "NONE"
}
],
"dns_providers": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"domains": [
{
"name": "foo.com",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare":-1
},
"records": [
{
"type": "A",
"name": "@",
"target": "1.2.3.4"
}
]
}
]
}

View File

@@ -0,0 +1,5 @@
var REG = NewRegistrar("Third-Party","NONE");
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI")
D("foo.com",REG,DnsProvider(CF),
A("@","1.2.3.4",TTL(42))
);

View File

@@ -0,0 +1,31 @@
{
"registrars": [
{
"name": "Third-Party",
"type": "NONE"
}
],
"dns_providers": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"domains": [
{
"name": "foo.com",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare":-1
},
"records": [
{
"type": "A",
"name": "@",
"target": "1.2.3.4",
"ttl": 42
}
]
}
]
}

View File

@@ -0,0 +1,5 @@
var CLOUDFLARE = NewRegistrar("Cloudflare","CLOUDFLAREAPI");
D("foo.com",CLOUDFLARE,
A("@","1.2.3.4",{"cloudflare_proxy":"ON"})
);

View File

@@ -0,0 +1,26 @@
{
"registrars": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"dns_providers": [],
"domains": [
{
"name": "foo.com",
"registrar": "Cloudflare",
"dnsProviders": {},
"records": [
{
"type": "A",
"name": "@",
"target": "1.2.3.4",
"meta": {
"cloudflare_proxy": "ON"
}
}
]
}
]
}

View File

@@ -0,0 +1,10 @@
var REG = NewRegistrar("Third-Party","NONE");
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI")
var BASE = IP("1.2.3.4")
D("foo.com",REG,DnsProvider(CF,0),
A("@",BASE),
A("p1",BASE+1),
A("p255", BASE+255)
);

View File

@@ -0,0 +1,28 @@
{
"registrars": [
{
"name": "Third-Party",
"type": "NONE"
}
],
"dns_providers": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"domains": [
{
"name": "foo.com",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare":0
},
"records": [
{ "type": "A","name": "@","target": "1.2.3.4"},
{ "type": "A","name": "p1","target": "1.2.3.5"},
{ "type": "A","name": "p255","target": "1.2.4.3"}
]
}
]
}

View File

@@ -0,0 +1,7 @@
var REG = NewRegistrar("Third-Party","NONE");
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI")
D("foo.com",REG,DnsProvider(CF,2),
A("@","1.2.3.4")
);
D("foo.com",REG);

View File

@@ -0,0 +1,35 @@
{ "registrars": [
{
"name": "Third-Party",
"type": "NONE"
}
],
"dns_providers": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"domains": [
{
"name": "foo.com",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare":2
},
"records": [
{
"type": "A",
"name": "@",
"target": "1.2.3.4"
}
]
},
{
"name": "foo.com",
"registrar": "Third-Party",
"dnsProviders": {},
"records": []
}
]
}

View File

@@ -0,0 +1,12 @@
var REG = NewRegistrar("Third-Party","NONE");
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI")
var TRANSFORM_INT = [
{low: "0.0.0.0", high: "1.1.1.1", newBase: "2.2.2.2" },
{low: "1.1.1.1", high: IP("2.2.2.2"), newBase: ["3.3.3.3","4.4.4.4",IP("5.5.5.5")]} ,
{low: "1.1.1.1", high: IP("2.2.2.2"), newIP: ["3.3.3.3","4.4.4.4",IP("5.5.5.5")]}
]
D("foo.com",REG,DnsProvider(CF),
A("@","1.2.3.4",{transform: TRANSFORM_INT})
);

View File

@@ -0,0 +1,34 @@
{
"registrars": [
{
"name": "Third-Party",
"type": "NONE"
}
],
"dns_providers": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"domains": [
{
"name": "foo.com",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare":-1
},
"records": [
{
"type": "A",
"name": "@",
"target": "1.2.3.4",
"meta": {
"transform": "0.0.0.0 ~ 1.1.1.1 ~ 2.2.2.2 ~ ; 1.1.1.1 ~ 2.2.2.2 ~ 3.3.3.3,4.4.4.4,5.5.5.5 ~ ; 1.1.1.1 ~ 2.2.2.2 ~ ~ 3.3.3.3,4.4.4.4,5.5.5.5"
}
}
],
"keepunknown": false
}
]
}

View File

@@ -0,0 +1,4 @@
var TRANSFORM_INT = [
{low: "0.0.0.0", high: "1.1.1.1", newBase: "2.2.2.2" }
]
D("foo.com","reg",IMPORT_TRANSFORM(TRANSFORM_INT,"foo2.com",60))

View File

@@ -0,0 +1,23 @@
{
"registrars": [],
"dns_providers": [],
"domains": [
{
"name": "foo.com",
"registrar": "reg",
"dnsProviders": {},
"records": [
{
"type": "IMPORT_TRANSFORM",
"name": "@",
"target": "foo2.com",
"ttl": 60,
"meta": {
"transform_table": "0.0.0.0 ~ 1.1.1.1 ~ 2.2.2.2 ~ "
}
}
],
"keepunknown": false
}
]
}

View File

@@ -0,0 +1,2 @@
require("pkg/js/parse_tests/import.js")

View File

@@ -0,0 +1,19 @@
{
"registrars": [],
"dns_providers": [],
"domains": [
{
"name": "foo.com",
"registrar": "none",
"dnsProviders": {},
"records": [
{
"type": "A",
"name": "@",
"target": "1.2.3.4"
}
],
"keepunknown": false
}
]
}

View File

@@ -0,0 +1 @@
D(REVERSE("1.2.0.0/16"),"none");

View File

@@ -0,0 +1,13 @@
{
"registrars": [],
"dns_providers": [],
"domains": [
{
"name": "2.1.in-addr.arpa",
"registrar": "none",
"dnsProviders": {},
"records": [],
"keepunknown": false
}
]
}

View File

@@ -0,0 +1,3 @@
D("foo.com","none",
ALIAS("@","foo.com.")
);

View File

@@ -0,0 +1,19 @@
{
"registrars": [],
"dns_providers": [],
"domains": [
{
"name": "foo.com",
"registrar": "none",
"dnsProviders": {},
"records": [
{
"type": "ALIAS",
"name": "@",
"target": "foo.com."
}
],
"keepunknown": false
}
]
}

View File

@@ -0,0 +1,4 @@
D("foo.com","none",
CF_REDIRECT("test.foo.com","https://goo.com/$1"),
CF_TEMP_REDIRECT("test.foo.com","https://goo.com/$1")
);

View File

@@ -0,0 +1,24 @@
{
"registrars": [],
"dns_providers": [],
"domains": [
{
"name": "foo.com",
"registrar": "none",
"dnsProviders": {},
"records": [
{
"type": "CF_REDIRECT",
"name": "@",
"target": "test.foo.com,https://goo.com/$1"
},
{
"type": "CF_TEMP_REDIRECT",
"name": "@",
"target": "test.foo.com,https://goo.com/$1"
}
],
"keepunknown": false
}
]
}

View File

@@ -0,0 +1,3 @@
D("foo.com","none",
A("@","1.2.3.4")
);

245
pkg/js/static.go Normal file
View File

@@ -0,0 +1,245 @@
package js
import (
"bytes"
"compress/gzip"
"encoding/base64"
"io/ioutil"
"net/http"
"os"
"path"
"sync"
"time"
)
type _escLocalFS struct{}
var _escLocal _escLocalFS
type _escStaticFS struct{}
var _escStatic _escStaticFS
type _escDirectory struct {
fs http.FileSystem
name string
}
type _escFile struct {
compressed string
size int64
modtime int64
local string
isDir bool
once sync.Once
data []byte
name string
}
func (_escLocalFS) Open(name string) (http.File, error) {
f, present := _escData[path.Clean(name)]
if !present {
return nil, os.ErrNotExist
}
return os.Open(f.local)
}
func (_escStaticFS) prepare(name string) (*_escFile, error) {
f, present := _escData[path.Clean(name)]
if !present {
return nil, os.ErrNotExist
}
var err error
f.once.Do(func() {
f.name = path.Base(name)
if f.size == 0 {
return
}
var gr *gzip.Reader
b64 := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(f.compressed))
gr, err = gzip.NewReader(b64)
if err != nil {
return
}
f.data, err = ioutil.ReadAll(gr)
})
if err != nil {
return nil, err
}
return f, nil
}
func (fs _escStaticFS) Open(name string) (http.File, error) {
f, err := fs.prepare(name)
if err != nil {
return nil, err
}
return f.File()
}
func (dir _escDirectory) Open(name string) (http.File, error) {
return dir.fs.Open(dir.name + name)
}
func (f *_escFile) File() (http.File, error) {
type httpFile struct {
*bytes.Reader
*_escFile
}
return &httpFile{
Reader: bytes.NewReader(f.data),
_escFile: f,
}, nil
}
func (f *_escFile) Close() error {
return nil
}
func (f *_escFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil
}
func (f *_escFile) Stat() (os.FileInfo, error) {
return f, nil
}
func (f *_escFile) Name() string {
return f.name
}
func (f *_escFile) Size() int64 {
return f.size
}
func (f *_escFile) Mode() os.FileMode {
return 0
}
func (f *_escFile) ModTime() time.Time {
return time.Unix(f.modtime, 0)
}
func (f *_escFile) IsDir() bool {
return f.isDir
}
func (f *_escFile) Sys() interface{} {
return f
}
// _escFS returns a http.Filesystem for the embedded assets. If useLocal is true,
// the filesystem's contents are instead used.
func _escFS(useLocal bool) http.FileSystem {
if useLocal {
return _escLocal
}
return _escStatic
}
// _escDir returns a http.Filesystem for the embedded assets on a given prefix dir.
// If useLocal is true, the filesystem's contents are instead used.
func _escDir(useLocal bool, name string) http.FileSystem {
if useLocal {
return _escDirectory{fs: _escLocal, name: name}
}
return _escDirectory{fs: _escStatic, name: name}
}
// _escFSByte returns the named file from the embedded assets. If useLocal is
// true, the filesystem's contents are instead used.
func _escFSByte(useLocal bool, name string) ([]byte, error) {
if useLocal {
f, err := _escLocal.Open(name)
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(f)
f.Close()
return b, err
}
f, err := _escStatic.prepare(name)
if err != nil {
return nil, err
}
return f.data, nil
}
// _escFSMustByte is the same as _escFSByte, but panics if name is not present.
func _escFSMustByte(useLocal bool, name string) []byte {
b, err := _escFSByte(useLocal, name)
if err != nil {
panic(err)
}
return b
}
// _escFSString is the string version of _escFSByte.
func _escFSString(useLocal bool, name string) (string, error) {
b, err := _escFSByte(useLocal, name)
return string(b), err
}
// _escFSMustString is the string version of _escFSMustByte.
func _escFSMustString(useLocal bool, name string) string {
return string(_escFSMustByte(useLocal, name))
}
var _escData = map[string]*_escFile{
"/helpers.js": {
local: "pkg/js/helpers.js",
size: 8320,
modtime: 0,
compressed: `
H4sIAAAAAAAA/9w5W2/bytHv+hUTAl9EfqLpSxK3oKKiqi0fGLVkQ5ZPXQiCsSZX0ia8YXcpx82Rf3ux
F5JLUoodoOlD8+Bod+c+szOzQytnGBinJOBWv9PZIApBmixhAN87AAAUrwjjFFHmw3zhyr0wYQ8ZTTck
xLXtNEYkkRudraYV4iXKIz6kKwYDmC/6nc4yTwJO0gRIQjhBEfkXth3FrMZ5H/cfSNCUQqy3fSVcS5Ct
IcoEP00LVnaCYuzy5wy7MebI0eKQJdhi0ynFEysYDMAaDyd3wytLMdrKv0J3ildCGUHOB0lUovjyrwuC
uC//ahGF9l6lsZflbG1TvHL62hM8p4kk1BL+PGE32hx2xUnxMBQAW6qQLuUBDAYD6KaPX3DAuw68fw92
l2QPQZpsMGUkTVgXSKJoOIZTxIZXB4QBLFMaI/7Aub3j3GmYJmTZz5um5nRlnZBlr1knwU/nMiSUYUr7
OmWAS8SaLCWQX/3UUn3fiuMgpSHz5wtXROJNFYjiVEfabHblw5ErKTJMhSX8+WJbFy6jaYAZO0d0xezY
1cFrGvvwUFgWMArWEKchWRJMXeFLwoEwQJ7n1WA1ZR8CFEUC6InwtaZrAiJK0bNfCCBUyikjGxw9m1Aq
OIQr6ApLlglPpSFCxFEJKe7Gg0fYheZux7WAKeLG1ur1y5Mt4IjhEn8ohNqBLCxgi7j5IgOyTbtux/mX
RWnKGuB2H+NrqecOzg8e/sZxEmrRPaG6G7c1MLH4mqZPYP1jOJ1cTn7ztSSl91TeyBOWZ1lKOQ59sHpQ
3EvogQUqYOW+5qviutJj2+kcHsJ5M6Z9OKMYcQwIzie3mo4HdwwDX2PIEEUx5pgyQKwIY0BJKIRjXhWX
LcJaQXl3lTqD/TdLCVo6jcAAjvpAPptJ2ItwsuLrPpBezymtV/OjAT0nC9dw6LbN4EQwQHSVxzjhdeqG
cwR0DAMoAedkUZl1z22scpdKQ6rA6ASkQbQ/RhfDu6vZLeg0xQABwxzSZaF6xRl4CijLomf5I4pgmfOc
4qJ+eYLeSNx6eZF5WhF/IlEEQYQRBZQ8Q0bxhqQ5gw2KcswEQ9OTGqsose06uNtXr5rS9KU0hWlTp6iF
yi6z2ZW9cXy4xVzG4Wx2JVmqKFVxaMiswOv5uTi0qSkE9TiPYACbOr/zMgXX2BY+KNjLPXVFDIOZuHtk
CGuG8KqM3xBFCWPUZquoXxMUY8uFIwcESMLO0jyRcXIEMUYJgzBNuhxEc5ZSXYSw8rdRUDwTOUl5EXdU
ExHoKIpM7VqNgkZ3iiah6BAKsrJJyJMQL0mCw251VysIODg2e5/XrGVUzLmQYSFyiaJVd+NQiUiyouSO
dQplnuc5lVIaDkhm5imR0mAAK8xLtCpG3RPndVlRGE4lXzt0raHlFtIIyk5d0uHwzcKWoL9Y3uHwxyJf
XQ5vda+L6Arz1+Su4EEh/ErhBTMtvZauoYFQ4WwyHI9+QgUD/terIJn9UAWRGO9nPyF/Cf3rpZ/dz16T
fXyvhMkoSSnhz2/TocCCEq2hTLDGwVdRVey56MxuOSXJygXxe5LHj6L7rfYXblVQXbDG94C/ZTjgDPZx
sZw3muzDG0wmuyZZ/Ao+Rmdo2lOIZrlgOs+FhklLE1UWkL+Y1JGJhwULnOoxiqouCj4rpGJtJGnZjNoS
1UjRO3qzGoFGWyb5vVMQc7KQrEWVd+rNcsWrZ8FB6RmweqRnideKKFFBSikOuGx4Lcdoac3YmvxMZpr8
19LS5Mc5SQg+HI9uR9PfR1NTAVPYBkBD6Fdqp1n7ZdzVn9CSlK//3+6KreqVzilKmFg+cPQY6bGGSEmC
/3wepU8+HLuwJqu1Dyeu6Pb/hhj24cPCBXX8sTj+JI8vb3w4XSwUGflQtI7hBU7gBT7ASx8+wgt8gheA
Fzi1OspBEUmwakQ7ZlQOREzCZ2gIuasXlfAZDJqwZWcvAKR0MACSefJnv7xFclmLdOMlqg4bUV7QevBi
lCkQt/QXcb4Xk4g8PglTbhNn63hfUpLYlmvGu3g27iZcYCru/dYVMZQSHinVEouaYmLjB6rJ47Zymmap
nlj/xxTUxA0VpRT7lRRP6QHM9XnJM/Oi9Mlx29siIKt9LX3HMLD8rUaDMvj0mC190jrAC1iOUEPIoFVV
gPq8D1bx3rsc31xPZw+z6XBye3E9HatLFSFhKRWF1SOyvIJvR3I5j96UGNS0MRAP21rRabKyXLD+apXk
S7Oqf9+7jSvU9Zv5wpTS2S6cWoEQ0tYdTnGgH2icR20fKyPe3E1/G9mGgdSGVjD0/o5xdpd8TdKnBAaw
RBHDRbK9fmghl3t78DnNcS0jNmsDcxlHdFcV2flYlsB9+V7e+1Su2oSicLZfSwKmPhs0XSnHoq3Ko1mI
bLvUSV9WWd0mIcbyGIvkiMKQYsY8UCNZDoR7ZaKoOitb1yJTdk22urIapj3sFuH33Zzi7i9NrogH33w4
V52aHJrqUaue/u6egYY4ICGGR8RwCGmiBsgF/AFcNCahTE1CxZtfdROAmFwV/UCFer1z6ilga5NPCass
58PlBYzvK8rK8tIdhWKlwU3fteJJNWMyYvZEExhzLAE3J4va2duGsRDbFAdG4oWfmIqCUr+IpjJtyKEW
k505ayNI3b0SGN6/B2PoWx00a1IpsYFb+95goLYRt62tcqYr0lNroPt2qIa19B2K5ZeU6tvQvbXDeoJm
ERfCjTsJt60QpAlLRRuUruxqvjzeO1i23HKu7IJl334lWUaS1TvHaqqys/6Gnh4RF5+igvrHFoqDvkrF
JIPqa09ZpBgsaRrDmvPMPzxkHAVf0w2myyh98oI0PkSHfz4++vSnj0eHxyfHp6dHIqdvCCoQvqANYgEl
GffQY5pziRORR4ro8+FjRDIdf96ax0Z5vbHDlDsdY2ANAwhT7rEsItzuet26Frb81wvnRwvn/08+nTo9
sTheOMbqpLb6sHAa35iKdiaPC8ZkKVZyelYOzxzzw6bkbdU+GhaRpN62klobJcnjRuoNVXb+v5NPpzsK
1AfRSf9F5pWDA3U/jBGeEBHGiK+9ZZSmVPA8FHpW4WFQhx50vS70INwx7gt1KMDZ3e3seuzCzfT698vz
0RRub0ZnlxeXZzAdnV1Pz2H2z5vRrTGVuXiYjs4vp6Ozmc1o4ELI3vYcEuZiNPBIEuJv10vZfsK7wQAO
juGPPwSZXUc736wWxSGRz1JGA/lBJGQc4pypseoabTAEaRwj1nqyQmvwU+ljuaLdYjToWa7VE3qVnY+p
/mw0vvmfs0FNqf2G+HcAAAD//xD4Q9mAIAAA
`,
},
"/": {
isDir: true,
local: "pkg/js",
},
}