github.com/shogo82148/goa-v1@v1.6.2/goagen/gen_js/generator.go (about)

     1  package genjs
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"sort"
     9  	"strings"
    10  	"text/template"
    11  	"time"
    12  
    13  	"github.com/shogo82148/goa-v1/design"
    14  	"github.com/shogo82148/goa-v1/goagen/codegen"
    15  	"github.com/shogo82148/goa-v1/goagen/utils"
    16  )
    17  
    18  //NewGenerator returns an initialized instance of a JavaScript Client Generator
    19  func NewGenerator(options ...Option) *Generator {
    20  	g := &Generator{}
    21  
    22  	for _, option := range options {
    23  		option(g)
    24  	}
    25  
    26  	return g
    27  }
    28  
    29  // Generator is the application code generator.
    30  type Generator struct {
    31  	API       *design.APIDefinition // The API definition
    32  	OutDir    string                // Destination directory
    33  	Timeout   time.Duration         // Timeout used by JavaScript client when making requests
    34  	Scheme    string                // Scheme used by JavaScript client
    35  	Host      string                // Host addressed by JavaScript client
    36  	NoExample bool                  // Do not generate an HTML example file
    37  	genfiles  []string              // Generated files
    38  }
    39  
    40  // Generate is the generator entry point called by the meta generator.
    41  func Generate() (files []string, err error) {
    42  	var (
    43  		outDir, ver  string
    44  		timeout      time.Duration
    45  		scheme, host string
    46  		noexample    bool
    47  	)
    48  
    49  	set := flag.NewFlagSet("client", flag.PanicOnError)
    50  	set.StringVar(&outDir, "out", "", "")
    51  	set.String("design", "", "")
    52  	set.DurationVar(&timeout, "timeout", time.Duration(20)*time.Second, "")
    53  	set.StringVar(&scheme, "scheme", "", "")
    54  	set.StringVar(&host, "host", "", "")
    55  	set.StringVar(&ver, "version", "", "")
    56  	set.BoolVar(&noexample, "noexample", false, "")
    57  	set.Parse(os.Args[1:])
    58  
    59  	// First check compatibility
    60  	if err := codegen.CheckVersion(ver); err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	// Now proceed
    65  	g := &Generator{OutDir: outDir, Timeout: timeout, Scheme: scheme, Host: host, NoExample: noexample, API: design.Design}
    66  
    67  	return g.Generate()
    68  }
    69  
    70  // Generate produces the skeleton main.
    71  func (g *Generator) Generate() (_ []string, err error) {
    72  	if g.API == nil {
    73  		return nil, fmt.Errorf("missing API definition, make sure design is properly initialized")
    74  	}
    75  
    76  	go utils.Catch(nil, func() { g.Cleanup() })
    77  
    78  	defer func() {
    79  		if err != nil {
    80  			g.Cleanup()
    81  		}
    82  	}()
    83  
    84  	if g.Timeout == 0 {
    85  		g.Timeout = 20 * time.Second
    86  	}
    87  	if g.Scheme == "" && len(g.API.Schemes) > 0 {
    88  		g.Scheme = g.API.Schemes[0]
    89  	}
    90  	if g.Scheme == "" {
    91  		g.Scheme = "http"
    92  	}
    93  	if g.Host == "" {
    94  		g.Host = g.API.Host
    95  	}
    96  	if g.Host == "" {
    97  		return nil, fmt.Errorf("missing host value, set it with --host")
    98  	}
    99  
   100  	g.OutDir = filepath.Join(g.OutDir, "js")
   101  	if err := os.RemoveAll(g.OutDir); err != nil {
   102  		return nil, err
   103  	}
   104  	if err := os.MkdirAll(g.OutDir, 0755); err != nil {
   105  		return nil, err
   106  	}
   107  	g.genfiles = append(g.genfiles, g.OutDir)
   108  
   109  	// Generate client.js
   110  	exampleAction, err := g.generateJS(filepath.Join(g.OutDir, "client.js"))
   111  	if err != nil {
   112  		return
   113  	}
   114  
   115  	// Generate axios.html
   116  	if err = g.generateAxiosJS(); err != nil {
   117  		return
   118  	}
   119  
   120  	if exampleAction != nil && !g.NoExample {
   121  		// Generate index.html
   122  		if err = g.generateIndexHTML(filepath.Join(g.OutDir, "index.html"), exampleAction); err != nil {
   123  			return
   124  		}
   125  
   126  		// Generate example
   127  		if err = g.generateExample(); err != nil {
   128  			return
   129  		}
   130  	}
   131  
   132  	return g.genfiles, nil
   133  }
   134  
   135  func (g *Generator) generateJS(jsFile string) (_ *design.ActionDefinition, err error) {
   136  	file, err := codegen.SourceFileFor(jsFile)
   137  	if err != nil {
   138  		return
   139  	}
   140  	defer file.Close()
   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  	defer file.Close()
   194  	g.genfiles = append(g.genfiles, htmlFile)
   195  
   196  	argNames := params(exampleAction)
   197  	var args string
   198  	if len(argNames) > 0 {
   199  		query := exampleAction.QueryParams.Type.ToObject()
   200  		argValues := make([]string, len(argNames))
   201  		for i, n := range argNames {
   202  			ex := query[n].GenerateExample(g.API.RandomGenerator(), nil)
   203  			argValues[i] = fmt.Sprintf("%v", ex)
   204  		}
   205  		args = strings.Join(argValues, ", ")
   206  	}
   207  	examplePath := exampleAction.Routes[0].FullPath()
   208  	pathParams := exampleAction.Routes[0].Params()
   209  	if len(pathParams) > 0 {
   210  		pathVars := exampleAction.AllParams().Type.ToObject()
   211  		pathValues := make([]interface{}, len(pathParams))
   212  		for i, n := range pathParams {
   213  			ex := pathVars[n].GenerateExample(g.API.RandomGenerator(), nil)
   214  			pathValues[i] = ex
   215  		}
   216  		format := design.WildcardRegex.ReplaceAllLiteralString(examplePath, "/%v")
   217  		examplePath = fmt.Sprintf(format, pathValues...)
   218  	}
   219  	if len(argNames) > 0 {
   220  		args = ", " + args
   221  	}
   222  	exampleFunc := fmt.Sprintf(
   223  		`%s%s ("%s"%s)`,
   224  		exampleAction.Name,
   225  		strings.Title(exampleAction.Parent.Name),
   226  		examplePath,
   227  		args,
   228  	)
   229  	data := map[string]interface{}{
   230  		"API":         g.API,
   231  		"ExampleFunc": exampleFunc,
   232  	}
   233  
   234  	return file.ExecuteTemplate("exampleHTML", exampleT, nil, data)
   235  }
   236  
   237  func (g *Generator) generateAxiosJS() error {
   238  	filePath := filepath.Join(g.OutDir, "axios.min.js")
   239  	if err := os.WriteFile(filePath, []byte(axios), 0644); err != nil {
   240  		return err
   241  	}
   242  	g.genfiles = append(g.genfiles, filePath)
   243  
   244  	return nil
   245  }
   246  
   247  func (g *Generator) generateExample() error {
   248  	controllerFile := filepath.Join(g.OutDir, "example.go")
   249  	file, err := codegen.SourceFileFor(controllerFile)
   250  	if err != nil {
   251  		return err
   252  	}
   253  	defer func() {
   254  		file.Close()
   255  		if err == nil {
   256  			err = file.FormatCode()
   257  		}
   258  	}()
   259  	imports := []*codegen.ImportSpec{
   260  		codegen.SimpleImport("net/http"),
   261  		codegen.SimpleImport("github.com/dimfeld/httptreemux"),
   262  		codegen.NewImport("goa", "github.com/shogo82148/goa-v1"),
   263  	}
   264  	if err := file.WriteHeader(fmt.Sprintf("%s JavaScript Client Example", g.API.Name), "js", imports); err != nil {
   265  		return err
   266  	}
   267  	g.genfiles = append(g.genfiles, controllerFile)
   268  
   269  	data := map[string]interface{}{"ServeDir": g.OutDir}
   270  	return file.ExecuteTemplate("examples", exampleCtrlT, nil, data)
   271  }
   272  
   273  // Cleanup removes all the files generated by this generator during the last invokation of Generate.
   274  func (g *Generator) Cleanup() {
   275  	for _, f := range g.genfiles {
   276  		os.Remove(f)
   277  	}
   278  	g.genfiles = nil
   279  }
   280  
   281  func params(action *design.ActionDefinition) []string {
   282  	if action.QueryParams == nil {
   283  		return nil
   284  	}
   285  	params := make([]string, len(action.QueryParams.Type.ToObject()))
   286  	i := 0
   287  	for n := range action.QueryParams.Type.ToObject() {
   288  		params[i] = n
   289  		i++
   290  	}
   291  	sort.Strings(params)
   292  	return params
   293  }
   294  
   295  const moduleT = `// This module exports functions that give access to the {{.API.Name}} API hosted at {{.API.Host}}.
   296  // It uses the axios javascript library for making the actual HTTP requests.
   297  define(['axios'] , function (axios) {
   298    function merge(obj1, obj2) {
   299      var obj3 = {};
   300      for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
   301      for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
   302      return obj3;
   303    }
   304  
   305    return function (scheme, host, timeout) {
   306      scheme = scheme || '{{.Scheme}}';
   307      host = host || '{{.Host}}';
   308      timeout = timeout || {{.Timeout}};
   309  
   310      // Client is the object returned by this module.
   311      var client = axios;
   312  
   313      // URL prefix for all API requests.
   314      var urlPrefix = scheme + '://' + host;
   315  `
   316  
   317  const moduleTend = `  return client;
   318    };
   319  });
   320  `
   321  
   322  const jsFuncsT = `{{$params := params .Action}}
   323    {{$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}}
   324    // path is the request path, the format is "{{(index .Action.Routes 0).FullPath}}"
   325    {{if .Action.Payload}}// data contains the action payload (request body)
   326    {{end}}{{if $params}}// {{join $params ", "}} {{if gt (len $params) 1}}are{{else}}is{{end}} used to build the request query string.
   327    {{end}}// config is an optional object to be merged into the config built by the function prior to making the request.
   328    // The content of the config object is described here: https://github.com/mzabriskie/axios#request-api
   329    // This function returns a promise which raises an error if the HTTP response is a 4xx or 5xx.
   330    client.{{$name}} = function (path{{if .Action.Payload}}, data{{end}}{{if $params}}, {{join $params ", "}}{{end}}, config) {
   331      var cfg = {
   332        timeout: timeout,
   333        url: urlPrefix + path,
   334        method: '{{toLower (index .Action.Routes 0).Verb}}',
   335  {{if $params}}      params: {
   336  {{range $index, $param := $params}}{{if $index}},
   337  {{end}}        {{$param}}: {{$param}}{{end}}
   338        },
   339  {{end}}{{if .Action.Payload}}    data: data,
   340  {{end}}      responseType: 'json'
   341      };
   342      if (config) {
   343        cfg = merge(cfg, config);
   344      }
   345      return client(cfg);
   346    }
   347  `
   348  
   349  const exampleT = `<!doctype html>
   350  <html>
   351    <head>
   352      <title>goa JavaScript client loader</title>
   353    </head>
   354    <body>
   355      <h1>{{.API.Name}} Client Test</h1>
   356      <div id="response"></div>
   357      <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.16/require.min.js"></script>
   358      <script>
   359        requirejs.config({
   360          paths: {
   361            axios: '/js/axios.min',
   362            client: '/js/client'
   363          }
   364        });
   365        requirejs(['client'], function (client) {
   366          client().{{.ExampleFunc}}
   367            .then(function (resp) {
   368              document.getElementById('response').innerHTML = resp.statusText;
   369            })
   370            .catch(function (resp) {
   371              document.getElementById('response').innerHTML = resp.statusText;
   372            });
   373        });
   374      </script>
   375    </body>
   376  </html>
   377  `
   378  
   379  const exampleCtrlT = `// MountController mounts the JavaScript example controller under "/js".
   380  // This is just an example, not the best way to do this. A better way would be to specify a file
   381  // server using the Files DSL in the design.
   382  // Use --noexample to prevent this file from being generated.
   383  func MountController(service *goa.Service) {
   384  	// Serve static files under js
   385  	service.ServeFiles("/js/*filepath", {{printf "%q" .ServeDir}})
   386  	service.LogInfo("mount", "ctrl", "JS", "action", "ServeFiles", "route", "GET /js/*")
   387  }
   388  `
   389  
   390  const axios = `/* axios v0.7.0 | (c) 2015 by Matt Zabriskie */
   391  !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)}}}])});`