github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/goagen/gen_js/generator.go (about)

     1  package genjs
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"sort"
    10  	"strings"
    11  	"text/template"
    12  	"time"
    13  
    14  	"github.com/goadesign/goa/design"
    15  	"github.com/goadesign/goa/goagen/codegen"
    16  	"github.com/goadesign/goa/goagen/utils"
    17  )
    18  
    19  //NewGenerator returns an initialized instance of a JavaScript Client Generator
    20  func NewGenerator(options ...Option) *Generator {
    21  	g := &Generator{}
    22  
    23  	for _, option := range options {
    24  		option(g)
    25  	}
    26  
    27  	return g
    28  }
    29  
    30  // Generator is the application code generator.
    31  type Generator struct {
    32  	API       *design.APIDefinition // The API definition
    33  	OutDir    string                // Destination directory
    34  	Timeout   time.Duration         // Timeout used by JavaScript client when making requests
    35  	Scheme    string                // Scheme used by JavaScript client
    36  	Host      string                // Host addressed by JavaScript client
    37  	NoExample bool                  // Do not generate an HTML example file
    38  	genfiles  []string              // Generated files
    39  }
    40  
    41  // Generate is the generator entry point called by the meta generator.
    42  func Generate() (files []string, err error) {
    43  	var (
    44  		outDir, ver  string
    45  		timeout      time.Duration
    46  		scheme, host string
    47  		noexample    bool
    48  	)
    49  
    50  	set := flag.NewFlagSet("client", flag.PanicOnError)
    51  	set.StringVar(&outDir, "out", "", "")
    52  	set.String("design", "", "")
    53  	set.DurationVar(&timeout, "timeout", time.Duration(20)*time.Second, "")
    54  	set.StringVar(&scheme, "scheme", "", "")
    55  	set.StringVar(&host, "host", "", "")
    56  	set.StringVar(&ver, "version", "", "")
    57  	set.BoolVar(&noexample, "noexample", false, "")
    58  	set.Parse(os.Args[1:])
    59  
    60  	// First check compatibility
    61  	if err := codegen.CheckVersion(ver); err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	// Now proceed
    66  	g := &Generator{OutDir: outDir, Timeout: timeout, Scheme: scheme, Host: host, NoExample: noexample, API: design.Design}
    67  
    68  	return g.Generate()
    69  }
    70  
    71  // Generate produces the skeleton main.
    72  func (g *Generator) Generate() (_ []string, err error) {
    73  	if g.API == nil {
    74  		return nil, fmt.Errorf("missing API definition, make sure design is properly initialized")
    75  	}
    76  
    77  	go utils.Catch(nil, func() { g.Cleanup() })
    78  
    79  	defer func() {
    80  		if err != nil {
    81  			g.Cleanup()
    82  		}
    83  	}()
    84  
    85  	if g.Timeout == 0 {
    86  		g.Timeout = 20 * time.Second
    87  	}
    88  	if g.Scheme == "" && len(g.API.Schemes) > 0 {
    89  		g.Scheme = g.API.Schemes[0]
    90  	}
    91  	if g.Scheme == "" {
    92  		g.Scheme = "http"
    93  	}
    94  	if g.Host == "" {
    95  		g.Host = g.API.Host
    96  	}
    97  	if g.Host == "" {
    98  		return nil, fmt.Errorf("missing host value, set it with --host")
    99  	}
   100  
   101  	g.OutDir = filepath.Join(g.OutDir, "js")
   102  	if err := os.RemoveAll(g.OutDir); err != nil {
   103  		return nil, err
   104  	}
   105  	if err := os.MkdirAll(g.OutDir, 0755); err != nil {
   106  		return nil, err
   107  	}
   108  	g.genfiles = append(g.genfiles, g.OutDir)
   109  
   110  	// Generate client.js
   111  	exampleAction, err := g.generateJS(filepath.Join(g.OutDir, "client.js"))
   112  	if err != nil {
   113  		return
   114  	}
   115  
   116  	// Generate axios.html
   117  	if err = g.generateAxiosJS(); err != nil {
   118  		return
   119  	}
   120  
   121  	if exampleAction != nil && !g.NoExample {
   122  		// Generate index.html
   123  		if err = g.generateIndexHTML(filepath.Join(g.OutDir, "index.html"), exampleAction); err != nil {
   124  			return
   125  		}
   126  
   127  		// Generate example
   128  		if err = g.generateExample(); err != nil {
   129  			return
   130  		}
   131  	}
   132  
   133  	return g.genfiles, nil
   134  }
   135  
   136  func (g *Generator) generateJS(jsFile string) (_ *design.ActionDefinition, err error) {
   137  	file, err := codegen.SourceFileFor(jsFile)
   138  	if err != nil {
   139  		return
   140  	}
   141  	g.genfiles = append(g.genfiles, jsFile)
   142  
   143  	data := map[string]interface{}{
   144  		"API":     g.API,
   145  		"Host":    g.Host,
   146  		"Scheme":  g.Scheme,
   147  		"Timeout": int64(g.Timeout / time.Millisecond),
   148  	}
   149  	if err = file.ExecuteTemplate("module", moduleT, nil, data); err != nil {
   150  		return
   151  	}
   152  
   153  	actions := make(map[string][]*design.ActionDefinition)
   154  	g.API.IterateResources(func(res *design.ResourceDefinition) error {
   155  		return res.IterateActions(func(action *design.ActionDefinition) error {
   156  			if as, ok := actions[action.Name]; ok {
   157  				actions[action.Name] = append(as, action)
   158  			} else {
   159  				actions[action.Name] = []*design.ActionDefinition{action}
   160  			}
   161  			return nil
   162  		})
   163  	})
   164  
   165  	var exampleAction *design.ActionDefinition
   166  	keys := []string{}
   167  	for n := range actions {
   168  		keys = append(keys, n)
   169  	}
   170  	sort.Strings(keys)
   171  	for _, n := range keys {
   172  		for _, a := range actions[n] {
   173  			if exampleAction == nil && a.Routes[0].Verb == "GET" {
   174  				exampleAction = a
   175  			}
   176  			data := map[string]interface{}{"Action": a}
   177  			funcs := template.FuncMap{"params": params}
   178  			if err = file.ExecuteTemplate("jsFuncs", jsFuncsT, funcs, data); err != nil {
   179  				return
   180  			}
   181  		}
   182  	}
   183  
   184  	_, err = file.Write([]byte(moduleTend))
   185  	return exampleAction, err
   186  }
   187  
   188  func (g *Generator) generateIndexHTML(htmlFile string, exampleAction *design.ActionDefinition) error {
   189  	file, err := codegen.SourceFileFor(htmlFile)
   190  	if err != nil {
   191  		return err
   192  	}
   193  	g.genfiles = append(g.genfiles, htmlFile)
   194  
   195  	argNames := params(exampleAction)
   196  	var args string
   197  	if len(argNames) > 0 {
   198  		query := exampleAction.QueryParams.Type.ToObject()
   199  		argValues := make([]string, len(argNames))
   200  		for i, n := range argNames {
   201  			ex := query[n].GenerateExample(g.API.RandomGenerator(), nil)
   202  			argValues[i] = fmt.Sprintf("%v", ex)
   203  		}
   204  		args = strings.Join(argValues, ", ")
   205  	}
   206  	examplePath := exampleAction.Routes[0].FullPath()
   207  	pathParams := exampleAction.Routes[0].Params()
   208  	if len(pathParams) > 0 {
   209  		pathVars := exampleAction.AllParams().Type.ToObject()
   210  		pathValues := make([]interface{}, len(pathParams))
   211  		for i, n := range pathParams {
   212  			ex := pathVars[n].GenerateExample(g.API.RandomGenerator(), nil)
   213  			pathValues[i] = ex
   214  		}
   215  		format := design.WildcardRegex.ReplaceAllLiteralString(examplePath, "/%v")
   216  		examplePath = fmt.Sprintf(format, pathValues...)
   217  	}
   218  	if len(argNames) > 0 {
   219  		args = ", " + args
   220  	}
   221  	exampleFunc := fmt.Sprintf(
   222  		`%s%s ("%s"%s)`,
   223  		exampleAction.Name,
   224  		strings.Title(exampleAction.Parent.Name),
   225  		examplePath,
   226  		args,
   227  	)
   228  	data := map[string]interface{}{
   229  		"API":         g.API,
   230  		"ExampleFunc": exampleFunc,
   231  	}
   232  
   233  	return file.ExecuteTemplate("exampleHTML", exampleT, nil, data)
   234  }
   235  
   236  func (g *Generator) generateAxiosJS() error {
   237  	filePath := filepath.Join(g.OutDir, "axios.min.js")
   238  	if err := ioutil.WriteFile(filePath, []byte(axios), 0644); err != nil {
   239  		return err
   240  	}
   241  	g.genfiles = append(g.genfiles, filePath)
   242  
   243  	return nil
   244  }
   245  
   246  func (g *Generator) generateExample() error {
   247  	controllerFile := filepath.Join(g.OutDir, "example.go")
   248  	file, err := codegen.SourceFileFor(controllerFile)
   249  	if err != nil {
   250  		return err
   251  	}
   252  	imports := []*codegen.ImportSpec{
   253  		codegen.SimpleImport("net/http"),
   254  		codegen.SimpleImport("github.com/dimfeld/httptreemux"),
   255  		codegen.SimpleImport("github.com/goadesign/goa"),
   256  	}
   257  	if err := file.WriteHeader(fmt.Sprintf("%s JavaScript Client Example", g.API.Name), "js", imports); err != nil {
   258  		return err
   259  	}
   260  	g.genfiles = append(g.genfiles, controllerFile)
   261  
   262  	data := map[string]interface{}{"ServeDir": g.OutDir}
   263  	if err := file.ExecuteTemplate("examples", exampleCtrlT, nil, data); err != nil {
   264  		return err
   265  	}
   266  
   267  	return file.FormatCode()
   268  }
   269  
   270  // Cleanup removes all the files generated by this generator during the last invokation of Generate.
   271  func (g *Generator) Cleanup() {
   272  	for _, f := range g.genfiles {
   273  		os.Remove(f)
   274  	}
   275  	g.genfiles = nil
   276  }
   277  
   278  func params(action *design.ActionDefinition) []string {
   279  	if action.QueryParams == nil {
   280  		return nil
   281  	}
   282  	params := make([]string, len(action.QueryParams.Type.ToObject()))
   283  	i := 0
   284  	for n := range action.QueryParams.Type.ToObject() {
   285  		params[i] = n
   286  		i++
   287  	}
   288  	sort.Strings(params)
   289  	return params
   290  }
   291  
   292  const moduleT = `// This module exports functions that give access to the {{.API.Name}} API hosted at {{.API.Host}}.
   293  // It uses the axios javascript library for making the actual HTTP requests.
   294  define(['axios'] , function (axios) {
   295    function merge(obj1, obj2) {
   296      var obj3 = {};
   297      for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
   298      for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
   299      return obj3;
   300    }
   301  
   302    return function (scheme, host, timeout) {
   303      scheme = scheme || '{{.Scheme}}';
   304      host = host || '{{.Host}}';
   305      timeout = timeout || {{.Timeout}};
   306  
   307      // Client is the object returned by this module.
   308      var client = axios;
   309  
   310      // URL prefix for all API requests.
   311      var urlPrefix = scheme + '://' + host;
   312  `
   313  
   314  const moduleTend = `  return client;
   315    };
   316  });
   317  `
   318  
   319  const jsFuncsT = `{{$params := params .Action}}
   320    {{$name := printf "%s%s" .Action.Name (title .Action.Parent.Name)}}// {{if .Action.Description}}{{.Action.Description}}{{else}}{{$name}} calls the {{.Action.Name}} action of the {{.Action.Parent.Name}} resource.{{end}}
   321    // path is the request path, the format is "{{(index .Action.Routes 0).FullPath}}"
   322    {{if .Action.Payload}}// data contains the action payload (request body)
   323    {{end}}{{if $params}}// {{join $params ", "}} {{if gt (len $params) 1}}are{{else}}is{{end}} used to build the request query string.
   324    {{end}}// config is an optional object to be merged into the config built by the function prior to making the request.
   325    // The content of the config object is described here: https://github.com/mzabriskie/axios#request-api
   326    // This function returns a promise which raises an error if the HTTP response is a 4xx or 5xx.
   327    client.{{$name}} = function (path{{if .Action.Payload}}, data{{end}}{{if $params}}, {{join $params ", "}}{{end}}, config) {
   328      cfg = {
   329        timeout: timeout,
   330        url: urlPrefix + path,
   331        method: '{{toLower (index .Action.Routes 0).Verb}}',
   332  {{if $params}}      params: {
   333  {{range $index, $param := $params}}{{if $index}},
   334  {{end}}        {{$param}}: {{$param}}{{end}}
   335        },
   336  {{end}}{{if .Action.Payload}}    data: data,
   337  {{end}}      responseType: 'json'
   338      };
   339      if (config) {
   340        cfg = merge(cfg, config);
   341      }
   342      return client(cfg);
   343    }
   344  `
   345  
   346  const exampleT = `<!doctype html>
   347  <html>
   348    <head>
   349      <title>goa JavaScript client loader</title>
   350    </head>
   351    <body>
   352      <h1>{{.API.Name}} Client Test</h1>
   353      <div id="response"></div>
   354      <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.16/require.min.js"></script>
   355      <script>
   356        requirejs.config({
   357          paths: {
   358            axios: '/js/axios.min',
   359            client: '/js/client'
   360          }
   361        });
   362        requirejs(['client'], function (client) {
   363          client().{{.ExampleFunc}}
   364            .then(function (resp) {
   365              document.getElementById('response').innerHTML = resp.statusText;
   366            })
   367            .catch(function (resp) {
   368              document.getElementById('response').innerHTML = resp.statusText;
   369            });
   370        });
   371      </script>
   372    </body>
   373  </html>
   374  `
   375  
   376  const exampleCtrlT = `// MountController mounts the JavaScript example controller under "/js".
   377  // This is just an example, not the best way to do this. A better way would be to specify a file
   378  // server using the Files DSL in the design.
   379  // Use --noexample to prevent this file from being generated.
   380  func MountController(service *goa.Service) {
   381  	// Serve static files under js
   382  	service.ServeFiles("/js/*filepath", {{printf "%q" .ServeDir}})
   383  	service.LogInfo("mount", "ctrl", "JS", "action", "ServeFiles", "route", "GET /js/*")
   384  }
   385  `
   386  
   387  const axios = `/* axios v0.7.0 | (c) 2015 by Matt Zabriskie */
   388  !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";var r=n(2),o=n(3),i=n(4),s=n(12),u=e.exports=function(e){"string"==typeof e&&(e=o.merge({url:arguments[0]},arguments[1])),e=o.merge({method:"get",headers:{},timeout:r.timeout,transformRequest:r.transformRequest,transformResponse:r.transformResponse},e),e.withCredentials=e.withCredentials||r.withCredentials;var t=[i,void 0],n=Promise.resolve(e);for(u.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),u.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n};u.defaults=r,u.all=function(e){return Promise.all(e)},u.spread=n(13),u.interceptors={request:new s,response:new s},function(){function e(){o.forEach(arguments,function(e){u[e]=function(t,n){return u(o.merge(n||{},{method:e,url:t}))}})}function t(){o.forEach(arguments,function(e){u[e]=function(t,n,r){return u(o.merge(r||{},{method:e,url:t,data:n}))}})}e("delete","get","head"),t("post","put","patch")}()},function(e,t,n){"use strict";var r=n(3),o=/^\)\]\}',?\n/,i={"Content-Type":"application/x-www-form-urlencoded"};e.exports={transformRequest:[function(e,t){return r.isFormData(e)?e:r.isArrayBuffer(e)?e:r.isArrayBufferView(e)?e.buffer:!r.isObject(e)||r.isFile(e)||r.isBlob(e)?e:(r.isUndefined(t)||(r.forEach(t,function(e,n){"content-type"===n.toLowerCase()&&(t["Content-Type"]=e)}),r.isUndefined(t["Content-Type"])&&(t["Content-Type"]="application/json")),JSON.stringify(e))}],transformResponse:[function(e){if("string"==typeof e){e=e.replace(o,"");try{e=JSON.parse(e)}catch(t){}}return e}],headers:{common:{Accept:"application/json, text/plain, */*"},patch:r.merge(i),post:r.merge(i),put:r.merge(i)},timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"}},function(e,t){"use strict";function n(e){return"[object Array]"===v.call(e)}function r(e){return"[object ArrayBuffer]"===v.call(e)}function o(e){return"[object FormData]"===v.call(e)}function i(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function s(e){return"string"==typeof e}function u(e){return"number"==typeof e}function a(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function c(e){return"[object Date]"===v.call(e)}function p(e){return"[object File]"===v.call(e)}function l(e){return"[object Blob]"===v.call(e)}function d(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function h(e){return"[object Arguments]"===v.call(e)}function m(){return"undefined"!=typeof window&&"undefined"!=typeof document&&"function"==typeof document.createElement}function y(e,t){if(null!==e&&"undefined"!=typeof e){var r=n(e)||h(e);if("object"==typeof e||r||(e=[e]),r)for(var o=0,i=e.length;i>o;o++)t.call(null,e[o],o,e);else for(var s in e)e.hasOwnProperty(s)&&t.call(null,e[s],s,e)}}function g(){var e={};return y(arguments,function(t){y(t,function(t,n){e[n]=t})}),e}var v=Object.prototype.toString;e.exports={isArray:n,isArrayBuffer:r,isFormData:o,isArrayBufferView:i,isString:s,isNumber:u,isObject:f,isUndefined:a,isDate:c,isFile:p,isBlob:l,isStandardBrowserEnv:m,forEach:y,merge:g,trim:d}},function(e,t,n){(function(t){"use strict";e.exports=function(e){return new Promise(function(r,o){try{"undefined"!=typeof XMLHttpRequest||"undefined"!=typeof ActiveXObject?n(6)(r,o,e):"undefined"!=typeof t&&n(6)(r,o,e)}catch(i){o(i)}})}}).call(t,n(5))},function(e,t){function n(){f=!1,s.length?a=s.concat(a):c=-1,a.length&&r()}function r(){if(!f){var e=setTimeout(n);f=!0;for(var t=a.length;t;){for(s=a,a=[];++c<t;)s&&s[c].run();c=-1,t=a.length}s=null,f=!1,clearTimeout(e)}}function o(e,t){this.fun=e,this.array=t}function i(){}var s,u=e.exports={},a=[],f=!1,c=-1;u.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];a.push(new o(e,t)),1!==a.length||f||setTimeout(r,0)},o.prototype.run=function(){this.fun.apply(null,this.array)},u.title="browser",u.browser=!0,u.env={},u.argv=[],u.version="",u.versions={},u.on=i,u.addListener=i,u.once=i,u.off=i,u.removeListener=i,u.removeAllListeners=i,u.emit=i,u.binding=function(e){throw new Error("process.binding is not supported")},u.cwd=function(){return"/"},u.chdir=function(e){throw new Error("process.chdir is not supported")},u.umask=function(){return 0}},function(e,t,n){"use strict";var r=n(2),o=n(3),i=n(7),s=n(8),u=n(9);e.exports=function(e,t,a){var f=u(a.data,a.headers,a.transformRequest),c=o.merge(r.headers.common,r.headers[a.method]||{},a.headers||{});o.isFormData(f)&&delete c["Content-Type"];var p=new(XMLHttpRequest||ActiveXObject)("Microsoft.XMLHTTP");if(p.open(a.method.toUpperCase(),i(a.url,a.params),!0),p.timeout=a.timeout,p.onreadystatechange=function(){if(p&&4===p.readyState){var n=s(p.getAllResponseHeaders()),r=-1!==["text",""].indexOf(a.responseType||"")?p.responseText:p.response,o={data:u(r,n,a.transformResponse),status:p.status,statusText:p.statusText,headers:n,config:a};(p.status>=200&&p.status<300?e:t)(o),p=null}},o.isStandardBrowserEnv()){var l=n(10),d=n(11),h=d(a.url)?l.read(a.xsrfCookieName||r.xsrfCookieName):void 0;h&&(c[a.xsrfHeaderName||r.xsrfHeaderName]=h)}if(o.forEach(c,function(e,t){f||"content-type"!==t.toLowerCase()?p.setRequestHeader(t,e):delete c[t]}),a.withCredentials&&(p.withCredentials=!0),a.responseType)try{p.responseType=a.responseType}catch(m){if("json"!==p.responseType)throw m}o.isArrayBuffer(f)&&(f=new DataView(f)),p.send(f)}},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(3);e.exports=function(e,t){if(!t)return e;var n=[];return o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)&&(t+="[]"),o.isArray(e)||(e=[e]),o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),n.push(r(t)+"="+r(e))}))}),n.length>0&&(e+=(-1===e.indexOf("?")?"?":"&")+n.join("&")),e}},function(e,t,n){"use strict";var r=n(3);e.exports=function(e){var t,n,o,i={};return e?(r.forEach(e.split("\n"),function(e){o=e.indexOf(":"),t=r.trim(e.substr(0,o)).toLowerCase(),n=r.trim(e.substr(o+1)),t&&(i[t]=i[t]?i[t]+", "+n:n)}),i):i}},function(e,t,n){"use strict";var r=n(3);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t,n){"use strict";var r=n(3);e.exports={write:function(e,t,n,o,i,s){var u=[];u.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&u.push("expires="+new Date(n).toGMTString()),r.isString(o)&&u.push("path="+o),r.isString(i)&&u.push("domain="+i),s===!0&&u.push("secure"),document.cookie=u.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}},function(e,t,n){"use strict";function r(e){var t=e;return s&&(u.setAttribute("href",t),t=u.href),u.setAttribute("href",t),{href:u.href,protocol:u.protocol?u.protocol.replace(/:$/,""):"",host:u.host,search:u.search?u.search.replace(/^\?/,""):"",hash:u.hash?u.hash.replace(/^#/,""):"",hostname:u.hostname,port:u.port,pathname:"/"===u.pathname.charAt(0)?u.pathname:"/"+u.pathname}}var o,i=n(3),s=/(msie|trident)/i.test(navigator.userAgent),u=document.createElement("a");o=r(window.location.href),e.exports=function(e){var t=i.isString(e)?r(e):e;return t.protocol===o.protocol&&t.host===o.host}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(3);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])});`