diff --git a/.babelrc b/.babelrc index d2ad76e..13306a8 100644 --- a/.babelrc +++ b/.babelrc @@ -10,9 +10,12 @@ ] ], "plugins": [ - ["@babel/plugin-proposal-private-methods", { - "loose": true - }], + [ + "@babel/plugin-proposal-private-methods", + { + "loose": true + } + ], ["@babel/plugin-proposal-private-property-in-object", { "loose": true }], ["@babel/plugin-proposal-class-properties", { "loose": true }], "@babel/plugin-transform-classes" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21c330c..03bf1a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: ['16'] + node: ['20', '22'] name: Node ${{ matrix.node }} steps: - uses: actions/checkout@v2 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..c43d1fd --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +__tests__ +dist diff --git a/README.md b/README.md index 5e21d62..efaed1b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ # Hellotext.js -Track the events happening on your site to [Hellotext](https://www.hellotext.com) in real-time with this library. +Official [Hellotext](https://www.hellotext.com) (client-side) JavaScript library. -## Installation +This library allows you the following, + +- Track events happening on your site to [Hellotext](https://www.hellotext.com) in real-time. +- Use Hellotext Forms to dynamically collect data from your customers based on your specific business requirements. + + +## Installation ### Using NPM @@ -21,7 +27,7 @@ yarn add @hellotext/hellotext Import the library into your app. ```javascript -import Hellotext from "@hellotext/hellotext"; +import Hellotext from '@hellotext/hellotext' ``` Initialize the library passing the public `HELLOTEXT_BUSINESS_ID` identifier that represents the business. @@ -29,165 +35,17 @@ Initialize the library passing the public `HELLOTEXT_BUSINESS_ID` identifier tha You can find it from the business's settings page. ```javascript -Hellotext.initialize("HELLOTEXT_BUSINESS_ID"); +Hellotext.initialize('HELLOTEXT_BUSINESS_ID') ``` Failing to initialize the class before calling any other method will throw a `NotInitializedError`. -## Usage - -Tracking events is straightforward and perhaps the simplest example is tracking a page view: - -```javascript -Hellotext.track("page.viewed"); -``` - -In the example above only the name of the action is required. - -### Handling Responses - -The `track` method returns a Promise that can be `await`ed using the async/await syntax. Or using `.then` on the returned Promise. - -```javascript -const response = await Hellotext.track("page.viewed"); -``` - -The return of the `Hellotext.track` method is an instance of a `Response` object that ships with the package. You can check the status of the response via methods, like: - -```javascript -if(response.failed) { - console.log("failed because", response.data) -} - -if(response.succeeded) { - console.log("success") - console.log(response.data) // { status: "received" } -} -``` - -### Parameters - -The parameters passed to the action must be a valid set of parameters as described in -[Tracking Actions](https://www.hellotext.com/api#tracking). - -#### URL Parameter - -The library takes care of handling the `url` parameter with the current URL automatically and is not required to specify it explicitly. -If you want to provide another url, you can pass a `url` key in the params object when tracking an event. - -```javascript -Hellotext.track("page.viewed", { - url: "www.example.org" -}); -``` - -### Errors - -Failing to provide valid set of parameters will result in an error object being returned, describing the parameters that did not satisfy the rules. - -```javascript -const response = await Hellotext.track("app.installed", { app_parameters: { name: "My App" }}) - -console.log(response.data) -``` - -yields - -```javascript -{ - errors: [ - { - type: 'parameter_not_unique', - parameter: 'name', - description: 'The value must be unique and it is already present in another object of the same type.' - }, - ] -} -``` - -For a complete list of errors types. See [Error Types](https://www.hellotext.com/api#errors) - -### Associated objects - -Generally, most actions also require an associated object. These can be of type [`app`](https://www.hellotext.com/api#apps), [`coupon`](https://www.hellotext.com/api#coupons), [`form`](https://www.hellotext.com/api#forms), [`order`](https://www.hellotext.com/api#orders), [`product`](https://www.hellotext.com/api#products) and [`refund`](https://www.hellotext.com/api#refunds). -Aside from [Custom Actions](https://www.hellotext.com/api#create_an_action), which don't require the trackable to be present. - - -You can create the associated object directly by defining its parameters in a hash: - -```javascript -Hellotext.track("order.placed", { - amount: 395.00, - currency: "USD", - order_parameters: { - "amount": "395.00", - "reference": "654321", - } -}); -``` - -If you want to reuse existing objects, you must pass the identifier of an existing associated object. For example, to track a product purchase the identifier of a previously created product object as the `product`. -For more information about identifiers, view the [Tracking API](https://www.hellotext.com/api#tracking) - -```javascript -Hellotext.track("product.purchased", { - amount: 395.00, - currency: "USD", - product: "erA2RAXE" -}); -``` - -## List of actions - -The following is a complete list of built-in actions and their required associated objects. - -| Action | Description | Required Parameter | -|-----------------------|----------------------------------------------------------| --- | -| **app.installed** | An app was installed. | `app` or [app_parameters](https://www.hellotext.com/api#app) -| **app.removed** | An app was removed. | `app` or [app_parameters](https://www.hellotext.com/api#app) -| **app.spent** | A customer spent on an app. | `app` or [app_parameters](https://www.hellotext.com/api#app) -| **cart.abandoned** | A cart was abandoned. | `product` or [product_parameters](https://www.hellotext.com/api#products) -| **cart.added** | Added an item to the cart. | `product` or [product_parameters](https://www.hellotext.com/api#products) -| **cart.removed** | Removed an item from the cart. | `product` or [product_parameters](https://www.hellotext.com/api#products) -| **coupon.redeemed** | A coupon was redeem by a customer. | `coupon` or [coupon_parameters](https://www.hellotext.com/api#coupons) -| **form.completed** | A form was completed by the customer. | `form` or [form_parameters](https://www.hellotext.com/api#forms) -| **order.placed** | Order has been placed. | `order` or [order_parameters](https://www.hellotext.com/api#orders) -| **order.confirmed** | Order has been confirmed by you. | `order` or [order_parameters](https://www.hellotext.com/api#orders) -| **order.cancelled** | Order has been cancelled either by you or your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders) -| **order.shipped** | Order has been shipped to your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders) -| **order.delivered** | Order has been delivered to your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders) -| **page.viewed** | A page was viewed by a customer. | `url` -| **product.purchased** | A product has been purchased. | `product` or [product_parameters](https://www.hellotext.com/api#products) -| **product.viewed** | A product page has been viewed. | `product` or [product_parameters](https://www.hellotext.com/api#products) -| **refund.requested** | A customer requested a refund. | `refund` or [refund_parameters](https://www.hellotext.com/api#refunds) -| **refund.received** | A refund was issued by you to your customer. | `refund` or [refund_parameters](https://www.hellotext.com/api#refunds) - -You can also create your **[own defined actions](https://www.hellotext.com/api#actions)**. - -## Additional Properties - -You can include additional attributes to the tracked event, additional properties must be included inside the `metadata` object: - -```javascript -Hellotext.track("product.purchased", { - amount: 0.20, - currency: "USD", - metadata: { - myProperty: "custom" - }, - tracked_at: 1665684173 -}); -``` - -### List of additional attributes +## Documentation -| Property | Description | Type | Default | -| --- | --- | --- | --- | -| **amount** | Monetary amount that represents the revenue associated to this tracked event. | float | `0` -| **currency** | Currency for the `amount` given in ISO 4217 format. | currency | `USD` -| **metadata** | Set of key-value pairs that you can attach to an event. This can be useful for storing additional information about the object in a structured format. | hash | `{}` -| **tracked_at** | Original date when the event happened. This is useful if you want to record an event that happened in the past. If no value is provided its value will be the same from `created_at`. | epoch | `null` +Learn how to leverage the library to track events and collect forms. +- [Tracking Events](/docs/tracking.md) +- [Forms](/docs/forms.md) ## Events @@ -209,54 +67,56 @@ Hellotext.removeEventListener(eventName, callback) ### List of events - `session-set`: This event is fired when the session value for `Hellotext.session` is set. Either through an API request, or if the session was found in the cookie. +- `forms:collected` This event is fired when forms are collected. The callback will receive the array of forms collected. +- `form:completed` This event is fired when a form has been completed. A form is completed when the user fills all required inputs and verifies their OTP(One-Time Password). The callback will receive the form object that was completed, alongside the data the user filled in the form. ## Understanding Sessions -The library looks for a session identifier present on the `hellotext_session` query parameter. If the session is not present as a cookie neither it will create a new random session identifier, you can disable this default behaviour via the configuration, see [Configuration Options](#configuration-options) for more information. -The session is automatically sent to Hellotext any time the `Hellotext.track` method is called. +The library looks for a session identifier present on the `hellotext_session` query parameter. If the session is not present as a cookie neither it will create a new random session identifier, you can disable this default behaviour via the configuration, see [Configuration Options](#configuration-options) for more information. +The session is automatically sent to Hellotext any time the `Hellotext.track` method is called. Short links redirections attaches a session identifier to the destination url as `hellotext_session` query parameter. This will identify all the events back to the customer who opened the link. ### Get session -It is possible to obtain the current session by simply calling `Hellotext.session`. +It is possible to obtain the current session by simply calling `Hellotext.session`. ```javascript await Hellotext.session // Returns bBJn9vR15yPaYkWmR2QK0jopMeNxrA6l ``` -If the session has not been set yet, the result returned will be `undefined`. +If the session has not been set yet, the result returned will be `undefined`. You can check whether the session has been set or not by calling `Hellotext.isInitialized`. ```javascript -if(Hellotext.isInitialized) { - console.log("session is present") +if (Hellotext.isInitialized) { + console.log('session is present') } else { - console.log("session has not been set") + console.log('session has not been set') } ``` Moreover, you can hook in and listen for the session being set, such that when it's set, you're notified about the change, like so ```javascript -Hellotext.on("session-set", (session) => { - console.log("session is: ", session) +Hellotext.on('session-set', session => { + console.log('session is: ', session) }) ``` You may want to store the session on your backend when customers are unidentified so you can later [attach it to a profile](https://www.hellotext.com/api#attach_session) when it becomes known. -### Configuration +### Configuration -When initializing the library, you may pass an optional configuration object as the second argument. +When initializing the library, you may pass an optional configuration object as the second argument. ```javascript -Hellotext.initialize("HELLOTEXT_BUSINESS_ID", configurationOptions); +Hellotext.initialize('HELLOTEXT_BUSINESS_ID', configurationOptions) ``` #### Configuration Options | Property | Description | Type | Default | -|---------------------|------------------------------------------------------------------------------------------------------------------|---------| --- | -| autogenerateSession | Whether the library should automatically generate a session when no session is found in the query or the cookies | Boolean | true +| ------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | ------- | +| autogenerateSession | Whether the library should automatically generate a session when no session is found in the query or the cookies | Boolean | true | diff --git a/__tests__/api/sessions_test.js b/__tests__/api/sessions_test.js new file mode 100644 index 0000000..f170eae --- /dev/null +++ b/__tests__/api/sessions_test.js @@ -0,0 +1,12 @@ +import API from '../../src/api/sessions' + +beforeEach(() => { + global.fetch = jest.fn().mockResolvedValue({ json: jest.fn().mockResolvedValue({ id: 1 }) }) +}) + +describe('create', () => { + it('creates a session object', async () => { + const response = await new API('M01az53K').create() + expect(response.id).toEqual(1) + }) +}) diff --git a/__tests__/builders/input_builder_test.js b/__tests__/builders/input_builder_test.js new file mode 100644 index 0000000..3d75742 --- /dev/null +++ b/__tests__/builders/input_builder_test.js @@ -0,0 +1,117 @@ +/** + * @jest-environment jsdom + */ + +import Hellotext from '../../src/hellotext' +import { InputBuilder } from '../../src/builders/input_builder' + +describe('when the input is first_name', () => { + const data = { + label: 'First Name', + type: 'text', + required: true, + placeholder: 'Enter your first name', + kind: 'first_name', + } + + it('builds an article element that contains a label and input elements', () => { + const article = InputBuilder.build(data) + const label = article.querySelector('label') + const input = article.querySelector('input') + + expect(label.innerText).toEqual('First Name') + expect(label.getAttribute('for')).toEqual('first_name') + + expect(input.id).toEqual('first_name') + expect(input.type).toEqual('text') + expect(input.required).toEqual(true) + expect(input.placeholder).toEqual('Enter your first name') + expect(input.name).toEqual('first_name') + }) +}) + +describe('when the input is last_name', () => { + const data = { + label: 'Last Name', + type: 'text', + required: false, + placeholder: 'Enter your last name', + kind: 'last_name', + } + + it('builds an article element that contains a label and input elements', () => { + const article = InputBuilder.build(data) + const label = article.querySelector('label') + const input = article.querySelector('input') + + expect(label.innerText).toEqual('Last Name') + expect(label.getAttribute('for')).toEqual('last_name') + + expect(input.id).toEqual('last_name') + expect(input.type).toEqual('text') + expect(input.required).toEqual(false) + expect(input.placeholder).toEqual('Enter your last name') + expect(input.name).toEqual('last_name') + }) +}) + +describe('when the input belongs to a property', () => { + const data = { + label: 'Email', + type: 'email', + required: true, + placeholder: 'Enter your email', + kind: 'email', + property: 'xybz', + } + + it('builds an article element that contains a label and input elements', () => { + const article = InputBuilder.build(data) + const label = article.querySelector('label') + const input = article.querySelector('input') + + expect(label.innerText).toEqual('Email') + expect(label.getAttribute('for')).toEqual('email') + + expect(input.id).toEqual('email') + expect(input.type).toEqual('email') + expect(input.required).toEqual(true) + expect(input.placeholder).toEqual('Enter your email') + expect(input.name).toEqual('email') + }) +}) + +describe('when the input is a phone number', () => { + const data = { + label: 'Phone', + type: 'tel', + required: true, + placeholder: 'Enter your phone number', + kind: 'phone', + } + + beforeEach(() => { + Hellotext.business = { + country: { + prefix: '598', + code: 'UY', + } + } + }) + + it('builds an article element that contains a label and input elements', () => { + const article = InputBuilder.build(data) + const label = article.querySelector('label') + const input = article.querySelector('input') + + expect(label.innerText).toEqual('Phone') + expect(label.getAttribute('for')).toEqual('phone') + + expect(input.id).toEqual('phone') + expect(input.type).toEqual('tel') + expect(input.required).toEqual(true) + expect(input.placeholder).toEqual('Enter your phone number') + expect(input.name).toEqual('phone') + expect(input.value).toEqual('+598') + }) +}) diff --git a/__tests__/event_emitter_test.js b/__tests__/core/event_test.js similarity index 51% rename from __tests__/event_emitter_test.js rename to __tests__/core/event_test.js index 3634b3d..2b6d0ad 100644 --- a/__tests__/event_emitter_test.js +++ b/__tests__/core/event_test.js @@ -1,7 +1,27 @@ -import EventEmitter from "../src/eventEmitter" +import Event from "../../src/core/event" + +describe(".valid", function () { + it("is true when event name is a valid defined name", () => { + expect(Event.valid("session-set")).toEqual(true) + }); + + it("is false when event name is not defined", () => { + expect(Event.valid("undefined-event")).toEqual(false) + }); +}); + +describe(".invalid", () => { + it("is true when event name is not defined", () => { + expect(Event.invalid("undefined-event")).toEqual(true) + }); + + it("is false when event name is a valid defined name", () => { + expect(Event.invalid("session-set")).toEqual(false) + }); +}); describe("#addSubscriber", function () { - const instance = new EventEmitter() + const instance = new Event() it("adds the callback to the list of subscribers for an event", () => { const callback = (session) => {} @@ -11,7 +31,7 @@ describe("#addSubscriber", function () { }); describe("#removeSubscriber", function () { - const instance = new EventEmitter() + const instance = new Event() it("removes the callback from the list of subscribers for an event", () => { const callback = (session) => {} @@ -22,14 +42,14 @@ describe("#removeSubscriber", function () { }); }); -describe("#emit", () => { - const instance = new EventEmitter() +describe("#dispatch", () => { + const instance = new Event() it("notifies the listeners for an event", () => { const callback = jest.fn() instance.addSubscriber("session-set", callback) - instance.emit("session-set", "session_payload") + instance.dispatch("session-set", "session_payload") expect(callback).toHaveBeenCalledTimes(1) }); diff --git a/__tests__/event_test.js b/__tests__/event_test.js deleted file mode 100644 index 6e45b13..0000000 --- a/__tests__/event_test.js +++ /dev/null @@ -1,21 +0,0 @@ -import Event from "../src/event" - -describe(".valid", function () { - it("is true when event name is a valid defined name", () => { - expect(Event.valid("session-set")).toEqual(true) - }); - - it("is false when event name is not defined", () => { - expect(Event.valid("undefined-event")).toEqual(false) - }); -}); - -describe(".invalid", () => { - it("is true when event name is not defined", () => { - expect(Event.invalid("undefined-event")).toEqual(true) - }); - - it("is false when event name is a valid defined name", () => { - expect(Event.invalid("session-set")).toEqual(false) - }); -}); diff --git a/__tests__/hellotext_test.js b/__tests__/hellotext_test.js index 3fd9aaa..1f79f0d 100644 --- a/__tests__/hellotext_test.js +++ b/__tests__/hellotext_test.js @@ -3,6 +3,7 @@ */ import Hellotext from "../src/hellotext"; +import { Business } from "../src/models" const getCookieValue = name => document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() @@ -10,6 +11,10 @@ const expireSession = () => { document.cookie = "hello_session=;expires=Thu, 01 Jan 1970 00:00:00 GMT" } +beforeEach(() => { + Business.prototype.fetchPublicData = jest.fn().mockResolvedValue({ whitelist: 'disabled' }) +}) + afterEach(() => { jest.clearAllMocks(); }); @@ -204,3 +209,10 @@ describe("when hello_preview query parameter is present", () => { }); }) }) + +describe('setSession', () => { + it('sets the session', () => { + Hellotext.setSession("12345") + expect(Hellotext.session).toEqual("12345") + }) +}) diff --git a/__tests__/models/business_test.js b/__tests__/models/business_test.js new file mode 100644 index 0000000..9b2a8c1 --- /dev/null +++ b/__tests__/models/business_test.js @@ -0,0 +1,26 @@ +/** + * @jest-environment jsdom + */ + + +import { Business } from '../../src/models' + +describe('enabledWhitelist', () => { + beforeEach(() => { + global.fetch = jest.fn().mockResolvedValue({ json: jest.fn().mockResolvedValue({}) }) + }) + + it('is true when the whitelist is an array', () => { + const business = new Business('123') + business.data = { whitelist: ['www.example.com'] } + + expect(business.enabledWhitelist).toEqual(true) + }) + + it('is false when the whitelist is a string', () => { + const business = new Business('123') + business.data = { whitelist: 'disabled' } + + expect(business.enabledWhitelist).toEqual(false) + }) +}) diff --git a/__tests__/models/cookies_test.js b/__tests__/models/cookies_test.js new file mode 100644 index 0000000..04c2652 --- /dev/null +++ b/__tests__/models/cookies_test.js @@ -0,0 +1,25 @@ +/** + * @jest-environment jsdom + */ + +import { Cookies } from '../../src/models' + +beforeEach(() => { + document.cookie = '' +}) + +describe('set', () => { + it('sets the value of a cookie', () => { + expect(Cookies.get('hello_session')).toEqual(undefined) + + Cookies.set('hello_session', 'session') + expect(Cookies.get('hello_session')).toEqual('session') + }) +}) + +describe('get', () => { + it('gets the value of a cookie', () => { + document.cookie = 'hello_session=session' + expect(Cookies.get('hello_session')).toEqual('session') + }) +}) diff --git a/__tests__/models/form_collection_test.js b/__tests__/models/form_collection_test.js new file mode 100644 index 0000000..1ebb471 --- /dev/null +++ b/__tests__/models/form_collection_test.js @@ -0,0 +1,100 @@ +/** + * @jest-environment jsdom + */ + +import { Business, FormCollection } from '../../src/models' +import Hellotext from '../../src/hellotext' + +beforeEach(() => { + Business.prototype.fetchPublicData = jest.fn().mockResolvedValue({ whitelist: 'disabled' }) + + Hellotext.initialize('M01az53K', { + autogenerateSession: false + }) +}) + +describe('collect', () => { + it('does not fetch forms when there are no forms to fetch', async () => { + const fetch = jest.fn() + global.fetch = fetch + + await Hellotext.forms.collect() + + expect(fetch).not.toHaveBeenCalled() + }) + + it('fetches forms when there are forms to fetch', async () => { + global.fetch = jest.fn().mockResolvedValue({ json: jest.fn().mockResolvedValue({ id: 1 }) }) + + document.body.innerHTML = ` +
+ ` + + await Hellotext.forms.collect() + + expect(fetch).toHaveBeenCalledTimes(1) + }) +}) + +describe('add', () => { + const forms = new FormCollection() + const form = { id: 1, name: 'Form 1' } + + it('adds a form to the forms array', () => { + forms.add(form) + expect(forms.length).toEqual(1) + }) + + it('does not add a form that is already in the forms array', () => { + forms.add(form) + forms.add(form) + + expect(forms.length).toEqual(1) + }) +}) + +describe('getting elements', () => { + const forms = new FormCollection() + forms.add({ id: 1, name: 'Form 1' }) + forms.add({ id: 2, name: 'Form 1' }) + + describe('getById', () => { + it('returns the form with the given id', () => { + expect(forms.getById(1).id).toEqual(1) + }) + }) + + describe('getByIndex', () => { + it('returns the form at the given index', () => { + expect(forms.getByIndex(1).id).toEqual(2) + }) + }) +}) + +describe('includes', () => { + const forms = new FormCollection() + const form = { id: 1, name: 'Form 1' } + + it('is true when the form is in the forms array', () => { + forms.add(form) + expect(forms.includes(1)).toEqual(true) + }) + + it('is false when the form is not in the forms array', () => { + expect(forms.includes(2)).toEqual(false) + }) +}) + +describe('excludes', () => { + const forms = new FormCollection() + const form = { id: 1, name: 'Form 1' } + + it('is true when the form is not in the forms array', () => { + expect(forms.excludes(2)).toEqual(true) + }) + + it('is false when the form is in the forms array', () => { + forms.add(form) + expect(forms.excludes(1)).toEqual(false) + }) +}) diff --git a/__tests__/models/form_test.js b/__tests__/models/form_test.js new file mode 100644 index 0000000..f40a73e --- /dev/null +++ b/__tests__/models/form_test.js @@ -0,0 +1,29 @@ +/** + * @jest-environment jsdom + */ + +import Hellotext from '../../src/hellotext' +import { Form } from '../../src/models' + +describe('id', () => { + it('is the form id', () => { + const form = new Form({ id: 1 }) + expect(form.id).toEqual(1) + }) +}) + +describe('markAsCompleted', () => { + it('saves the form as completed in localStorage', () => { + const form = new Form({ id: 1 }) + form.markAsCompleted() + expect(localStorage.getItem('hello-form-1')).toEqual('completed') + }) + + it('emits a form:completed event', () => { + const form = new Form({ id: 1 }) + const emit = jest.spyOn(Hellotext.eventEmitter, 'dispatch') + + form.markAsCompleted() + expect(emit).toHaveBeenCalledWith('form:completed', { id: 1 }) + }) +}) diff --git a/__tests__/models/query_test.js b/__tests__/models/query_test.js new file mode 100644 index 0000000..08b07a3 --- /dev/null +++ b/__tests__/models/query_test.js @@ -0,0 +1,97 @@ +/** + * @jest-environment jsdom + */ + +import { Query } from '../../src/models' + +describe('get', () => { + beforeEach(() => { + const windowMock = { + location: { search: "?hello_session=session&hello_preview=1" }, + } + + jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock) + }) + + it('gets the value of a query parameter', () => { + const query = new Query() + + expect(query.get("session")).toEqual("session") + expect(query.get("preview")).toEqual("1") + }) +}) + +describe('has', () => { + beforeEach(() => { + const windowMock = { + location: { search: "?hello_session=session" }, + } + + jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock) + }) + + it('is true when the query parameter is present', () => { + const query = new Query() + expect(query.has("session")).toEqual(true) + }) + + it('is false when the query parameter is not present', () => { + const query = new Query() + expect(query.has("preview")).toEqual(false) + }) +}) + +describe('inPreviewMode', () => { + it('is true when the preview query parameter is present', () => { + const windowMock = { + location: { search: "?hello_preview" }, + } + + jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock) + + expect(Query.inPreviewMode).toEqual(true) + }) + + it('is false when the preview query parameter is not present', () => { + const windowMock = { + location: { search: "" }, + } + + jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock) + + expect(Query.inPreviewMode).toEqual(false) + }) +}) + +describe('session', () => { + it('gets the session from the query parameter when present in query', () => { + const windowMock = { + location: { search: "?hello_session=session" }, + } + + jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock) + + const query = new Query() + expect(query.session).toEqual("session") + }) + + it('gets the session from the cookie when not present in query', () => { + const windowMock = { + location: { search: "" }, + } + + jest.spyOn(global, 'window', 'get').mockImplementation(() => windowMock) + + document.cookie = "hello_session=session" + + const query = new Query() + expect(query.session).toEqual("session") + }) +}) + +describe("#toHellotextParameter", () => { + it("prefixes the argument with hello_", () => { + const query = new Query() + expect(query.toHellotextParam("preview")).toEqual("hello_preview") + }); +}) diff --git a/__tests__/query_test.js b/__tests__/query_test.js deleted file mode 100644 index 679d200..0000000 --- a/__tests__/query_test.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import Query from "../src/query"; - -describe("#toHellotextParameter", () => { - it("prefixes the argument with hello_", () => { - const query = new Query() - - expect(query.toHellotextParam("preview")).toEqual("hello_preview") - }); -}) diff --git a/dist/hellotext.js b/dist/hellotext.js index 3e71025..1286af5 100644 --- a/dist/hellotext.js +++ b/dist/hellotext.js @@ -1 +1 @@ -(()=>{"use strict";var e={413:(e,t)=>{function r(e){var t="function"==typeof Map?new Map:void 0;return r=function(e){if(null===e||(r=e,-1===Function.toString.call(r).indexOf("[native code]")))return e;var r;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,o)}function o(){return n(e,arguments,a(this).constructor)}return o.prototype=Object.create(e.prototype,{constructor:{value:o,enumerable:!1,writable:!0,configurable:!0}}),i(o,e)},r(e)}function n(e,t,r){return n=o()?Reflect.construct.bind():function(e,t,r){var n=[null];n.push.apply(n,t);var o=new(Function.bind.apply(e,n));return r&&i(o,r.prototype),o},n.apply(null,arguments)}function o(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function i(e,t){return i=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},i(e,t)}function a(e){return a=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},a(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.InvalidEvent=void 0;var s=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&i(e,t)}(u,e);var t,r,n,s=(r=u,n=o(),function(){var e,t=a(r);if(n){var o=a(this).constructor;e=Reflect.construct(t,arguments,o)}else e=t.apply(this,arguments);return function(e,t){if(t&&("object"==typeof t||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e)}(this,e)});function u(e){var t;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,u),(t=s.call(this,"".concat(e," is not valid. Please provide a valid event name"))).name="InvalidEvent",t}return t=u,Object.defineProperty(t,"prototype",{writable:!1}),t}(r(Error));t.InvalidEvent=s},215:(e,t)=>{function r(e){var t="function"==typeof Map?new Map:void 0;return r=function(e){if(null===e||(r=e,-1===Function.toString.call(r).indexOf("[native code]")))return e;var r;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,o)}function o(){return n(e,arguments,a(this).constructor)}return o.prototype=Object.create(e.prototype,{constructor:{value:o,enumerable:!1,writable:!0,configurable:!0}}),i(o,e)},r(e)}function n(e,t,r){return n=o()?Reflect.construct.bind():function(e,t,r){var n=[null];n.push.apply(n,t);var o=new(Function.bind.apply(e,n));return r&&i(o,r.prototype),o},n.apply(null,arguments)}function o(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function i(e,t){return i=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},i(e,t)}function a(e){return a=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},a(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.NotInitializedError=void 0;var s=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&i(e,t)}(u,e);var t,r,n,s=(r=u,n=o(),function(){var e,t=a(r);if(n){var o=a(this).constructor;e=Reflect.construct(t,arguments,o)}else e=t.apply(this,arguments);return function(e,t){if(t&&("object"==typeof t||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e)}(this,e)});function u(){var e;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,u),(e=s.call(this,"You need to initialize before tracking events. Call Hellotext.initialize and pass your public business id")).name="NotInitializedError",e}return t=u,Object.defineProperty(t,"prototype",{writable:!1}),t}(r(Error));t.NotInitializedError=s},372:(e,t)=>{function r(e,t){for(var r=0;rt===e))}}],null&&r(t.prototype,null),n&&r(t,n),Object.defineProperty(t,"prototype",{writable:!1}),e}();t.default=n,n.events=["session-set"]},179:(e,t)=>{function r(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function n(e){for(var t=1;te!==t)))}},{key:"emit",value:function(e,t){this.subscribers[e].forEach((e=>{e(t)}))}},{key:"listeners",get:function(){return 0!==Object.keys(this.subscribers).length}}])&&i(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();t.default=s},474:(e,t)=>{function r(e,t){for(var r=0;r{function r(e,t){for(var r=0;r{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e="undefined"!=typeof globalThis&&globalThis||"undefined"!=typeof self&&self||void 0!==e&&e,t={searchParams:"URLSearchParams"in e,iterable:"Symbol"in e&&"iterator"in Symbol,blob:"FileReader"in e&&"Blob"in e&&function(){try{return new Blob,!0}catch(e){return!1}}(),formData:"FormData"in e,arrayBuffer:"ArrayBuffer"in e};if(t.arrayBuffer)var r=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],n=ArrayBuffer.isView||function(e){return e&&r.indexOf(Object.prototype.toString.call(e))>-1};function o(e){if("string"!=typeof e&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(e)||""===e)throw new TypeError('Invalid character in header field name: "'+e+'"');return e.toLowerCase()}function i(e){return"string"!=typeof e&&(e=String(e)),e}function a(e){var r={next:function(){var t=e.shift();return{done:void 0===t,value:t}}};return t.iterable&&(r[Symbol.iterator]=function(){return r}),r}function s(e){this.map={},e instanceof s?e.forEach((function(e,t){this.append(t,e)}),this):Array.isArray(e)?e.forEach((function(e){this.append(e[0],e[1])}),this):e&&Object.getOwnPropertyNames(e).forEach((function(t){this.append(t,e[t])}),this)}function u(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function c(e){return new Promise((function(t,r){e.onload=function(){t(e.result)},e.onerror=function(){r(e.error)}}))}function f(e){var t=new FileReader,r=c(t);return t.readAsArrayBuffer(e),r}function l(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function p(){return this.bodyUsed=!1,this._initBody=function(e){var r;this.bodyUsed=this.bodyUsed,this._bodyInit=e,e?"string"==typeof e?this._bodyText=e:t.blob&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:t.formData&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:t.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():t.arrayBuffer&&t.blob&&(r=e)&&DataView.prototype.isPrototypeOf(r)?(this._bodyArrayBuffer=l(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):t.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(e)||n(e))?this._bodyArrayBuffer=l(e):this._bodyText=e=Object.prototype.toString.call(e):this._bodyText="",this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):t.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},t.blob&&(this.blob=function(){var e=u(this);if(e)return e;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?u(this)||(ArrayBuffer.isView(this._bodyArrayBuffer)?Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset,this._bodyArrayBuffer.byteOffset+this._bodyArrayBuffer.byteLength)):Promise.resolve(this._bodyArrayBuffer)):this.blob().then(f)}),this.text=function(){var e,t,r,n=u(this);if(n)return n;if(this._bodyBlob)return e=this._bodyBlob,r=c(t=new FileReader),t.readAsText(e),r;if(this._bodyArrayBuffer)return Promise.resolve(function(e){for(var t=new Uint8Array(e),r=new Array(t.length),n=0;n-1?n:r),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&o)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(o),!("GET"!==this.method&&"HEAD"!==this.method||"no-store"!==t.cache&&"no-cache"!==t.cache)){var i=/([?&])_=[^&]*/;i.test(this.url)?this.url=this.url.replace(i,"$1_="+(new Date).getTime()):this.url+=(/\?/.test(this.url)?"&":"?")+"_="+(new Date).getTime()}}function d(e){var t=new FormData;return e.trim().split("&").forEach((function(e){if(e){var r=e.split("="),n=r.shift().replace(/\+/g," "),o=r.join("=").replace(/\+/g," ");t.append(decodeURIComponent(n),decodeURIComponent(o))}})),t}function b(e,t){if(!(this instanceof b))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');t||(t={}),this.type="default",this.status=void 0===t.status?200:t.status,this.ok=this.status>=200&&this.status<300,this.statusText=void 0===t.statusText?"":""+t.statusText,this.headers=new s(t.headers),this.url=t.url||"",this._initBody(e)}y.prototype.clone=function(){return new y(this,{body:this._bodyInit})},p.call(y.prototype),p.call(b.prototype),b.prototype.clone=function(){return new b(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new s(this.headers),url:this.url})},b.error=function(){var e=new b(null,{status:0,statusText:""});return e.type="error",e};var v=[301,302,303,307,308];b.redirect=function(e,t){if(-1===v.indexOf(t))throw new RangeError("Invalid status code");return new b(null,{status:t,headers:{location:e}})};var w=e.DOMException;try{new w}catch(e){(w=function(e,t){this.message=e,this.name=t;var r=Error(e);this.stack=r.stack}).prototype=Object.create(Error.prototype),w.prototype.constructor=w}function m(r,n){return new Promise((function(o,a){var u=new y(r,n);if(u.signal&&u.signal.aborted)return a(new w("Aborted","AbortError"));var c=new XMLHttpRequest;function f(){c.abort()}c.onload=function(){var e,t,r={status:c.status,statusText:c.statusText,headers:(e=c.getAllResponseHeaders()||"",t=new s,e.replace(/\r?\n[\t ]+/g," ").split("\r").map((function(e){return 0===e.indexOf("\n")?e.substr(1,e.length):e})).forEach((function(e){var r=e.split(":"),n=r.shift().trim();if(n){var o=r.join(":").trim();t.append(n,o)}})),t)};r.url="responseURL"in c?c.responseURL:r.headers.get("X-Request-URL");var n="response"in c?c.response:c.responseText;setTimeout((function(){o(new b(n,r))}),0)},c.onerror=function(){setTimeout((function(){a(new TypeError("Network request failed"))}),0)},c.ontimeout=function(){setTimeout((function(){a(new TypeError("Network request failed"))}),0)},c.onabort=function(){setTimeout((function(){a(new w("Aborted","AbortError"))}),0)},c.open(u.method,function(t){try{return""===t&&e.location.href?e.location.href:t}catch(e){return t}}(u.url),!0),"include"===u.credentials?c.withCredentials=!0:"omit"===u.credentials&&(c.withCredentials=!1),"responseType"in c&&(t.blob?c.responseType="blob":t.arrayBuffer&&u.headers.get("Content-Type")&&-1!==u.headers.get("Content-Type").indexOf("application/octet-stream")&&(c.responseType="arraybuffer")),!n||"object"!=typeof n.headers||n.headers instanceof s?u.headers.forEach((function(e,t){c.setRequestHeader(t,e)})):Object.getOwnPropertyNames(n.headers).forEach((function(e){c.setRequestHeader(e,i(n.headers[e]))})),u.signal&&(u.signal.addEventListener("abort",f),c.onreadystatechange=function(){4===c.readyState&&u.signal.removeEventListener("abort",f)}),c.send(void 0===u._bodyInit?null:u._bodyInit)}))}m.polyfill=!0,e.fetch||(e.fetch=m,e.Headers=s,e.Request=y,e.Response=b)})(),(()=>{var e=s(r(372)),t=s(r(179)),n=s(r(310)),o=s(r(474)),i=r(215),a=r(413);function s(e){return e&&e.__esModule?e:{default:e}}function u(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function c(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{autogenerateSession:!0};if(d(this,m)[m]=e,d(this,O)[O]=t,d(this,P)[P]=new o.default(window.location.search),!d(this,P)[P].has("preview")){var r=d(this,P)[P].get("session")||d(this,S)[S];r&&"undefined"!==r&&"null"!==r?(d(this,w)[w]=r,d(this,T)[T]()):t.autogenerateSession&&d(this,_)[_]().then((e=>{d(this,w)[w]=e.id,d(this,T)[T]()}))}}},{key:"track",value:(u=p((function*(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(d(this,j)[j])throw new i.NotInitializedError;if(d(this,P)[P].has("preview"))return new n.default(!0,{received:!0});var r=yield fetch(this.__apiURL+"track/events",{headers:d(this,E)[E],method:"post",body:JSON.stringify(c(c({session:this.session,action:e},t),{},{url:t&&t.url||window.location.href}))});return new n.default(200===r.status,yield r.json())})),function(e){return u.apply(this,arguments)})},{key:"on",value:function(t,r){if(e.default.invalid(t))throw new a.InvalidEvent(t);d(this,g)[g].addSubscriber(t,r)}},{key:"removeEventListener",value:function(t,r){if(e.default.invalid(t))throw new a.InvalidEvent(t);d(this,g)[g].removeSubscriber(t,r)}},{key:"session",get:function(){if(d(this,j)[j])throw new i.NotInitializedError;return d(this,w)[w]}},{key:"isInitialized",get:function(){return void 0!==d(this,w)[w]}}],s&&h(r,s),Object.defineProperty(r,"prototype",{writable:!1}),t}();function x(){return(x=p((function*(){if(d(this,j)[j])throw new i.NotInitializedError;var e=this.__apiURL+"track/sessions";return this.mintingPromise=yield fetch(e,{method:"post",headers:{Authorization:"Bearer ".concat(d(this,m)[m])}}),this.mintingPromise.json()}))).apply(this,arguments)}Object.defineProperty(A,S,{get:function(){var e;return null===(e=document.cookie.match("(^|;)\\s*hello_session\\s*=\\s*([^;]+)"))||void 0===e?void 0:e.pop()},set:void 0}),Object.defineProperty(A,T,{value:function(){if(d(this,j)[j])throw new i.NotInitializedError;d(this,g)[g].listeners&&d(this,g)[g].emit("session-set",d(this,w)[w]),document.cookie="hello_session=".concat(d(this,w)[w])}}),Object.defineProperty(A,E,{get:function(){if(d(this,j)[j])throw new i.NotInitializedError;return{Authorization:"Bearer ".concat(d(this,m)[m]),Accept:"application.json","Content-Type":"application/json"}},set:void 0}),Object.defineProperty(A,_,{value:function(){return x.apply(this,arguments)}}),Object.defineProperty(A,j,{get:function(){return void 0===d(this,m)[m]},set:void 0}),A.__apiURL="https://api.hellotext.com/v1/",Object.defineProperty(A,w,{writable:!0,value:void 0}),Object.defineProperty(A,m,{writable:!0,value:void 0}),Object.defineProperty(A,O,{writable:!0,value:void 0}),Object.defineProperty(A,g,{writable:!0,value:new t.default}),Object.defineProperty(A,P,{writable:!0,value:void 0})})()})(); \ No newline at end of file +(()=>{"use strict";var e={599:(e,t,r)=>{r.r(t),r.d(t,{Application:()=>X,AttributeObserver:()=>w,Context:()=>I,Controller:()=>ue,ElementObserver:()=>g,IndexedMultimap:()=>A,Multimap:()=>k,SelectorObserver:()=>C,StringMapObserver:()=>T,TokenListObserver:()=>M,ValueListObserver:()=>_,add:()=>O,defaultSchema:()=>J,del:()=>E,fetch:()=>P,prune:()=>j});class n{constructor(e,t,r){this.eventTarget=e,this.eventName=t,this.eventOptions=r,this.unorderedBindings=new Set}connect(){this.eventTarget.addEventListener(this.eventName,this,this.eventOptions)}disconnect(){this.eventTarget.removeEventListener(this.eventName,this,this.eventOptions)}bindingConnected(e){this.unorderedBindings.add(e)}bindingDisconnected(e){this.unorderedBindings.delete(e)}handleEvent(e){const t=function(e){if("immediatePropagationStopped"in e)return e;{const{stopImmediatePropagation:t}=e;return Object.assign(e,{immediatePropagationStopped:!1,stopImmediatePropagation(){this.immediatePropagationStopped=!0,t.call(this)}})}}(e);for(const e of this.bindings){if(t.immediatePropagationStopped)break;e.handleEvent(t)}}hasBindings(){return this.unorderedBindings.size>0}get bindings(){return Array.from(this.unorderedBindings).sort(((e,t)=>{const r=e.index,n=t.index;return rn?1:0}))}}class i{constructor(e){this.application=e,this.eventListenerMaps=new Map,this.started=!1}start(){this.started||(this.started=!0,this.eventListeners.forEach((e=>e.connect())))}stop(){this.started&&(this.started=!1,this.eventListeners.forEach((e=>e.disconnect())))}get eventListeners(){return Array.from(this.eventListenerMaps.values()).reduce(((e,t)=>e.concat(Array.from(t.values()))),[])}bindingConnected(e){this.fetchEventListenerForBinding(e).bindingConnected(e)}bindingDisconnected(e,t=!1){this.fetchEventListenerForBinding(e).bindingDisconnected(e),t&&this.clearEventListenersForBinding(e)}handleError(e,t,r={}){this.application.handleError(e,`Error ${t}`,r)}clearEventListenersForBinding(e){const t=this.fetchEventListenerForBinding(e);t.hasBindings()||(t.disconnect(),this.removeMappedEventListenerFor(e))}removeMappedEventListenerFor(e){const{eventTarget:t,eventName:r,eventOptions:n}=e,i=this.fetchEventListenerMapForEventTarget(t),o=this.cacheKey(r,n);i.delete(o),0==i.size&&this.eventListenerMaps.delete(t)}fetchEventListenerForBinding(e){const{eventTarget:t,eventName:r,eventOptions:n}=e;return this.fetchEventListener(t,r,n)}fetchEventListener(e,t,r){const n=this.fetchEventListenerMapForEventTarget(e),i=this.cacheKey(t,r);let o=n.get(i);return o||(o=this.createEventListener(e,t,r),n.set(i,o)),o}createEventListener(e,t,r){const i=new n(e,t,r);return this.started&&i.connect(),i}fetchEventListenerMapForEventTarget(e){let t=this.eventListenerMaps.get(e);return t||(t=new Map,this.eventListenerMaps.set(e,t)),t}cacheKey(e,t){const r=[e];return Object.keys(t).sort().forEach((e=>{r.push(`${t[e]?"":"!"}${e}`)})),r.join(":")}}const o={stop:({event:e,value:t})=>(t&&e.stopPropagation(),!0),prevent:({event:e,value:t})=>(t&&e.preventDefault(),!0),self:({event:e,value:t,element:r})=>!t||r===e.target},s=/^(?:(?:([^.]+?)\+)?(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;function a(e){return e.replace(/(?:[_-])([a-z0-9])/g,((e,t)=>t.toUpperCase()))}function u(e){return a(e.replace(/--/g,"-").replace(/__/g,"_"))}function c(e){return e.charAt(0).toUpperCase()+e.slice(1)}function l(e){return e.replace(/([A-Z])/g,((e,t)=>`-${t.toLowerCase()}`))}function h(e){return null!=e}function d(e,t){return Object.prototype.hasOwnProperty.call(e,t)}const f=["meta","ctrl","alt","shift"];class p{constructor(e,t,r,n){this.element=e,this.index=t,this.eventTarget=r.eventTarget||e,this.eventName=r.eventName||function(e){const t=e.tagName.toLowerCase();if(t in m)return m[t](e)}(e)||y("missing event name"),this.eventOptions=r.eventOptions||{},this.identifier=r.identifier||y("missing identifier"),this.methodName=r.methodName||y("missing method name"),this.keyFilter=r.keyFilter||"",this.schema=n}static forToken(e,t){return new this(e.element,e.index,function(e){const t=e.trim().match(s)||[];let r=t[2],n=t[3];return n&&!["keydown","keyup","keypress"].includes(r)&&(r+=`.${n}`,n=""),{eventTarget:(i=t[4],"window"==i?window:"document"==i?document:void 0),eventName:r,eventOptions:t[7]?(o=t[7],o.split(":").reduce(((e,t)=>Object.assign(e,{[t.replace(/^!/,"")]:!/^!/.test(t)})),{})):{},identifier:t[5],methodName:t[6],keyFilter:t[1]||n};var i,o}(e.content),t)}toString(){const e=this.keyFilter?`.${this.keyFilter}`:"",t=this.eventTargetName?`@${this.eventTargetName}`:"";return`${this.eventName}${e}${t}->${this.identifier}#${this.methodName}`}shouldIgnoreKeyboardEvent(e){if(!this.keyFilter)return!1;const t=this.keyFilter.split("+");if(this.keyFilterDissatisfied(e,t))return!0;const r=t.filter((e=>!f.includes(e)))[0];return!!r&&(d(this.keyMappings,r)||y(`contains unknown key filter: ${this.keyFilter}`),this.keyMappings[r].toLowerCase()!==e.key.toLowerCase())}shouldIgnoreMouseEvent(e){if(!this.keyFilter)return!1;const t=[this.keyFilter];return!!this.keyFilterDissatisfied(e,t)}get params(){const e={},t=new RegExp(`^data-${this.identifier}-(.+)-param$`,"i");for(const{name:r,value:n}of Array.from(this.element.attributes)){const i=r.match(t),o=i&&i[1];o&&(e[a(o)]=v(n))}return e}get eventTargetName(){return(e=this.eventTarget)==window?"window":e==document?"document":void 0;var e}get keyMappings(){return this.schema.keyMappings}keyFilterDissatisfied(e,t){const[r,n,i,o]=f.map((e=>t.includes(e)));return e.metaKey!==r||e.ctrlKey!==n||e.altKey!==i||e.shiftKey!==o}}const m={a:()=>"click",button:()=>"click",form:()=>"submit",details:()=>"toggle",input:e=>"submit"==e.getAttribute("type")?"click":"input",select:()=>"change",textarea:()=>"input"};function y(e){throw new Error(e)}function v(e){try{return JSON.parse(e)}catch(t){return e}}class b{constructor(e,t){this.context=e,this.action=t}get index(){return this.action.index}get eventTarget(){return this.action.eventTarget}get eventOptions(){return this.action.eventOptions}get identifier(){return this.context.identifier}handleEvent(e){const t=this.prepareActionEvent(e);this.willBeInvokedByEvent(e)&&this.applyEventModifiers(t)&&this.invokeWithEvent(t)}get eventName(){return this.action.eventName}get method(){const e=this.controller[this.methodName];if("function"==typeof e)return e;throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`)}applyEventModifiers(e){const{element:t}=this.action,{actionDescriptorFilters:r}=this.context.application,{controller:n}=this.context;let i=!0;for(const[o,s]of Object.entries(this.eventOptions))if(o in r){const a=r[o];i=i&&a({name:o,value:s,event:e,element:t,controller:n})}return i}prepareActionEvent(e){return Object.assign(e,{params:this.action.params})}invokeWithEvent(e){const{target:t,currentTarget:r}=e;try{this.method.call(this.controller,e),this.context.logDebugActivity(this.methodName,{event:e,target:t,currentTarget:r,action:this.methodName})}catch(t){const{identifier:r,controller:n,element:i,index:o}=this,s={identifier:r,controller:n,element:i,index:o,event:e};this.context.handleError(t,`invoking action "${this.action}"`,s)}}willBeInvokedByEvent(e){const t=e.target;return!(e instanceof KeyboardEvent&&this.action.shouldIgnoreKeyboardEvent(e))&&!(e instanceof MouseEvent&&this.action.shouldIgnoreMouseEvent(e))&&(this.element===t||(t instanceof Element&&this.element.contains(t)?this.scope.containsElement(t):this.scope.containsElement(this.action.element)))}get controller(){return this.context.controller}get methodName(){return this.action.methodName}get element(){return this.scope.element}get scope(){return this.context.scope}}class g{constructor(e,t){this.mutationObserverInit={attributes:!0,childList:!0,subtree:!0},this.element=e,this.started=!1,this.delegate=t,this.elements=new Set,this.mutationObserver=new MutationObserver((e=>this.processMutations(e)))}start(){this.started||(this.started=!0,this.mutationObserver.observe(this.element,this.mutationObserverInit),this.refresh())}pause(e){this.started&&(this.mutationObserver.disconnect(),this.started=!1),e(),this.started||(this.mutationObserver.observe(this.element,this.mutationObserverInit),this.started=!0)}stop(){this.started&&(this.mutationObserver.takeRecords(),this.mutationObserver.disconnect(),this.started=!1)}refresh(){if(this.started){const e=new Set(this.matchElementsInTree());for(const t of Array.from(this.elements))e.has(t)||this.removeElement(t);for(const t of Array.from(e))this.addElement(t)}}processMutations(e){if(this.started)for(const t of e)this.processMutation(t)}processMutation(e){"attributes"==e.type?this.processAttributeChange(e.target,e.attributeName):"childList"==e.type&&(this.processRemovedNodes(e.removedNodes),this.processAddedNodes(e.addedNodes))}processAttributeChange(e,t){this.elements.has(e)?this.delegate.elementAttributeChanged&&this.matchElement(e)?this.delegate.elementAttributeChanged(e,t):this.removeElement(e):this.matchElement(e)&&this.addElement(e)}processRemovedNodes(e){for(const t of Array.from(e)){const e=this.elementFromNode(t);e&&this.processTree(e,this.removeElement)}}processAddedNodes(e){for(const t of Array.from(e)){const e=this.elementFromNode(t);e&&this.elementIsActive(e)&&this.processTree(e,this.addElement)}}matchElement(e){return this.delegate.matchElement(e)}matchElementsInTree(e=this.element){return this.delegate.matchElementsInTree(e)}processTree(e,t){for(const r of this.matchElementsInTree(e))t.call(this,r)}elementFromNode(e){if(e.nodeType==Node.ELEMENT_NODE)return e}elementIsActive(e){return e.isConnected==this.element.isConnected&&this.element.contains(e)}addElement(e){this.elements.has(e)||this.elementIsActive(e)&&(this.elements.add(e),this.delegate.elementMatched&&this.delegate.elementMatched(e))}removeElement(e){this.elements.has(e)&&(this.elements.delete(e),this.delegate.elementUnmatched&&this.delegate.elementUnmatched(e))}}class w{constructor(e,t,r){this.attributeName=t,this.delegate=r,this.elementObserver=new g(e,this)}get element(){return this.elementObserver.element}get selector(){return`[${this.attributeName}]`}start(){this.elementObserver.start()}pause(e){this.elementObserver.pause(e)}stop(){this.elementObserver.stop()}refresh(){this.elementObserver.refresh()}get started(){return this.elementObserver.started}matchElement(e){return e.hasAttribute(this.attributeName)}matchElementsInTree(e){const t=this.matchElement(e)?[e]:[],r=Array.from(e.querySelectorAll(this.selector));return t.concat(r)}elementMatched(e){this.delegate.elementMatchedAttribute&&this.delegate.elementMatchedAttribute(e,this.attributeName)}elementUnmatched(e){this.delegate.elementUnmatchedAttribute&&this.delegate.elementUnmatchedAttribute(e,this.attributeName)}elementAttributeChanged(e,t){this.delegate.elementAttributeValueChanged&&this.attributeName==t&&this.delegate.elementAttributeValueChanged(e,t)}}function O(e,t,r){P(e,t).add(r)}function E(e,t,r){P(e,t).delete(r),j(e,t)}function P(e,t){let r=e.get(t);return r||(r=new Set,e.set(t,r)),r}function j(e,t){const r=e.get(t);null!=r&&0==r.size&&e.delete(t)}class k{constructor(){this.valuesByKey=new Map}get keys(){return Array.from(this.valuesByKey.keys())}get values(){return Array.from(this.valuesByKey.values()).reduce(((e,t)=>e.concat(Array.from(t))),[])}get size(){return Array.from(this.valuesByKey.values()).reduce(((e,t)=>e+t.size),0)}add(e,t){O(this.valuesByKey,e,t)}delete(e,t){E(this.valuesByKey,e,t)}has(e,t){const r=this.valuesByKey.get(e);return null!=r&&r.has(t)}hasKey(e){return this.valuesByKey.has(e)}hasValue(e){return Array.from(this.valuesByKey.values()).some((t=>t.has(e)))}getValuesForKey(e){const t=this.valuesByKey.get(e);return t?Array.from(t):[]}getKeysForValue(e){return Array.from(this.valuesByKey).filter((([t,r])=>r.has(e))).map((([e,t])=>e))}}class A extends k{constructor(){super(),this.keysByValue=new Map}get values(){return Array.from(this.keysByValue.keys())}add(e,t){super.add(e,t),O(this.keysByValue,t,e)}delete(e,t){super.delete(e,t),E(this.keysByValue,t,e)}hasValue(e){return this.keysByValue.has(e)}getKeysForValue(e){const t=this.keysByValue.get(e);return t?Array.from(t):[]}}class C{constructor(e,t,r,n){this._selector=t,this.details=n,this.elementObserver=new g(e,this),this.delegate=r,this.matchesByElement=new k}get started(){return this.elementObserver.started}get selector(){return this._selector}set selector(e){this._selector=e,this.refresh()}start(){this.elementObserver.start()}pause(e){this.elementObserver.pause(e)}stop(){this.elementObserver.stop()}refresh(){this.elementObserver.refresh()}get element(){return this.elementObserver.element}matchElement(e){const{selector:t}=this;if(t){const r=e.matches(t);return this.delegate.selectorMatchElement?r&&this.delegate.selectorMatchElement(e,this.details):r}return!1}matchElementsInTree(e){const{selector:t}=this;if(t){const r=this.matchElement(e)?[e]:[],n=Array.from(e.querySelectorAll(t)).filter((e=>this.matchElement(e)));return r.concat(n)}return[]}elementMatched(e){const{selector:t}=this;t&&this.selectorMatched(e,t)}elementUnmatched(e){const t=this.matchesByElement.getKeysForValue(e);for(const r of t)this.selectorUnmatched(e,r)}elementAttributeChanged(e,t){const{selector:r}=this;if(r){const t=this.matchElement(e),n=this.matchesByElement.has(r,e);t&&!n?this.selectorMatched(e,r):!t&&n&&this.selectorUnmatched(e,r)}}selectorMatched(e,t){this.delegate.selectorMatched(e,t,this.details),this.matchesByElement.add(t,e)}selectorUnmatched(e,t){this.delegate.selectorUnmatched(e,t,this.details),this.matchesByElement.delete(t,e)}}class T{constructor(e,t){this.element=e,this.delegate=t,this.started=!1,this.stringMap=new Map,this.mutationObserver=new MutationObserver((e=>this.processMutations(e)))}start(){this.started||(this.started=!0,this.mutationObserver.observe(this.element,{attributes:!0,attributeOldValue:!0}),this.refresh())}stop(){this.started&&(this.mutationObserver.takeRecords(),this.mutationObserver.disconnect(),this.started=!1)}refresh(){if(this.started)for(const e of this.knownAttributeNames)this.refreshAttribute(e,null)}processMutations(e){if(this.started)for(const t of e)this.processMutation(t)}processMutation(e){const t=e.attributeName;t&&this.refreshAttribute(t,e.oldValue)}refreshAttribute(e,t){const r=this.delegate.getStringMapKeyForAttribute(e);if(null!=r){this.stringMap.has(e)||this.stringMapKeyAdded(r,e);const n=this.element.getAttribute(e);if(this.stringMap.get(e)!=n&&this.stringMapValueChanged(n,r,t),null==n){const t=this.stringMap.get(e);this.stringMap.delete(e),t&&this.stringMapKeyRemoved(r,e,t)}else this.stringMap.set(e,n)}}stringMapKeyAdded(e,t){this.delegate.stringMapKeyAdded&&this.delegate.stringMapKeyAdded(e,t)}stringMapValueChanged(e,t,r){this.delegate.stringMapValueChanged&&this.delegate.stringMapValueChanged(e,t,r)}stringMapKeyRemoved(e,t,r){this.delegate.stringMapKeyRemoved&&this.delegate.stringMapKeyRemoved(e,t,r)}get knownAttributeNames(){return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)))}get currentAttributeNames(){return Array.from(this.element.attributes).map((e=>e.name))}get recordedAttributeNames(){return Array.from(this.stringMap.keys())}}class M{constructor(e,t,r){this.attributeObserver=new w(e,t,this),this.delegate=r,this.tokensByElement=new k}get started(){return this.attributeObserver.started}start(){this.attributeObserver.start()}pause(e){this.attributeObserver.pause(e)}stop(){this.attributeObserver.stop()}refresh(){this.attributeObserver.refresh()}get element(){return this.attributeObserver.element}get attributeName(){return this.attributeObserver.attributeName}elementMatchedAttribute(e){this.tokensMatched(this.readTokensForElement(e))}elementAttributeValueChanged(e){const[t,r]=this.refreshTokensForElement(e);this.tokensUnmatched(t),this.tokensMatched(r)}elementUnmatchedAttribute(e){this.tokensUnmatched(this.tokensByElement.getValuesForKey(e))}tokensMatched(e){e.forEach((e=>this.tokenMatched(e)))}tokensUnmatched(e){e.forEach((e=>this.tokenUnmatched(e)))}tokenMatched(e){this.delegate.tokenMatched(e),this.tokensByElement.add(e.element,e)}tokenUnmatched(e){this.delegate.tokenUnmatched(e),this.tokensByElement.delete(e.element,e)}refreshTokensForElement(e){const t=this.tokensByElement.getValuesForKey(e),r=this.readTokensForElement(e),n=function(e,t){const r=Math.max(e.length,t.length);return Array.from({length:r},((r,n)=>[e[n],t[n]]))}(t,r).findIndex((([e,t])=>{return n=t,!((r=e)&&n&&r.index==n.index&&r.content==n.content);var r,n}));return-1==n?[[],[]]:[t.slice(n),r.slice(n)]}readTokensForElement(e){const t=this.attributeName;return function(e,t,r){return e.trim().split(/\s+/).filter((e=>e.length)).map(((e,n)=>({element:t,attributeName:r,content:e,index:n})))}(e.getAttribute(t)||"",e,t)}}class _{constructor(e,t,r){this.tokenListObserver=new M(e,t,this),this.delegate=r,this.parseResultsByToken=new WeakMap,this.valuesByTokenByElement=new WeakMap}get started(){return this.tokenListObserver.started}start(){this.tokenListObserver.start()}stop(){this.tokenListObserver.stop()}refresh(){this.tokenListObserver.refresh()}get element(){return this.tokenListObserver.element}get attributeName(){return this.tokenListObserver.attributeName}tokenMatched(e){const{element:t}=e,{value:r}=this.fetchParseResultForToken(e);r&&(this.fetchValuesByTokenForElement(t).set(e,r),this.delegate.elementMatchedValue(t,r))}tokenUnmatched(e){const{element:t}=e,{value:r}=this.fetchParseResultForToken(e);r&&(this.fetchValuesByTokenForElement(t).delete(e),this.delegate.elementUnmatchedValue(t,r))}fetchParseResultForToken(e){let t=this.parseResultsByToken.get(e);return t||(t=this.parseToken(e),this.parseResultsByToken.set(e,t)),t}fetchValuesByTokenForElement(e){let t=this.valuesByTokenByElement.get(e);return t||(t=new Map,this.valuesByTokenByElement.set(e,t)),t}parseToken(e){try{return{value:this.delegate.parseValueForToken(e)}}catch(e){return{error:e}}}}class S{constructor(e,t){this.context=e,this.delegate=t,this.bindingsByAction=new Map}start(){this.valueListObserver||(this.valueListObserver=new _(this.element,this.actionAttribute,this),this.valueListObserver.start())}stop(){this.valueListObserver&&(this.valueListObserver.stop(),delete this.valueListObserver,this.disconnectAllActions())}get element(){return this.context.element}get identifier(){return this.context.identifier}get actionAttribute(){return this.schema.actionAttribute}get schema(){return this.context.schema}get bindings(){return Array.from(this.bindingsByAction.values())}connectAction(e){const t=new b(this.context,e);this.bindingsByAction.set(e,t),this.delegate.bindingConnected(t)}disconnectAction(e){const t=this.bindingsByAction.get(e);t&&(this.bindingsByAction.delete(e),this.delegate.bindingDisconnected(t))}disconnectAllActions(){this.bindings.forEach((e=>this.delegate.bindingDisconnected(e,!0))),this.bindingsByAction.clear()}parseValueForToken(e){const t=p.forToken(e,this.schema);if(t.identifier==this.identifier)return t}elementMatchedValue(e,t){this.connectAction(t)}elementUnmatchedValue(e,t){this.disconnectAction(t)}}class x{constructor(e,t){this.context=e,this.receiver=t,this.stringMapObserver=new T(this.element,this),this.valueDescriptorMap=this.controller.valueDescriptorMap}start(){this.stringMapObserver.start(),this.invokeChangedCallbacksForDefaultValues()}stop(){this.stringMapObserver.stop()}get element(){return this.context.element}get controller(){return this.context.controller}getStringMapKeyForAttribute(e){if(e in this.valueDescriptorMap)return this.valueDescriptorMap[e].name}stringMapKeyAdded(e,t){const r=this.valueDescriptorMap[t];this.hasValue(e)||this.invokeChangedCallback(e,r.writer(this.receiver[e]),r.writer(r.defaultValue))}stringMapValueChanged(e,t,r){const n=this.valueDescriptorNameMap[t];null!==e&&(null===r&&(r=n.writer(n.defaultValue)),this.invokeChangedCallback(t,e,r))}stringMapKeyRemoved(e,t,r){const n=this.valueDescriptorNameMap[e];this.hasValue(e)?this.invokeChangedCallback(e,n.writer(this.receiver[e]),r):this.invokeChangedCallback(e,n.writer(n.defaultValue),r)}invokeChangedCallbacksForDefaultValues(){for(const{key:e,name:t,defaultValue:r,writer:n}of this.valueDescriptors)null==r||this.controller.data.has(e)||this.invokeChangedCallback(t,n(r),void 0)}invokeChangedCallback(e,t,r){const n=`${e}Changed`,i=this.receiver[n];if("function"==typeof i){const n=this.valueDescriptorNameMap[e];try{const e=n.reader(t);let o=r;r&&(o=n.reader(r)),i.call(this.receiver,e,o)}catch(e){throw e instanceof TypeError&&(e.message=`Stimulus Value "${this.context.identifier}.${n.name}" - ${e.message}`),e}}}get valueDescriptors(){const{valueDescriptorMap:e}=this;return Object.keys(e).map((t=>e[t]))}get valueDescriptorNameMap(){const e={};return Object.keys(this.valueDescriptorMap).forEach((t=>{const r=this.valueDescriptorMap[t];e[r.name]=r})),e}hasValue(e){const t=`has${c(this.valueDescriptorNameMap[e].name)}`;return this.receiver[t]}}class B{constructor(e,t){this.context=e,this.delegate=t,this.targetsByName=new k}start(){this.tokenListObserver||(this.tokenListObserver=new M(this.element,this.attributeName,this),this.tokenListObserver.start())}stop(){this.tokenListObserver&&(this.disconnectAllTargets(),this.tokenListObserver.stop(),delete this.tokenListObserver)}tokenMatched({element:e,content:t}){this.scope.containsElement(e)&&this.connectTarget(e,t)}tokenUnmatched({element:e,content:t}){this.disconnectTarget(e,t)}connectTarget(e,t){var r;this.targetsByName.has(t,e)||(this.targetsByName.add(t,e),null===(r=this.tokenListObserver)||void 0===r||r.pause((()=>this.delegate.targetConnected(e,t))))}disconnectTarget(e,t){var r;this.targetsByName.has(t,e)&&(this.targetsByName.delete(t,e),null===(r=this.tokenListObserver)||void 0===r||r.pause((()=>this.delegate.targetDisconnected(e,t))))}disconnectAllTargets(){for(const e of this.targetsByName.keys)for(const t of this.targetsByName.getValuesForKey(e))this.disconnectTarget(t,e)}get attributeName(){return`data-${this.context.identifier}-target`}get element(){return this.context.element}get scope(){return this.context.scope}}function F(e,t){const r=L(e);return Array.from(r.reduce(((e,r)=>(function(e,t){const r=e[t];return Array.isArray(r)?r:[]}(r,t).forEach((t=>e.add(t))),e)),new Set))}function L(e){const t=[];for(;e;)t.push(e),e=Object.getPrototypeOf(e);return t.reverse()}class N{constructor(e,t){this.started=!1,this.context=e,this.delegate=t,this.outletsByName=new k,this.outletElementsByName=new k,this.selectorObserverMap=new Map,this.attributeObserverMap=new Map}start(){this.started||(this.outletDefinitions.forEach((e=>{this.setupSelectorObserverForOutlet(e),this.setupAttributeObserverForOutlet(e)})),this.started=!0,this.dependentContexts.forEach((e=>e.refresh())))}refresh(){this.selectorObserverMap.forEach((e=>e.refresh())),this.attributeObserverMap.forEach((e=>e.refresh()))}stop(){this.started&&(this.started=!1,this.disconnectAllOutlets(),this.stopSelectorObservers(),this.stopAttributeObservers())}stopSelectorObservers(){this.selectorObserverMap.size>0&&(this.selectorObserverMap.forEach((e=>e.stop())),this.selectorObserverMap.clear())}stopAttributeObservers(){this.attributeObserverMap.size>0&&(this.attributeObserverMap.forEach((e=>e.stop())),this.attributeObserverMap.clear())}selectorMatched(e,t,{outletName:r}){const n=this.getOutlet(e,r);n&&this.connectOutlet(n,e,r)}selectorUnmatched(e,t,{outletName:r}){const n=this.getOutletFromMap(e,r);n&&this.disconnectOutlet(n,e,r)}selectorMatchElement(e,{outletName:t}){const r=this.selector(t),n=this.hasOutlet(e,t),i=e.matches(`[${this.schema.controllerAttribute}~=${t}]`);return!!r&&n&&i&&e.matches(r)}elementMatchedAttribute(e,t){const r=this.getOutletNameFromOutletAttributeName(t);r&&this.updateSelectorObserverForOutlet(r)}elementAttributeValueChanged(e,t){const r=this.getOutletNameFromOutletAttributeName(t);r&&this.updateSelectorObserverForOutlet(r)}elementUnmatchedAttribute(e,t){const r=this.getOutletNameFromOutletAttributeName(t);r&&this.updateSelectorObserverForOutlet(r)}connectOutlet(e,t,r){var n;this.outletElementsByName.has(r,t)||(this.outletsByName.add(r,e),this.outletElementsByName.add(r,t),null===(n=this.selectorObserverMap.get(r))||void 0===n||n.pause((()=>this.delegate.outletConnected(e,t,r))))}disconnectOutlet(e,t,r){var n;this.outletElementsByName.has(r,t)&&(this.outletsByName.delete(r,e),this.outletElementsByName.delete(r,t),null===(n=this.selectorObserverMap.get(r))||void 0===n||n.pause((()=>this.delegate.outletDisconnected(e,t,r))))}disconnectAllOutlets(){for(const e of this.outletElementsByName.keys)for(const t of this.outletElementsByName.getValuesForKey(e))for(const r of this.outletsByName.getValuesForKey(e))this.disconnectOutlet(r,t,e)}updateSelectorObserverForOutlet(e){const t=this.selectorObserverMap.get(e);t&&(t.selector=this.selector(e))}setupSelectorObserverForOutlet(e){const t=this.selector(e),r=new C(document.body,t,this,{outletName:e});this.selectorObserverMap.set(e,r),r.start()}setupAttributeObserverForOutlet(e){const t=this.attributeNameForOutletName(e),r=new w(this.scope.element,t,this);this.attributeObserverMap.set(e,r),r.start()}selector(e){return this.scope.outlets.getSelectorForOutletName(e)}attributeNameForOutletName(e){return this.scope.schema.outletAttributeForScope(this.identifier,e)}getOutletNameFromOutletAttributeName(e){return this.outletDefinitions.find((t=>this.attributeNameForOutletName(t)===e))}get outletDependencies(){const e=new k;return this.router.modules.forEach((t=>{F(t.definition.controllerConstructor,"outlets").forEach((r=>e.add(r,t.identifier)))})),e}get outletDefinitions(){return this.outletDependencies.getKeysForValue(this.identifier)}get dependentControllerIdentifiers(){return this.outletDependencies.getValuesForKey(this.identifier)}get dependentContexts(){const e=this.dependentControllerIdentifiers;return this.router.contexts.filter((t=>e.includes(t.identifier)))}hasOutlet(e,t){return!!this.getOutlet(e,t)||!!this.getOutletFromMap(e,t)}getOutlet(e,t){return this.application.getControllerForElementAndIdentifier(e,t)}getOutletFromMap(e,t){return this.outletsByName.getValuesForKey(t).find((t=>t.element===e))}get scope(){return this.context.scope}get schema(){return this.context.schema}get identifier(){return this.context.identifier}get application(){return this.context.application}get router(){return this.application.router}}class I{constructor(e,t){this.logDebugActivity=(e,t={})=>{const{identifier:r,controller:n,element:i}=this;t=Object.assign({identifier:r,controller:n,element:i},t),this.application.logDebugActivity(this.identifier,e,t)},this.module=e,this.scope=t,this.controller=new e.controllerConstructor(this),this.bindingObserver=new S(this,this.dispatcher),this.valueObserver=new x(this,this.controller),this.targetObserver=new B(this,this),this.outletObserver=new N(this,this);try{this.controller.initialize(),this.logDebugActivity("initialize")}catch(e){this.handleError(e,"initializing controller")}}connect(){this.bindingObserver.start(),this.valueObserver.start(),this.targetObserver.start(),this.outletObserver.start();try{this.controller.connect(),this.logDebugActivity("connect")}catch(e){this.handleError(e,"connecting controller")}}refresh(){this.outletObserver.refresh()}disconnect(){try{this.controller.disconnect(),this.logDebugActivity("disconnect")}catch(e){this.handleError(e,"disconnecting controller")}this.outletObserver.stop(),this.targetObserver.stop(),this.valueObserver.stop(),this.bindingObserver.stop()}get application(){return this.module.application}get identifier(){return this.module.identifier}get schema(){return this.application.schema}get dispatcher(){return this.application.dispatcher}get element(){return this.scope.element}get parentElement(){return this.element.parentElement}handleError(e,t,r={}){const{identifier:n,controller:i,element:o}=this;r=Object.assign({identifier:n,controller:i,element:o},r),this.application.handleError(e,`Error ${t}`,r)}targetConnected(e,t){this.invokeControllerMethod(`${t}TargetConnected`,e)}targetDisconnected(e,t){this.invokeControllerMethod(`${t}TargetDisconnected`,e)}outletConnected(e,t,r){this.invokeControllerMethod(`${u(r)}OutletConnected`,e,t)}outletDisconnected(e,t,r){this.invokeControllerMethod(`${u(r)}OutletDisconnected`,e,t)}invokeControllerMethod(e,...t){const r=this.controller;"function"==typeof r[e]&&r[e](...t)}}const D="function"==typeof Object.getOwnPropertySymbols?e=>[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)]:Object.getOwnPropertyNames,$=(()=>{function e(e){function t(){return Reflect.construct(e,arguments,new.target)}return t.prototype=Object.create(e.prototype,{constructor:{value:t}}),Reflect.setPrototypeOf(t,e),t}try{return function(){const t=e((function(){this.a.call(this)}));t.prototype.a=function(){},new t}(),e}catch(e){return e=>class extends e{}}})();class R{constructor(e,t){this.application=e,this.definition=function(e){return{identifier:e.identifier,controllerConstructor:(t=e.controllerConstructor,function(e,t){const r=$(e),n=function(e,t){return D(t).reduce(((r,n)=>{const i=function(e,t,r){const n=Object.getOwnPropertyDescriptor(e,r);if(!n||!("value"in n)){const e=Object.getOwnPropertyDescriptor(t,r).value;return n&&(e.get=n.get||e.get,e.set=n.set||e.set),e}}(e,t,n);return i&&Object.assign(r,{[n]:i}),r}),{})}(e.prototype,t);return Object.defineProperties(r.prototype,n),r}(t,function(e){return F(e,"blessings").reduce(((t,r)=>{const n=r(e);for(const e in n){const r=t[e]||{};t[e]=Object.assign(r,n[e])}return t}),{})}(t)))};var t}(t),this.contextsByScope=new WeakMap,this.connectedContexts=new Set}get identifier(){return this.definition.identifier}get controllerConstructor(){return this.definition.controllerConstructor}get contexts(){return Array.from(this.connectedContexts)}connectContextForScope(e){const t=this.fetchContextForScope(e);this.connectedContexts.add(t),t.connect()}disconnectContextForScope(e){const t=this.contextsByScope.get(e);t&&(this.connectedContexts.delete(t),t.disconnect())}fetchContextForScope(e){let t=this.contextsByScope.get(e);return t||(t=new I(this,e),this.contextsByScope.set(e,t)),t}}class V{constructor(e){this.scope=e}has(e){return this.data.has(this.getDataKey(e))}get(e){return this.getAll(e)[0]}getAll(e){return(this.data.get(this.getDataKey(e))||"").match(/[^\s]+/g)||[]}getAttributeName(e){return this.data.getAttributeNameForKey(this.getDataKey(e))}getDataKey(e){return`${e}-class`}get data(){return this.scope.data}}class K{constructor(e){this.scope=e}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get(e){const t=this.getAttributeNameForKey(e);return this.element.getAttribute(t)}set(e,t){const r=this.getAttributeNameForKey(e);return this.element.setAttribute(r,t),this.get(e)}has(e){const t=this.getAttributeNameForKey(e);return this.element.hasAttribute(t)}delete(e){if(this.has(e)){const t=this.getAttributeNameForKey(e);return this.element.removeAttribute(t),!0}return!1}getAttributeNameForKey(e){return`data-${this.identifier}-${l(e)}`}}class U{constructor(e){this.warnedKeysByObject=new WeakMap,this.logger=e}warn(e,t,r){let n=this.warnedKeysByObject.get(e);n||(n=new Set,this.warnedKeysByObject.set(e,n)),n.has(t)||(n.add(t),this.logger.warn(r,e))}}function q(e,t){return`[${e}~="${t}"]`}class z{constructor(e){this.scope=e}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get schema(){return this.scope.schema}has(e){return null!=this.find(e)}find(...e){return e.reduce(((e,t)=>e||this.findTarget(t)||this.findLegacyTarget(t)),void 0)}findAll(...e){return e.reduce(((e,t)=>[...e,...this.findAllTargets(t),...this.findAllLegacyTargets(t)]),[])}findTarget(e){const t=this.getSelectorForTargetName(e);return this.scope.findElement(t)}findAllTargets(e){const t=this.getSelectorForTargetName(e);return this.scope.findAllElements(t)}getSelectorForTargetName(e){return q(this.schema.targetAttributeForScope(this.identifier),e)}findLegacyTarget(e){const t=this.getLegacySelectorForTargetName(e);return this.deprecate(this.scope.findElement(t),e)}findAllLegacyTargets(e){const t=this.getLegacySelectorForTargetName(e);return this.scope.findAllElements(t).map((t=>this.deprecate(t,e)))}getLegacySelectorForTargetName(e){const t=`${this.identifier}.${e}`;return q(this.schema.targetAttribute,t)}deprecate(e,t){if(e){const{identifier:r}=this,n=this.schema.targetAttribute,i=this.schema.targetAttributeForScope(r);this.guide.warn(e,`target:${t}`,`Please replace ${n}="${r}.${t}" with ${i}="${t}". The ${n} attribute is deprecated and will be removed in a future version of Stimulus.`)}return e}get guide(){return this.scope.guide}}class H{constructor(e,t){this.scope=e,this.controllerElement=t}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get schema(){return this.scope.schema}has(e){return null!=this.find(e)}find(...e){return e.reduce(((e,t)=>e||this.findOutlet(t)),void 0)}findAll(...e){return e.reduce(((e,t)=>[...e,...this.findAllOutlets(t)]),[])}getSelectorForOutletName(e){const t=this.schema.outletAttributeForScope(this.identifier,e);return this.controllerElement.getAttribute(t)}findOutlet(e){const t=this.getSelectorForOutletName(e);if(t)return this.findElement(t,e)}findAllOutlets(e){const t=this.getSelectorForOutletName(e);return t?this.findAllElements(t,e):[]}findElement(e,t){return this.scope.queryElements(e).filter((r=>this.matchesElement(r,e,t)))[0]}findAllElements(e,t){return this.scope.queryElements(e).filter((r=>this.matchesElement(r,e,t)))}matchesElement(e,t,r){const n=e.getAttribute(this.scope.schema.controllerAttribute)||"";return e.matches(t)&&n.split(" ").includes(r)}}class Z{constructor(e,t,r,n){this.targets=new z(this),this.classes=new V(this),this.data=new K(this),this.containsElement=e=>e.closest(this.controllerSelector)===this.element,this.schema=e,this.element=t,this.identifier=r,this.guide=new U(n),this.outlets=new H(this.documentScope,t)}findElement(e){return this.element.matches(e)?this.element:this.queryElements(e).find(this.containsElement)}findAllElements(e){return[...this.element.matches(e)?[this.element]:[],...this.queryElements(e).filter(this.containsElement)]}queryElements(e){return Array.from(this.element.querySelectorAll(e))}get controllerSelector(){return q(this.schema.controllerAttribute,this.identifier)}get isDocumentScope(){return this.element===document.documentElement}get documentScope(){return this.isDocumentScope?this:new Z(this.schema,document.documentElement,this.identifier,this.guide.logger)}}class W{constructor(e,t,r){this.element=e,this.schema=t,this.delegate=r,this.valueListObserver=new _(this.element,this.controllerAttribute,this),this.scopesByIdentifierByElement=new WeakMap,this.scopeReferenceCounts=new WeakMap}start(){this.valueListObserver.start()}stop(){this.valueListObserver.stop()}get controllerAttribute(){return this.schema.controllerAttribute}parseValueForToken(e){const{element:t,content:r}=e;return this.parseValueForElementAndIdentifier(t,r)}parseValueForElementAndIdentifier(e,t){const r=this.fetchScopesByIdentifierForElement(e);let n=r.get(t);return n||(n=this.delegate.createScopeForElementAndIdentifier(e,t),r.set(t,n)),n}elementMatchedValue(e,t){const r=(this.scopeReferenceCounts.get(t)||0)+1;this.scopeReferenceCounts.set(t,r),1==r&&this.delegate.scopeConnected(t)}elementUnmatchedValue(e,t){const r=this.scopeReferenceCounts.get(t);r&&(this.scopeReferenceCounts.set(t,r-1),1==r&&this.delegate.scopeDisconnected(t))}fetchScopesByIdentifierForElement(e){let t=this.scopesByIdentifierByElement.get(e);return t||(t=new Map,this.scopesByIdentifierByElement.set(e,t)),t}}class G{constructor(e){this.application=e,this.scopeObserver=new W(this.element,this.schema,this),this.scopesByIdentifier=new k,this.modulesByIdentifier=new Map}get element(){return this.application.element}get schema(){return this.application.schema}get logger(){return this.application.logger}get controllerAttribute(){return this.schema.controllerAttribute}get modules(){return Array.from(this.modulesByIdentifier.values())}get contexts(){return this.modules.reduce(((e,t)=>e.concat(t.contexts)),[])}start(){this.scopeObserver.start()}stop(){this.scopeObserver.stop()}loadDefinition(e){this.unloadIdentifier(e.identifier);const t=new R(this.application,e);this.connectModule(t);const r=e.controllerConstructor.afterLoad;r&&r.call(e.controllerConstructor,e.identifier,this.application)}unloadIdentifier(e){const t=this.modulesByIdentifier.get(e);t&&this.disconnectModule(t)}getContextForElementAndIdentifier(e,t){const r=this.modulesByIdentifier.get(t);if(r)return r.contexts.find((t=>t.element==e))}proposeToConnectScopeForElementAndIdentifier(e,t){const r=this.scopeObserver.parseValueForElementAndIdentifier(e,t);r?this.scopeObserver.elementMatchedValue(r.element,r):console.error(`Couldn't find or create scope for identifier: "${t}" and element:`,e)}handleError(e,t,r){this.application.handleError(e,t,r)}createScopeForElementAndIdentifier(e,t){return new Z(this.schema,e,t,this.logger)}scopeConnected(e){this.scopesByIdentifier.add(e.identifier,e);const t=this.modulesByIdentifier.get(e.identifier);t&&t.connectContextForScope(e)}scopeDisconnected(e){this.scopesByIdentifier.delete(e.identifier,e);const t=this.modulesByIdentifier.get(e.identifier);t&&t.disconnectContextForScope(e)}connectModule(e){this.modulesByIdentifier.set(e.identifier,e),this.scopesByIdentifier.getValuesForKey(e.identifier).forEach((t=>e.connectContextForScope(t)))}disconnectModule(e){this.modulesByIdentifier.delete(e.identifier),this.scopesByIdentifier.getValuesForKey(e.identifier).forEach((t=>e.disconnectContextForScope(t)))}}const J={controllerAttribute:"data-controller",actionAttribute:"data-action",targetAttribute:"data-target",targetAttributeForScope:e=>`data-${e}-target`,outletAttributeForScope:(e,t)=>`data-${e}-${t}-outlet`,keyMappings:Object.assign(Object.assign({enter:"Enter",tab:"Tab",esc:"Escape",space:" ",up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",page_up:"PageUp",page_down:"PageDown"},Q("abcdefghijklmnopqrstuvwxyz".split("").map((e=>[e,e])))),Q("0123456789".split("").map((e=>[e,e]))))};function Q(e){return e.reduce(((e,[t,r])=>Object.assign(Object.assign({},e),{[t]:r})),{})}class X{constructor(e=document.documentElement,t=J){this.logger=console,this.debug=!1,this.logDebugActivity=(e,t,r={})=>{this.debug&&this.logFormattedMessage(e,t,r)},this.element=e,this.schema=t,this.dispatcher=new i(this),this.router=new G(this),this.actionDescriptorFilters=Object.assign({},o)}static start(e,t){const r=new this(e,t);return r.start(),r}async start(){await new Promise((e=>{"loading"==document.readyState?document.addEventListener("DOMContentLoaded",(()=>e())):e()})),this.logDebugActivity("application","starting"),this.dispatcher.start(),this.router.start(),this.logDebugActivity("application","start")}stop(){this.logDebugActivity("application","stopping"),this.dispatcher.stop(),this.router.stop(),this.logDebugActivity("application","stop")}register(e,t){this.load({identifier:e,controllerConstructor:t})}registerActionOption(e,t){this.actionDescriptorFilters[e]=t}load(e,...t){(Array.isArray(e)?e:[e,...t]).forEach((e=>{e.controllerConstructor.shouldLoad&&this.router.loadDefinition(e)}))}unload(e,...t){(Array.isArray(e)?e:[e,...t]).forEach((e=>this.router.unloadIdentifier(e)))}get controllers(){return this.router.contexts.map((e=>e.controller))}getControllerForElementAndIdentifier(e,t){const r=this.router.getContextForElementAndIdentifier(e,t);return r?r.controller:null}handleError(e,t,r){var n;this.logger.error("%s\n\n%o\n\n%o",t,e,r),null===(n=window.onerror)||void 0===n||n.call(window,t,"",0,0,e)}logFormattedMessage(e,t,r={}){r=Object.assign({application:this},r),this.logger.groupCollapsed(`${e} #${t}`),this.logger.log("details:",Object.assign({},r)),this.logger.groupEnd()}}function Y(e,t,r){return e.application.getControllerForElementAndIdentifier(t,r)}function ee(e,t,r){let n=Y(e,t,r);return n||(e.application.router.proposeToConnectScopeForElementAndIdentifier(t,r),n=Y(e,t,r),n||void 0)}function te([e,t],r){return function(e){const{token:t,typeDefinition:r}=e,n=`${l(t)}-value`,i=function(e){const{controller:t,token:r,typeDefinition:n}=e,i=function(e){const{controller:t,token:r,typeObject:n}=e,i=h(n.type),o=h(n.default),s=i&&o,a=i&&!o,u=!i&&o,c=re(n.type),l=ne(e.typeObject.default);if(a)return c;if(u)return l;if(c!==l)throw new Error(`The specified default value for the Stimulus Value "${t?`${t}.${r}`:r}" must match the defined type "${c}". The provided default value of "${n.default}" is of type "${l}".`);return s?c:void 0}({controller:t,token:r,typeObject:n}),o=ne(n),s=re(n),a=i||o||s;if(a)return a;throw new Error(`Unknown value type "${t?`${t}.${n}`:r}" for "${r}" value`)}(e);return{type:i,key:n,name:a(n),get defaultValue(){return function(e){const t=re(e);if(t)return ie[t];const r=d(e,"default"),n=d(e,"type"),i=e;if(r)return i.default;if(n){const{type:e}=i,t=re(e);if(t)return ie[t]}return e}(r)},get hasCustomDefaultValue(){return void 0!==ne(r)},reader:oe[i],writer:se[i]||se.default}}({controller:r,token:e,typeDefinition:t})}function re(e){switch(e){case Array:return"array";case Boolean:return"boolean";case Number:return"number";case Object:return"object";case String:return"string"}}function ne(e){switch(typeof e){case"boolean":return"boolean";case"number":return"number";case"string":return"string"}return Array.isArray(e)?"array":"[object Object]"===Object.prototype.toString.call(e)?"object":void 0}const ie={get array(){return[]},boolean:!1,number:0,get object(){return{}},string:""},oe={array(e){const t=JSON.parse(e);if(!Array.isArray(t))throw new TypeError(`expected value of type "array" but instead got value "${e}" of type "${ne(t)}"`);return t},boolean:e=>!("0"==e||"false"==String(e).toLowerCase()),number:e=>Number(e.replace(/_/g,"")),object(e){const t=JSON.parse(e);if(null===t||"object"!=typeof t||Array.isArray(t))throw new TypeError(`expected value of type "object" but instead got value "${e}" of type "${ne(t)}"`);return t},string:e=>e},se={default:function(e){return`${e}`},array:ae,object:ae};function ae(e){return JSON.stringify(e)}class ue{constructor(e){this.context=e}static get shouldLoad(){return!0}static afterLoad(e,t){}get application(){return this.context.application}get scope(){return this.context.scope}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get targets(){return this.scope.targets}get outlets(){return this.scope.outlets}get classes(){return this.scope.classes}get data(){return this.scope.data}initialize(){}connect(){}disconnect(){}dispatch(e,{target:t=this.element,detail:r={},prefix:n=this.identifier,bubbles:i=!0,cancelable:o=!0}={}){const s=new CustomEvent(n?`${n}:${e}`:e,{detail:r,bubbles:i,cancelable:o});return t.dispatchEvent(s),s}}ue.blessings=[function(e){return F(e,"classes").reduce(((e,t)=>{return Object.assign(e,{[`${r=t}Class`]:{get(){const{classes:e}=this;if(e.has(r))return e.get(r);{const t=e.getAttributeName(r);throw new Error(`Missing attribute "${t}"`)}}},[`${r}Classes`]:{get(){return this.classes.getAll(r)}},[`has${c(r)}Class`]:{get(){return this.classes.has(r)}}});var r}),{})},function(e){return F(e,"targets").reduce(((e,t)=>{return Object.assign(e,{[`${r=t}Target`]:{get(){const e=this.targets.find(r);if(e)return e;throw new Error(`Missing target element "${r}" for "${this.identifier}" controller`)}},[`${r}Targets`]:{get(){return this.targets.findAll(r)}},[`has${c(r)}Target`]:{get(){return this.targets.has(r)}}});var r}),{})},function(e){const t=function(e,t){return L(e).reduce(((e,r)=>(e.push(...function(e,t){const r=e[t];return r?Object.keys(r).map((e=>[e,r[e]])):[]}(r,t)),e)),[])}(e,"values"),r={valueDescriptorMap:{get(){return t.reduce(((e,t)=>{const r=te(t,this.identifier),n=this.data.getAttributeNameForKey(r.key);return Object.assign(e,{[n]:r})}),{})}}};return t.reduce(((e,t)=>Object.assign(e,function(e,t){const r=te(e,void 0),{key:n,name:i,reader:o,writer:s}=r;return{[i]:{get(){const e=this.data.get(n);return null!==e?o(e):r.defaultValue},set(e){void 0===e?this.data.delete(n):this.data.set(n,s(e))}},[`has${c(i)}`]:{get(){return this.data.has(n)||r.hasCustomDefaultValue}}}}(t))),r)},function(e){return F(e,"outlets").reduce(((e,t)=>Object.assign(e,function(e){const t=u(e);return{[`${t}Outlet`]:{get(){const t=this.outlets.find(e),r=this.outlets.getSelectorForOutletName(e);if(t){const r=ee(this,t,e);if(r)return r;throw new Error(`The provided outlet element is missing an outlet controller "${e}" instance for host controller "${this.identifier}"`)}throw new Error(`Missing outlet element "${e}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${r}".`)}},[`${t}Outlets`]:{get(){const t=this.outlets.findAll(e);return t.length>0?t.map((t=>{const r=ee(this,t,e);if(r)return r;console.warn(`The provided outlet element is missing an outlet controller "${e}" instance for host controller "${this.identifier}"`,t)})).filter((e=>e)):[]}},[`${t}OutletElement`]:{get(){const t=this.outlets.find(e),r=this.outlets.getSelectorForOutletName(e);if(t)return t;throw new Error(`Missing outlet element "${e}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${r}".`)}},[`${t}OutletElements`]:{get(){return this.outlets.findAll(e)}},[`has${c(t)}Outlet`]:{get(){return this.outlets.has(e)}}}}(t))),{})}],ue.targets=[],ue.outlets=[],ue.values={}},697:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=s(r(231)),i=s(r(830)),o=s(r(911));function s(e){return e&&e.__esModule?e:{default:e}}function a(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(613);function i(e,t,r,n,i,o,s){try{var a=e[o](s),u=a.value}catch(e){return void r(e)}a.done?t(u):Promise.resolve(u).then(n,i)}function o(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(613),i=r(541),o=r(874);function s(e,t,r,n,i,o,s){try{var a=e[o](s),u=a.value}catch(e){return void r(e)}a.done?t(u):Promise.resolve(u).then(n,i)}function a(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=r(688))&&n.__esModule?n:{default:n},o=r(613),s=r(874);function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function u(e){for(var t=1;t{function r(e,t,r,n,i,o,s){try{var a=e[o](s),u=a.value}catch(e){return void r(e)}a.done?t(u):Promise.resolve(u).then(n,i)}function n(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(613);function i(e,t,r,n,i,o,s){try{var a=e[o](s),u=a.value}catch(e){return void r(e)}a.done?t(u):Promise.resolve(u).then(n,i)}function o(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=(n=r(688))&&n.__esModule?n:{default:n},o=r(613),s=r(874);function a(e,t,r,n,i,o,s){try{var a=e[o](s),u=a.value}catch(e){return void r(e)}a.done?t(u):Promise.resolve(u).then(n,i)}function u(e){return function(){var t=this,r=arguments;return new Promise((function(n,i){var o=e.apply(t,r);function s(e){a(o,n,i,s,u,"next",e)}function u(e){a(o,n,i,s,u,"throw",e)}s(void 0)}))}}function c(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.InputBuilder=void 0;var n,i=(n=r(688))&&n.__esModule?n:{default:n};function o(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.LogoBuilder=void 0;var n,i=(n=r(688))&&n.__esModule?n:{default:n};function o(e,t){for(var r=0;r\n ".concat(i.default.business.locale.white_label.powered_by,'\n \n \n Hellotext\n \n \n \n ')}})},761:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.OTPBuilder=void 0;var n,i=(n=r(688))&&n.__esModule?n:{default:n};function o(e,t){for(var r=0;r\n
\n

').concat(t,'

\n \n
\n \n
\n \n
\n \n ")}})},425:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(599),i=u(r(688)),o=r(541),s=u(r(495)),a=r(761);function u(e){return e&&e.__esModule?e:{default:e}}function c(e,t,r,n,i,o,s){try{var a=e[o](s),u=a.value}catch(e){return void r(e)}a.done?t(u):Promise.resolve(u).then(n,i)}function l(e,t){for(var r=0;re.disabled=!0));var r=yield t.json();r.identified?this.completed():(i.default.setSession(r.session),this.revealOTPContainer(r.id))}},u=function(){var e=this,t=arguments;return new Promise((function(r,i){var o=n.apply(e,t);function s(e){c(o,r,i,s,a,"next",e)}function a(e){c(o,r,i,s,a,"throw",e)}s(void 0)}))},function(e){return u.apply(this,arguments)})},{key:"revealOTPContainer",value:function(e){var t=this.requiredInputs.find((e=>"email"===e.name))?i.default.business.locale.otp.sent_to_email:i.default.business.locale.otp.sent_to_phone;this.element.appendChild(a.OTPBuilder.build(e,t))}},{key:"completed",value:function(){this.form.markAsCompleted(this.formData),this.element.remove()}},{key:"showErrorMessages",value:function(){this.element.querySelectorAll("input:invalid").forEach((e=>{e.closest("article").querySelector("[data-error-container]").innerText=e.validationMessage}))}},{key:"clearErrorMessages",value:function(){this.element.querySelectorAll("input").forEach((e=>{e.closest("article").querySelector("[data-error-container]").innerText=""}))}},{key:"inputTargetConnected",value:function(e){e.getAttribute("data-default-value")&&(e.value=e.getAttribute("data-default-value"))}},{key:"requiredInputs",get:function(){return this.inputTargets.filter((e=>e.required))}},{key:"invalid",get:function(){return!this.element.checkValidity()}}],r&&l(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),v}(n.Controller);t.default=p,p.values={data:Object,step:{type:Number,default:1}},p.targets=["inputContainer","input","button","otpContainer"]},454:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(599),i=s(r(688)),o=s(r(230));function s(e){return e&&e.__esModule?e:{default:e}}function a(e,t,r,n,i,o,s){try{var a=e[o](s),u=a.value}catch(e){return void r(e)}a.done?t(u):Promise.resolve(u).then(n,i)}function u(e){return function(){var t=this,r=arguments;return new Promise((function(n,i){var o=e.apply(t,r);function s(e){a(o,n,i,s,u,"next",e)}function u(e){a(o,n,i,s,u,"throw",e)}s(void 0)}))}}function c(e,t){for(var r=0;r{this.attempts=0}),6e4)}},{key:"connect",value:function(){l(d(m.prototype),"connect",this).call(this),this.inputTarget.addEventListener("input",this.onInputChange)}},{key:"disconnect",value:function(){clearInterval(this.throttleInterval),this.inputTarget.removeEventListener("input",this.onInputChange),l(d(m.prototype),"disconnect",this).call(this)}},{key:"resend",value:(s=u((function*(){if(this.throttled)return alert(i.default.business.locale.otp.throttled);this.resendButtonTarget.disabled=!0,(yield o.default.resendOTP(this.submissionIdValue)).succeeded&&(this.resendButtonTarget.disabled=!1),alert(i.default.business.locale.otp.resend_successful),this.attempts+=1})),function(){return s.apply(this,arguments)})},{key:"onInputChange",value:(n=u((function*(){6===this.inputTarget.value.length&&(this.inputTarget.disabled=!0,this.resendButtonTarget.disabled=!0,(yield o.default.verifyOTP(this.submissionIdValue,this.inputTarget.value)).succeeded?this.dispatch("verified",{detail:{submissionId:this.submissionIdValue}}):alert(i.default.business.locale.otp.invalid),this.inputTarget.disabled=!1,this.resendButtonTarget.disabled=!1)})),function(){return n.apply(this,arguments)})},{key:"throttled",get:function(){return this.attempts>=3}}],r&&c(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),m}(n.Controller);t.default=f,f.values={submissionId:String},f.targets=["input","resendButton"]},660:(e,t)=>{function r(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(160);function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;tt===e))}}],(r=[{key:"addSubscriber",value:function(t,r){if(e.invalid(t))throw new n.InvalidEvent(t);this.subscribers=o(o({},this.subscribers),{},{[t]:this.subscribers[t]?[...this.subscribers[t],r]:[r]})}},{key:"removeSubscriber",value:function(t,r){if(e.invalid(t))throw new n.InvalidEvent(t);this.subscribers[t]&&(this.subscribers[t]=this.subscribers[t].filter((e=>e!==r)))}},{key:"dispatch",value:function(e,t){var r;null===(r=this.subscribers[e])||void 0===r||r.forEach((e=>{e(t)}))}},{key:"listeners",get:function(){return 0!==Object.keys(this.subscribers).length}}])&&a(t.prototype,r),i&&a(t,i),Object.defineProperty(t,"prototype",{writable:!1}),e}();t.default=c,c.events=["session-set","forms:collected","form:completed"]},613:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"Configuration",{enumerable:!0,get:function(){return o.Configuration}}),Object.defineProperty(t,"Event",{enumerable:!0,get:function(){return i.default}});var n,i=(n=r(51))&&n.__esModule?n:{default:n},o=r(660)},160:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"InvalidEvent",{enumerable:!0,get:function(){return n.InvalidEvent}}),Object.defineProperty(t,"NotInitializedError",{enumerable:!0,get:function(){return i.NotInitializedError}});var n=r(547),i=r(735)},547:(e,t)=>{function r(e){var t="function"==typeof Map?new Map:void 0;return r=function(e){if(null===e||(r=e,-1===Function.toString.call(r).indexOf("[native code]")))return e;var r;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,i)}function i(){return n(e,arguments,s(this).constructor)}return i.prototype=Object.create(e.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),o(i,e)},r(e)}function n(e,t,r){return n=i()?Reflect.construct.bind():function(e,t,r){var n=[null];n.push.apply(n,t);var i=new(Function.bind.apply(e,n));return r&&o(i,r.prototype),i},n.apply(null,arguments)}function i(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function o(e,t){return o=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},o(e,t)}function s(e){return s=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},s(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.InvalidEvent=void 0;var a=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&o(e,t)}(u,e);var t,r,n,a=(r=u,n=i(),function(){var e,t=s(r);if(n){var i=s(this).constructor;e=Reflect.construct(t,arguments,i)}else e=t.apply(this,arguments);return function(e,t){if(t&&("object"==typeof t||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e)}(this,e)});function u(e){var t;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,u),(t=a.call(this,"".concat(e," is not valid. Please provide a valid event name"))).name="InvalidEvent",t}return t=u,Object.defineProperty(t,"prototype",{writable:!1}),t}(r(Error));t.InvalidEvent=a},735:(e,t)=>{function r(e){var t="function"==typeof Map?new Map:void 0;return r=function(e){if(null===e||(r=e,-1===Function.toString.call(r).indexOf("[native code]")))return e;var r;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,i)}function i(){return n(e,arguments,s(this).constructor)}return i.prototype=Object.create(e.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),o(i,e)},r(e)}function n(e,t,r){return n=i()?Reflect.construct.bind():function(e,t,r){var n=[null];n.push.apply(n,t);var i=new(Function.bind.apply(e,n));return r&&o(i,r.prototype),i},n.apply(null,arguments)}function i(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function o(e,t){return o=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},o(e,t)}function s(e){return s=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},s(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.NotInitializedError=void 0;var a=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&o(e,t)}(u,e);var t,r,n,a=(r=u,n=i(),function(){var e,t=s(r);if(n){var i=s(this).constructor;e=Reflect.construct(t,arguments,i)}else e=t.apply(this,arguments);return function(e,t){if(t&&("object"==typeof t||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e)}(this,e)});function u(){var e;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,u),(e=a.call(this,"You need to initialize before tracking events. Call Hellotext.initialize and pass your public business id")).name="NotInitializedError",e}return t=u,Object.defineProperty(t,"prototype",{writable:!1}),t}(r(Error));t.NotInitializedError=a},688:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,i=r(613),o=(n=r(697))&&n.__esModule?n:{default:n},s=r(541),a=r(160);function u(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function c(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{autogenerateSession:!0};this.business=new s.Business(e),this.forms=new s.FormCollection,m(this,g)[g]=i.Configuration.assign(t),m(this,w)[w]=new s.Query,addEventListener("load",(()=>{this.forms.collect()})),m(this,w)[w].inPreviewMode||(m(this,w)[w].session?m(this,b)[b]=s.Cookies.set("hello_session",m(this,w)[w].session):t.autogenerateSession&&m(this,O)[O]().then((e=>{m(this,b)[b]=s.Cookies.set("hello_session",e.id)})))}},{key:"setSession",value:function(e){m(this,b)[b]=s.Cookies.set("hello_session",e)}},{key:"track",value:(n=d((function*(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this.notInitialized)throw new a.NotInitializedError;return yield o.default.events.create({headers:this.headers,body:c(c({session:this.session,action:e},t),{},{url:t&&t.url||window.location.href})})})),function(e){return n.apply(this,arguments)})},{key:"on",value:function(e,t){this.eventEmitter.addSubscriber(e,t)}},{key:"removeEventListener",value:function(e,t){this.eventEmitter.removeSubscriber(e,t)}},{key:"session",get:function(){if(this.notInitialized)throw new a.NotInitializedError;return m(this,b)[b]}},{key:"isInitialized",get:function(){return void 0!==m(this,b)[b]}},{key:"notInitialized",get:function(){return void 0===this.business.id}},{key:"headers",get:function(){if(this.notInitialized)throw new a.NotInitializedError;return{Authorization:"Bearer ".concat(this.business.id),Accept:"application.json","Content-Type":"application/json"}}}],r&&f(t,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();function P(){return(P=d((function*(){if(this.notInitialized)throw new a.NotInitializedError;return o.default.sessions(this.business.id).create()}))).apply(this,arguments)}Object.defineProperty(E,O,{value:function(){return P.apply(this,arguments)}}),Object.defineProperty(E,b,{writable:!0,value:void 0}),Object.defineProperty(E,g,{writable:!0,value:void 0}),Object.defineProperty(E,w,{writable:!0,value:void 0}),E.eventEmitter=new i.Event,E.forms=void 0,E.business=void 0;var j=E;t.default=j},989:(e,t,r)=>{var n=r(599),i=a(r(688)),o=a(r(425)),s=a(r(454));function a(e){return e&&e.__esModule?e:{default:e}}r(689);var u=n.Application.start();u.register("hellotext--form",o.default),u.register("hellotext--otp",s.default),i.default},485:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,t.default={otp:{sent_to_email:"A One-Time Password has been sent to your email address",sent_to_phone:"An One-Time Password has been sent to your phone number",resend_successful:"The One-Time Password has been resent successfully",invalid:"Invalid One-Time Password",resend:"Resend OTP",throttled:"You have reached the maximum number of attempts. Please try again in 1 minute."},white_label:{powered_by:"Powered by"}}},594:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,t.default={otp:{sent_to_email:"Un código de un solo uso ha sido enviado a tu correo electrónico",sent_to_phone:"Un código de un solo uso ha sido enviado a tu número de teléfono",resend_successful:"El código de un solo uso ha sido reenviado exitosamente",invalid:"Código de un solo uso inválido",resend:"Reenviar OTP",throttled:"Has alcanzado el número máximo de intentos. Por favor intenta de nuevo en 1 minuto."},white_label:{powered_by:"Desarrollado por"}}},779:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=o(r(485)),i=o(r(594));function o(e){return e&&e.__esModule?e:{default:e}}var s={en:n.default,es:i.default};t.default=s},926:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Business=void 0;var n=o(r(830)),i=o(r(779));function o(e){return e&&e.__esModule?e:{default:e}}function s(e,t){for(var r=0;re.json())).then((e=>this.data=e))}}])&&s(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();t.Business=a},524:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Cookies=void 0;var n,i=(n=r(688))&&n.__esModule?n:{default:n};function o(e,t){for(var r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.Form=void 0;var n,i=(n=r(688))&&n.__esModule?n:{default:n},o=r(219),s=r(846);function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function u(e,t,r){return(t=h(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function c(e,t,r,n,i,o,s){try{var a=e[o](s),u=a.value}catch(e){return void r(e)}a.done?t(u):Promise.resolve(u).then(n,i)}function l(e,t){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:null;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),Object.defineProperty(this,m,{value:v}),this.data=t,this.element=r||document.querySelector('[data-hello-form="'.concat(this.id,'"]'))||document.createElement("form")}var t,r,n,h;return t=e,r=[{key:"mount",value:(n=function*(){var e=this.data.steps[0];this.buildHeader(e.header),this.buildInputs(e.inputs),this.buildButton(e.button),this.buildFooter(e.footer),this.elementAttributes.forEach((e=>{this.element.setAttribute(e.name,e.value)})),document.contains(this.element)||document.body.appendChild(this.element),i.default.business.features.white_label||this.element.prepend(s.LogoBuilder.build())},h=function(){var e=this,t=arguments;return new Promise((function(r,i){var o=n.apply(e,t);function s(e){c(o,r,i,s,a,"next",e)}function a(e){c(o,r,i,s,a,"throw",e)}s(void 0)}))},function(){return h.apply(this,arguments)})},{key:"buildHeader",value:function(e){var t=d(this,m)[m]("[data-form-header]","header");t.innerHTML=e.content,this.element.querySelector("[data-form-header]")?this.element.querySelector("[data-form-header]").replaceWith(t):this.element.prepend(t)}},{key:"buildInputs",value:function(e){var t=d(this,m)[m]("[data-form-inputs]","main");e.map((e=>o.InputBuilder.build(e))).forEach((e=>t.appendChild(e))),this.element.querySelector("[data-form-inputs]")?this.element.querySelector("[data-form-inputs]").replaceWith(t):this.element.querySelector("[data-form-header]").insertAdjacentHTML("afterend",t.outerHTML)}},{key:"buildButton",value:function(e){var t=d(this,m)[m]("[data-form-button]","button");t.innerText=e.text,t.setAttribute("data-action","click->hellotext--form#submit"),t.setAttribute("data-hellotext--form-target","button"),this.element.querySelector("[data-form-button]")?this.element.querySelector("[data-form-button]").replaceWith(t):this.element.querySelector("[data-form-inputs]").insertAdjacentHTML("afterend",t.outerHTML)}},{key:"buildFooter",value:function(e){var t=d(this,m)[m]("[data-form-footer]","footer");t.innerHTML=e.content,this.element.querySelector("[data-form-footer]")?this.element.querySelector("[data-form-footer]").replaceWith(t):this.element.appendChild(t)}},{key:"markAsCompleted",value:function(e){localStorage.setItem("hello-form-".concat(this.id),"completed"),i.default.eventEmitter.dispatch("form:completed",function(e){for(var t=1;thellotext--form#completed"}]}}],r&&l(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();function v(e,t){var r=this.element.querySelector(e);if(r)return r.cloneNode(!0);var n=document.createElement(t);return n.setAttribute(e.replace("[","").replace("]",""),""),n}t.Form=y},187:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.FormCollection=void 0;var n=a(r(688)),i=a(r(697)),o=r(860),s=r(160);function a(e){return e&&e.__esModule?e:{default:e}}function u(e,t){for(var r=0;ri.default.forms.get(e).then((e=>e.json()))));n.default.business.enabledWhitelist||console.warn("No whitelist has been configured. It is advised to whitelist the domain to avoid bots from submitting forms."),Promise.all(t).then((e=>e.forEach(this.add))).then((()=>n.default.eventEmitter.dispatch("forms:collected",this)))}}},{key:"forEach",value:function(e){this.forms.forEach(e)}},{key:"map",value:function(e){return this.forms.map(e)}},{key:"add",value:function(e){this.includes(e.id)||this.forms.push(new o.Form(e))}},{key:"getById",value:function(e){return this.forms.find((t=>t.id===e))}},{key:"getByIndex",value:function(e){return this.forms[e]}},{key:"includes",value:function(e){return this.forms.some((t=>t.id===e))}},{key:"excludes",value:function(e){return!this.includes(e)}},{key:"length",get:function(){return this.forms.length}}],r&&u(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();function f(){return Array.from(document.querySelectorAll("[data-hello-form]")).map((e=>e.dataset.helloForm)).filter(this.excludes)}t.FormCollection=d},541:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"Business",{enumerable:!0,get:function(){return n.Business}}),Object.defineProperty(t,"Cookies",{enumerable:!0,get:function(){return s.Cookies}}),Object.defineProperty(t,"Form",{enumerable:!0,get:function(){return i.Form}}),Object.defineProperty(t,"FormCollection",{enumerable:!0,get:function(){return a.FormCollection}}),Object.defineProperty(t,"Query",{enumerable:!0,get:function(){return o.Query}});var n=r(926),i=r(860),o=r(992),s=r(524),a=r(187)},992:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Query=void 0;var n=r(524);function i(e,t){for(var r=0;r{r.d(t,{Z:()=>a});var n=r(81),i=r.n(n),o=r(645),s=r.n(o)()(i());s.push([e.id,"form[data-hello-form] {\n position: relative;\n\n article {\n [data-error-container] {\n display: none;\n }\n\n &:has(input:invalid) {\n [data-error-container] {\n display: block;\n }\n }\n }\n\n [data-logo-container] {\n display: flex;\n justify-content: center;\n align-items: flex-end;\n position: absolute;\n right: 1rem;\n bottom: 1rem;\n\n small {\n margin: 0 .3rem;\n }\n\n [data-hello-brand] {\n width: 4rem;\n }\n }\n}\n",""]);const a=s},645:e=>{e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var r="",n=void 0!==t[5];return t[4]&&(r+="@supports (".concat(t[4],") {")),t[2]&&(r+="@media ".concat(t[2]," {")),n&&(r+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),r+=e(t),n&&(r+="}"),t[2]&&(r+="}"),t[4]&&(r+="}"),r})).join("")},t.i=function(e,r,n,i,o){"string"==typeof e&&(e=[[null,e,void 0]]);var s={};if(n)for(var a=0;a0?" ".concat(l[5]):""," {").concat(l[1],"}")),l[5]=o),r&&(l[2]?(l[1]="@media ".concat(l[2]," {").concat(l[1],"}"),l[2]=r):l[2]=r),i&&(l[4]?(l[1]="@supports (".concat(l[4],") {").concat(l[1],"}"),l[4]=i):l[4]="".concat(i)),t.push(l))}},t}},81:e=>{e.exports=function(e){return e[1]}},689:(e,t,r)=>{r.r(t),r.d(t,{default:()=>v});var n=r(379),i=r.n(n),o=r(795),s=r.n(o),a=r(569),u=r.n(a),c=r(565),l=r.n(c),h=r(216),d=r.n(h),f=r(589),p=r.n(f),m=r(601),y={};y.styleTagTransform=p(),y.setAttributes=l(),y.insert=u().bind(null,"head"),y.domAPI=s(),y.insertStyleElement=d(),i()(m.Z,y);const v=m.Z&&m.Z.locals?m.Z.locals:void 0},379:e=>{var t=[];function r(e){for(var r=-1,n=0;n{var t={};e.exports=function(e,r){var n=function(e){if(void 0===t[e]){var r=document.querySelector(e);if(window.HTMLIFrameElement&&r instanceof window.HTMLIFrameElement)try{r=r.contentDocument.head}catch(e){r=null}t[e]=r}return t[e]}(e);if(!n)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");n.appendChild(r)}},216:e=>{e.exports=function(e){var t=document.createElement("style");return e.setAttributes(t,e.attributes),e.insert(t,e.options),t}},565:(e,t,r)=>{e.exports=function(e){var t=r.nc;t&&e.setAttribute("nonce",t)}},795:e=>{e.exports=function(e){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var t=e.insertStyleElement(e);return{update:function(r){!function(e,t,r){var n="";r.supports&&(n+="@supports (".concat(r.supports,") {")),r.media&&(n+="@media ".concat(r.media," {"));var i=void 0!==r.layer;i&&(n+="@layer".concat(r.layer.length>0?" ".concat(r.layer):""," {")),n+=r.css,i&&(n+="}"),r.media&&(n+="}"),r.supports&&(n+="}");var o=r.sourceMap;o&&"undefined"!=typeof btoa&&(n+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(o))))," */")),t.styleTagTransform(n,e,t.options)}(t,e,r)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}},589:e=>{e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}},147:(e,t,r)=>{var n="undefined"!=typeof globalThis&&globalThis||"undefined"!=typeof self&&self||void 0!==n&&n,i={searchParams:"URLSearchParams"in n,iterable:"Symbol"in n&&"iterator"in Symbol,blob:"FileReader"in n&&"Blob"in n&&function(){try{return new Blob,!0}catch(e){return!1}}(),formData:"FormData"in n,arrayBuffer:"ArrayBuffer"in n};if(i.arrayBuffer)var o=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],s=ArrayBuffer.isView||function(e){return e&&o.indexOf(Object.prototype.toString.call(e))>-1};function a(e){if("string"!=typeof e&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(e)||""===e)throw new TypeError('Invalid character in header field name: "'+e+'"');return e.toLowerCase()}function u(e){return"string"!=typeof e&&(e=String(e)),e}function c(e){var t={next:function(){var t=e.shift();return{done:void 0===t,value:t}}};return i.iterable&&(t[Symbol.iterator]=function(){return t}),t}function l(e){this.map={},e instanceof l?e.forEach((function(e,t){this.append(t,e)}),this):Array.isArray(e)?e.forEach((function(e){this.append(e[0],e[1])}),this):e&&Object.getOwnPropertyNames(e).forEach((function(t){this.append(t,e[t])}),this)}function h(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function d(e){return new Promise((function(t,r){e.onload=function(){t(e.result)},e.onerror=function(){r(e.error)}}))}function f(e){var t=new FileReader,r=d(t);return t.readAsArrayBuffer(e),r}function p(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function m(){return this.bodyUsed=!1,this._initBody=function(e){var t;this.bodyUsed=this.bodyUsed,this._bodyInit=e,e?"string"==typeof e?this._bodyText=e:i.blob&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:i.formData&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:i.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():i.arrayBuffer&&i.blob&&(t=e)&&DataView.prototype.isPrototypeOf(t)?(this._bodyArrayBuffer=p(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):i.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(e)||s(e))?this._bodyArrayBuffer=p(e):this._bodyText=e=Object.prototype.toString.call(e):this._bodyText="",this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):i.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},i.blob&&(this.blob=function(){var e=h(this);if(e)return e;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?h(this)||(ArrayBuffer.isView(this._bodyArrayBuffer)?Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset,this._bodyArrayBuffer.byteOffset+this._bodyArrayBuffer.byteLength)):Promise.resolve(this._bodyArrayBuffer)):this.blob().then(f)}),this.text=function(){var e,t,r,n=h(this);if(n)return n;if(this._bodyBlob)return e=this._bodyBlob,r=d(t=new FileReader),t.readAsText(e),r;if(this._bodyArrayBuffer)return Promise.resolve(function(e){for(var t=new Uint8Array(e),r=new Array(t.length),n=0;n-1?n:r),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&i)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(i),!("GET"!==this.method&&"HEAD"!==this.method||"no-store"!==t.cache&&"no-cache"!==t.cache)){var o=/([?&])_=[^&]*/;o.test(this.url)?this.url=this.url.replace(o,"$1_="+(new Date).getTime()):this.url+=(/\?/.test(this.url)?"&":"?")+"_="+(new Date).getTime()}}function b(e){var t=new FormData;return e.trim().split("&").forEach((function(e){if(e){var r=e.split("="),n=r.shift().replace(/\+/g," "),i=r.join("=").replace(/\+/g," ");t.append(decodeURIComponent(n),decodeURIComponent(i))}})),t}function g(e,t){if(!(this instanceof g))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');t||(t={}),this.type="default",this.status=void 0===t.status?200:t.status,this.ok=this.status>=200&&this.status<300,this.statusText=void 0===t.statusText?"":""+t.statusText,this.headers=new l(t.headers),this.url=t.url||"",this._initBody(e)}v.prototype.clone=function(){return new v(this,{body:this._bodyInit})},m.call(v.prototype),m.call(g.prototype),g.prototype.clone=function(){return new g(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new l(this.headers),url:this.url})},g.error=function(){var e=new g(null,{status:0,statusText:""});return e.type="error",e};var w=[301,302,303,307,308];g.redirect=function(e,t){if(-1===w.indexOf(t))throw new RangeError("Invalid status code");return new g(null,{status:t,headers:{location:e}})};var O=n.DOMException;try{new O}catch(e){(O=function(e,t){this.message=e,this.name=t;var r=Error(e);this.stack=r.stack}).prototype=Object.create(Error.prototype),O.prototype.constructor=O}function E(e,t){return new Promise((function(r,o){var s=new v(e,t);if(s.signal&&s.signal.aborted)return o(new O("Aborted","AbortError"));var a=new XMLHttpRequest;function c(){a.abort()}a.onload=function(){var e,t,n={status:a.status,statusText:a.statusText,headers:(e=a.getAllResponseHeaders()||"",t=new l,e.replace(/\r?\n[\t ]+/g," ").split("\r").map((function(e){return 0===e.indexOf("\n")?e.substr(1,e.length):e})).forEach((function(e){var r=e.split(":"),n=r.shift().trim();if(n){var i=r.join(":").trim();t.append(n,i)}})),t)};n.url="responseURL"in a?a.responseURL:n.headers.get("X-Request-URL");var i="response"in a?a.response:a.responseText;setTimeout((function(){r(new g(i,n))}),0)},a.onerror=function(){setTimeout((function(){o(new TypeError("Network request failed"))}),0)},a.ontimeout=function(){setTimeout((function(){o(new TypeError("Network request failed"))}),0)},a.onabort=function(){setTimeout((function(){o(new O("Aborted","AbortError"))}),0)},a.open(s.method,function(e){try{return""===e&&n.location.href?n.location.href:e}catch(t){return e}}(s.url),!0),"include"===s.credentials?a.withCredentials=!0:"omit"===s.credentials&&(a.withCredentials=!1),"responseType"in a&&(i.blob?a.responseType="blob":i.arrayBuffer&&s.headers.get("Content-Type")&&-1!==s.headers.get("Content-Type").indexOf("application/octet-stream")&&(a.responseType="arraybuffer")),!t||"object"!=typeof t.headers||t.headers instanceof l?s.headers.forEach((function(e,t){a.setRequestHeader(t,e)})):Object.getOwnPropertyNames(t.headers).forEach((function(e){a.setRequestHeader(e,u(t.headers[e]))})),s.signal&&(s.signal.addEventListener("abort",c),a.onreadystatechange=function(){4===a.readyState&&s.signal.removeEventListener("abort",c)}),a.send(void 0===s._bodyInit?null:s._bodyInit)}))}E.polyfill=!0,n.fetch||(n.fetch=E,n.Headers=l,n.Request=v,n.Response=g)}},t={};function r(n){var i=t[n];if(void 0!==i)return i.exports;var o=t[n]={id:n,exports:{}};return e[n](o,o.exports,r),o.exports}r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nc=void 0,r(147),r(599),r(989)})(); \ No newline at end of file diff --git a/docs/forms.md b/docs/forms.md new file mode 100644 index 0000000..c2ea77a --- /dev/null +++ b/docs/forms.md @@ -0,0 +1,127 @@ +## Forms + +Create dynamic forms based on built-in subscriber attributes such as name, email, phone or other custom properties unique to your business. +Then let Hellotext handle building the form, collecting, validating and authenticating the data users submit. + +For more information on how to create a form from the dashboard, view this [guide](https://help.hellotext.com/forms). + +### Collection Phase + +After initializing the `Hellotext` class, it attaches a `load` event listener and once the page is loaded, +it looks for any HTML element that is on the page that has a `data-hello-form` attribute. + +You can access the forms object to also trigger the form collection phase manually. +This is useful if you have a Single Page Application(SPA) and cannot hardcode the `data-hello-form` element on the rendered page. + +To manually collect forms, do the following. + +```javascript +Hellotext.initialize('HELLOTEXT_BUSINESS_ID') +Hellotext.forms.collect() +``` + +Once loaded, you can access the `FormCollection` object by calling `Hellotext.forms`. + +Make sure you have initialized with `Hellotext.initialize` otherwise an error is reported. + +Form collection finishes once Hellotext has fetched the data for the form elements present on the page from the Hellotext API. +Afterwards, it dispatches a `forms:collected` event that you can subscribe to. + +```javascript +Hellotext.on('forms:collected', (forms) => { + console.log(forms) // Instance of FormCollection +}) +``` + +The `FormCollection` class is a wrapper around the forms, which providers other useful methods. + +- `getById(id: string): Form` - Get a form by it's id +- `getByIndex(index: number): Form` - Get a form by it's index +- `includes(id: string): boolean` - Check if a form is included in the collection +- `excludes(id: string): boolean` - Check if a form is not included in the collection +- `length` - Get the number of forms in the collection +- `forEach`, `map` are also supported. + +### Mounting forms + +After the collection phase, form elements would be available to be mounted. Hellotext does not automatically mount form elements, +you have total control on when and where to mount the form elements. To mount a form object, you call the `mount` method on the form object. + +```javascript +Hellotext.on('forms:collected', (forms) => { + forms.getByIndex(0).mount() +}) +``` + +Mounting a form creates the form and it's components that are associated to it, and attaches it to the DOM. +Hellotext looks for a `form` element with the `data-hello-form` attribute and mounts the form inside it. +If this condition is not met, Hellotext creates the form manually and appends it to the body of the document. +We recommend to make the criteria met to ensure the form is loaded into an expected place in your page. + +### Validation + +Hellotext automatically validates the form inputs based on how they were configured on the dashboard +using browser's native [checkValidity()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement/checkValidity). +Once the user tries to submit the form and there are missing required fields, +the submission is halted and we display default browser's error message using [HTMLObjectElement.validationMessage](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validationMessage) property. + +### Authentication + +Hellotext protects you from bot submissions and protects your customers from identity theft and impersonation. +When a subscriber fills the form, Hellotext sends a One Time Password (OTP) code to the subscriber. If they have an email, +an email is sent to them, otherwise an SMS is sent to their phone number. +Until a valid OTP has been entered, data will not show up on the dashboard and will not contribute to attribution marketing. + +### Form Completion + +Once the user enters the OTP they received. The form is considered to be complete and will be sent to the Hellotext API to create(or update) a profile from the submission information. +The library also dispatches a `form:completed` event that you can subscribe to. In addition, a Session object is set and stored on the browser's cookies. +Additionally, the `Hellotext.session` is also set if no session was present already, you can listen for the session events by subscribing to `session-set` event. + +```javascript +Hellotext.on('form:completed', (form) => { + console.log(form) // An object from FormData +}) + +{ + id: "xxxxx", // Id of the form that has been completed + first_name: "Billy", + last_name: "Butcher", + email: "theboys@hellotext.com", + phone: "+1234567890", + property_by_id[xxxxx]: "value" +} +``` + +The data in the from will differ based on the inputs you have configured on the dashboard. + +### Understanding form's layout + +Hellotext assumes a fixed layout for forms, which are in order of Header, Inputs, Button and Notice. + +But you can override this layout if you want. Overriding a form's layout can be achieved +by moving the placement of the form's components. For example, if you want to display the Button component after the Footer, here's how you can do that + +```html +
+
+
+ + +
+``` + +Hellotext would simply load the contents inside the respective elements without creating the default layout. +If these elements were not defined, Hellotext would render the button then the notice component. + +### Customizing the Form's styles + +Generated form elements have minimum styles to make them display correctly. No colors, borders or padding are applied. +You can style the form elements to match your brand guidelines. Hellotext.js ships with a few lines of CSS to apply layout on the components. +See `styles/index.css` for the default styles applied to the form elements. + +### White Labels + +As a subscriber to Hellotext, if your package does not support white labels, a `powered by Hellotext` logo is displayed at the bottom-right of the form. +You are free to move the position of this element if you want, but it should be visible to your subscribers. diff --git a/docs/tracking.md b/docs/tracking.md new file mode 100644 index 0000000..dd0c9a3 --- /dev/null +++ b/docs/tracking.md @@ -0,0 +1,155 @@ +## Tracking Events + +Track subscriber events as they happen on your website and let Hellotext report them back to your business. + +Tracking events is straightforward and perhaps the simplest example is tracking a page view: + +```javascript +Hellotext.track('page.viewed') +``` + +In the example above only the name of the action is required. + +### Handling Responses + +The `track` method returns a Promise that can be `await`ed using the async/await syntax. Or using `.then` on the returned Promise. + +```javascript +const response = await Hellotext.track('page.viewed') +``` + +The return of the `Hellotext.track` method is an instance of a `Response` object that ships with the package. You can check the status of the response via methods, like: + +```javascript +if (response.failed) { + console.log('failed because', response.data) +} + +if (response.succeeded) { + console.log('success') + console.log(response.data) // { status: "received" } +} +``` + +### Parameters + +The parameters passed to the action must be a valid set of parameters as described in +[Tracking Actions](https://www.hellotext.com/api#tracking). + +#### URL Parameter + +The library takes care of handling the `url` parameter with the current URL automatically and is not required to specify it explicitly. +If you want to provide another url, you can pass a `url` key in the params object when tracking an event. + +```javascript +Hellotext.track('page.viewed', { + url: 'www.example.org', +}) +``` + +### Errors + +Failing to provide valid set of parameters will result in an error object being returned, describing the parameters that did not satisfy the rules. + +```javascript +const response = await Hellotext.track('app.installed', { app_parameters: { name: 'My App' } }) + +console.log(response.data) +``` + +yields + +```javascript +{ + errors: [ + { + type: 'parameter_not_unique', + parameter: 'name', + description: + 'The value must be unique and it is already present in another object of the same type.', + }, + ] +} +``` + +For a complete list of errors types. See [Error Types](https://www.hellotext.com/api#errors) + +### Associated objects + +Generally, most actions also require an associated object. These can be of type [`app`](https://www.hellotext.com/api#apps), [`coupon`](https://www.hellotext.com/api#coupons), [`form`](https://www.hellotext.com/api#forms), [`order`](https://www.hellotext.com/api#orders), [`product`](https://www.hellotext.com/api#products) and [`refund`](https://www.hellotext.com/api#refunds). +Aside from [Custom Actions](https://www.hellotext.com/api#create_an_action), which don't require the trackable to be present. + +You can create the associated object directly by defining its parameters in a hash: + +```javascript +Hellotext.track('order.placed', { + amount: 395.0, + currency: 'USD', + order_parameters: { + amount: '395.00', + reference: '654321', + }, +}) +``` + +If you want to reuse existing objects, you must pass the identifier of an existing associated object. For example, to track a product purchase the identifier of a previously created product object as the `product`. +For more information about identifiers, view the [Tracking API](https://www.hellotext.com/api#tracking) + +```javascript +Hellotext.track('product.purchased', { + amount: 395.0, + currency: 'USD', + product: 'erA2RAXE', +}) +``` + +## List of actions + +The following is a complete list of built-in actions and their required associated objects. + +| Action | Description | Required Parameter | +| --------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------- | +| **app.installed** | An app was installed. | `app` or [app_parameters](https://www.hellotext.com/api#app) | +| **app.removed** | An app was removed. | `app` or [app_parameters](https://www.hellotext.com/api#app) | +| **app.spent** | A customer spent on an app. | `app` or [app_parameters](https://www.hellotext.com/api#app) | +| **cart.abandoned** | A cart was abandoned. | `product` or [product_parameters](https://www.hellotext.com/api#products) | +| **cart.added** | Added an item to the cart. | `product` or [product_parameters](https://www.hellotext.com/api#products) | +| **cart.removed** | Removed an item from the cart. | `product` or [product_parameters](https://www.hellotext.com/api#products) | +| **coupon.redeemed** | A coupon was redeem by a customer. | `coupon` or [coupon_parameters](https://www.hellotext.com/api#coupons) | +| **form.completed** | A form was completed by the customer. | `form` or [form_parameters](https://www.hellotext.com/api#forms) | +| **order.placed** | Order has been placed. | `order` or [order_parameters](https://www.hellotext.com/api#orders) | +| **order.confirmed** | Order has been confirmed by you. | `order` or [order_parameters](https://www.hellotext.com/api#orders) | +| **order.cancelled** | Order has been cancelled either by you or your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders) | +| **order.shipped** | Order has been shipped to your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders) | +| **order.delivered** | Order has been delivered to your customer. | `order` or [order_parameters](https://www.hellotext.com/api#orders) | +| **page.viewed** | A page was viewed by a customer. | `url` | +| **product.purchased** | A product has been purchased. | `product` or [product_parameters](https://www.hellotext.com/api#products) | +| **product.viewed** | A product page has been viewed. | `product` or [product_parameters](https://www.hellotext.com/api#products) | +| **refund.requested** | A customer requested a refund. | `refund` or [refund_parameters](https://www.hellotext.com/api#refunds) | +| **refund.received** | A refund was issued by you to your customer. | `refund` or [refund_parameters](https://www.hellotext.com/api#refunds) | + +You can also create your **[own defined actions](https://www.hellotext.com/api#actions)**. + +## Additional Properties + +You can include additional attributes to the tracked event, additional properties must be included inside the `metadata` object: + +```javascript +Hellotext.track('product.purchased', { + amount: 0.2, + currency: 'USD', + metadata: { + myProperty: 'custom', + }, + tracked_at: 1665684173, +}) +``` + +### List of additional attributes + +| Property | Description | Type | Default | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | +| **amount** | Monetary amount that represents the revenue associated to this tracked event. | float | `0` | +| **currency** | Currency for the `amount` given in ISO 4217 format. | currency | `USD` | +| **metadata** | Set of key-value pairs that you can attach to an event. This can be useful for storing additional information about the object in a structured format. | hash | `{}` | +| **tracked_at** | Original date when the event happened. This is useful if you want to record an event that happened in the past. If no value is provided its value will be the same from `created_at`. | epoch | `null` | diff --git a/lib/api.js b/lib/api.js new file mode 100644 index 0000000..3c743d7 --- /dev/null +++ b/lib/api.js @@ -0,0 +1,73 @@ +'use strict' + +Object.defineProperty(exports, '__esModule', { + value: true, +}) +exports.default = void 0 +var _sessions = _interopRequireDefault(require('./api/sessions')) +var _businesses = _interopRequireDefault(require('./api/businesses')) +var _events = _interopRequireDefault(require('./api/events')) +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj } +} +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function') + } +} +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i] + descriptor.enumerable = descriptor.enumerable || false + descriptor.configurable = true + if ('value' in descriptor) descriptor.writable = true + Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps) + if (staticProps) _defineProperties(Constructor, staticProps) + Object.defineProperty(Constructor, 'prototype', { writable: false }) + return Constructor +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, 'string') + return typeof key === 'symbol' ? key : String(key) +} +function _toPrimitive(input, hint) { + if (typeof input !== 'object' || input === null) return input + var prim = input[Symbol.toPrimitive] + if (prim !== undefined) { + var res = prim.call(input, hint || 'default') + if (typeof res !== 'object') return res + throw new TypeError('@@toPrimitive must return a primitive value.') + } + return (hint === 'string' ? String : Number)(input) +} +var API = /*#__PURE__*/ (function () { + function API() { + _classCallCheck(this, API) + } + _createClass(API, null, [ + { + key: 'sessions', + value: function sessions(businessId) { + return new _sessions.default(businessId) + }, + }, + { + key: 'businesses', + get: function get() { + return _businesses.default + }, + }, + { + key: 'events', + get: function get() { + return _events.default + }, + }, + ]) + return API +})() +exports.default = API diff --git a/lib/api/businesses.js b/lib/api/businesses.js new file mode 100644 index 0000000..537e074 --- /dev/null +++ b/lib/api/businesses.js @@ -0,0 +1,45 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _core = require("../core"); +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var _default = /*#__PURE__*/function () { + function _default() { + _classCallCheck(this, _default); + } + _createClass(_default, null, [{ + key: "endpoint", + get: function get() { + return _core.Configuration.endpoint('public/businesses'); + } + }, { + key: "get", + value: function () { + var _get = _asyncToGenerator(function* (id) { + return fetch("".concat(this.endpoint, "/").concat(id), { + method: 'GET', + headers: { + Authorization: "Bearer ".concat(id), + Accept: 'application.json', + 'Content-Type': 'application/json' + } + }); + }); + function get(_x) { + return _get.apply(this, arguments); + } + return get; + }() + }]); + return _default; +}(); +exports.default = _default; \ No newline at end of file diff --git a/lib/api/events.js b/lib/api/events.js new file mode 100644 index 0000000..dfc46db --- /dev/null +++ b/lib/api/events.js @@ -0,0 +1,54 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _core = require("../core"); +var _models = require("../models"); +var _response = require("./response"); +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var EventsAPI = /*#__PURE__*/function () { + function EventsAPI() { + _classCallCheck(this, EventsAPI); + } + _createClass(EventsAPI, null, [{ + key: "endpoint", + get: function get() { + return _core.Configuration.endpoint('track/events'); + } + }, { + key: "create", + value: function () { + var _create = _asyncToGenerator(function* (_ref) { + var { + headers, + body + } = _ref; + if (_models.Query.inPreviewMode) { + return new _response.Response(true, { + received: true + }); + } + var response = yield fetch(this.endpoint, { + method: 'POST', + headers, + body: JSON.stringify(body) + }); + return new _response.Response(response.status === 200, yield response.json()); + }); + function create(_x) { + return _create.apply(this, arguments); + } + return create; + }() + }]); + return EventsAPI; +}(); +exports.default = EventsAPI; \ No newline at end of file diff --git a/lib/api/forms.js b/lib/api/forms.js new file mode 100644 index 0000000..aa026d1 --- /dev/null +++ b/lib/api/forms.js @@ -0,0 +1,65 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _hellotext = _interopRequireDefault(require("../hellotext")); +var _core = require("../core"); +var _response = require("./response"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var FormsAPI = /*#__PURE__*/function () { + function FormsAPI() { + _classCallCheck(this, FormsAPI); + } + _createClass(FormsAPI, null, [{ + key: "endpoint", + get: function get() { + return _core.Configuration.endpoint('public/forms'); + } + }, { + key: "get", + value: function () { + var _get = _asyncToGenerator(function* (id) { + return fetch("".concat(this.endpoint, "/").concat(id), { + method: 'GET', + headers: _hellotext.default.headers + }); + }); + function get(_x) { + return _get.apply(this, arguments); + } + return get; + }() + }, { + key: "submit", + value: function () { + var _submit = _asyncToGenerator(function* (id, data) { + var response = yield fetch("".concat(this.endpoint, "/").concat(id, "/submissions"), { + method: 'POST', + headers: _hellotext.default.headers, + body: JSON.stringify(_objectSpread({ + session: _hellotext.default.session + }, data)) + }); + return new _response.Response(response.ok, response); + }); + function submit(_x2, _x3) { + return _submit.apply(this, arguments); + } + return submit; + }() + }]); + return FormsAPI; +}(); +exports.default = FormsAPI; \ No newline at end of file diff --git a/lib/api/index.js b/lib/api/index.js new file mode 100644 index 0000000..4c9891f --- /dev/null +++ b/lib/api/index.js @@ -0,0 +1,51 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "Response", { + enumerable: true, + get: function get() { + return _response.Response; + } +}); +exports.default = void 0; +var _sessions = _interopRequireDefault(require("./sessions")); +var _businesses = _interopRequireDefault(require("./businesses")); +var _events = _interopRequireDefault(require("./events")); +var _forms = _interopRequireDefault(require("./forms")); +var _response = require("./response"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var API = /*#__PURE__*/function () { + function API() { + _classCallCheck(this, API); + } + _createClass(API, null, [{ + key: "sessions", + value: function sessions(businessId) { + return new _sessions.default(businessId); + } + }, { + key: "businesses", + get: function get() { + return _businesses.default; + } + }, { + key: "events", + get: function get() { + return _events.default; + } + }, { + key: "forms", + get: function get() { + return _forms.default; + } + }]); + return API; +}(); +exports.default = API; \ No newline at end of file diff --git a/lib/api/response.js b/lib/api/response.js new file mode 100644 index 0000000..c54d517 --- /dev/null +++ b/lib/api/response.js @@ -0,0 +1,57 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Response = void 0; +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } +var id = 0; +function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } +var _success = /*#__PURE__*/_classPrivateFieldLooseKey("success"); +var Response = /*#__PURE__*/function () { + function Response(success, response) { + _classCallCheck(this, Response); + Object.defineProperty(this, _success, { + writable: true, + value: void 0 + }); + this.response = response; + _classPrivateFieldLooseBase(this, _success)[_success] = success; + } + _createClass(Response, [{ + key: "data", + get: function get() { + return this.response; + } + }, { + key: "json", + value: function () { + var _json = _asyncToGenerator(function* () { + return yield this.response.json(); + }); + function json() { + return _json.apply(this, arguments); + } + return json; + }() + }, { + key: "failed", + get: function get() { + return _classPrivateFieldLooseBase(this, _success)[_success] === false; + } + }, { + key: "succeeded", + get: function get() { + return _classPrivateFieldLooseBase(this, _success)[_success] === true; + } + }]); + return Response; +}(); +exports.Response = Response; \ No newline at end of file diff --git a/lib/api/sessions.js b/lib/api/sessions.js new file mode 100644 index 0000000..70b22aa --- /dev/null +++ b/lib/api/sessions.js @@ -0,0 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _core = require("../core"); +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var SessionsAPI = /*#__PURE__*/function () { + function SessionsAPI(businessId) { + _classCallCheck(this, SessionsAPI); + this.businessId = businessId; + } + _createClass(SessionsAPI, [{ + key: "create", + value: function () { + var _create = _asyncToGenerator(function* () { + var response = yield fetch(SessionsAPI.endpoint, { + method: 'POST', + headers: { + Authorization: "Bearer ".concat(this.businessId), + Accept: 'application/json' + } + }); + return response.json(); + }); + function create() { + return _create.apply(this, arguments); + } + return create; + }() + }], [{ + key: "endpoint", + get: function get() { + return _core.Configuration.endpoint('track/sessions'); + } + }]); + return SessionsAPI; +}(); +exports.default = SessionsAPI; \ No newline at end of file diff --git a/lib/api/submissions.js b/lib/api/submissions.js new file mode 100644 index 0000000..00976ee --- /dev/null +++ b/lib/api/submissions.js @@ -0,0 +1,61 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _hellotext = _interopRequireDefault(require("../hellotext")); +var _core = require("../core"); +var _response = require("./response"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var SubmissionsAPI = /*#__PURE__*/function () { + function SubmissionsAPI() { + _classCallCheck(this, SubmissionsAPI); + } + _createClass(SubmissionsAPI, null, [{ + key: "endpoint", + get: function get() { + return _core.Configuration.endpoint('public/submissions'); + } + }, { + key: "resendOTP", + value: function () { + var _resendOTP = _asyncToGenerator(function* (id) { + var response = yield fetch("".concat(this.endpoint, "/").concat(id, "/otps"), { + method: 'POST', + headers: _hellotext.default.headers + }); + return new _response.Response(response.ok, response); + }); + function resendOTP(_x) { + return _resendOTP.apply(this, arguments); + } + return resendOTP; + }() + }, { + key: "verifyOTP", + value: function () { + var _verifyOTP = _asyncToGenerator(function* (id, otp) { + var response = yield fetch("".concat(this.endpoint, "/").concat(id, "/otps/").concat(otp, "/verify"), { + method: 'POST', + headers: _hellotext.default.headers + }); + return new _response.Response(response.ok, response); + }); + function verifyOTP(_x2, _x3) { + return _verifyOTP.apply(this, arguments); + } + return verifyOTP; + }() + }]); + return SubmissionsAPI; +}(); +var _default = SubmissionsAPI; +exports.default = _default; \ No newline at end of file diff --git a/lib/builders/inputBuilder.js b/lib/builders/inputBuilder.js new file mode 100644 index 0000000..6e7bade --- /dev/null +++ b/lib/builders/inputBuilder.js @@ -0,0 +1,97 @@ +'use strict' + +Object.defineProperty(exports, '__esModule', { + value: true, +}) +exports.InputBuilder = void 0 +var _hellotext = _interopRequireDefault(require('../hellotext')) +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj } +} +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function') + } +} +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i] + descriptor.enumerable = descriptor.enumerable || false + descriptor.configurable = true + if ('value' in descriptor) descriptor.writable = true + Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps) + if (staticProps) _defineProperties(Constructor, staticProps) + Object.defineProperty(Constructor, 'prototype', { writable: false }) + return Constructor +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, 'string') + return typeof key === 'symbol' ? key : String(key) +} +function _toPrimitive(input, hint) { + if (typeof input !== 'object' || input === null) return input + var prim = input[Symbol.toPrimitive] + if (prim !== undefined) { + var res = prim.call(input, hint || 'default') + if (typeof res !== 'object') return res + throw new TypeError('@@toPrimitive must return a primitive value.') + } + return (hint === 'string' ? String : Number)(input) +} +var InputBuilder = /*#__PURE__*/ (function () { + function InputBuilder() { + _classCallCheck(this, InputBuilder) + } + _createClass(InputBuilder, null, [ + { + key: 'build', + value: function build(data) { + var article = document.createElement('article') + var label = document.createElement('label') + var input = document.createElement('input') + label.innerText = data.label + input.type = data.type + input.required = data.required + input.placeholder = data.placeholder + if (['first_name', 'last_name'].includes(data.kind)) { + input.type = 'text' + input.id = input.name = data.kind + label.setAttribute('for', data.kind) + } else { + input.type = data.type + if (data.type === 'email') { + input.id = input.name = 'email' + label.setAttribute('for', 'email') + } else if (input.type === 'tel') { + input.id = input.name = 'phone' + label.setAttribute('for', 'phone') + input.value = '+'.concat(_hellotext.default.business.country.prefix) + input.setAttribute( + 'data-default-value', + '+'.concat(_hellotext.default.business.country.prefix), + ) + } else { + input.name = input.id = 'property_by_id['.concat(data.property, ']') + label.setAttribute('for', 'property_by_id['.concat(data.property, ']')) + } + } + var main = document.createElement('main') + main.appendChild(label) + main.appendChild(input) + article.appendChild(main) + article.setAttribute('data-hellotext--form-target', 'inputContainer') + input.setAttribute('data-hellotext--form-target', 'input') + var errorContainer = document.createElement('div') + errorContainer.setAttribute('data-error-container', '') + article.appendChild(errorContainer) + return article + }, + }, + ]) + return InputBuilder +})() +exports.InputBuilder = InputBuilder diff --git a/lib/builders/input_builder.js b/lib/builders/input_builder.js new file mode 100644 index 0000000..d103561 --- /dev/null +++ b/lib/builders/input_builder.js @@ -0,0 +1,61 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.InputBuilder = void 0; +var _hellotext = _interopRequireDefault(require("../hellotext")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var InputBuilder = /*#__PURE__*/function () { + function InputBuilder() { + _classCallCheck(this, InputBuilder); + } + _createClass(InputBuilder, null, [{ + key: "build", + value: function build(data) { + var article = document.createElement('article'); + var label = document.createElement('label'); + var input = document.createElement('input'); + label.innerText = data.label; + input.type = data.type; + input.required = data.required; + input.placeholder = data.placeholder; + if (['first_name', 'last_name'].includes(data.kind)) { + input.type = 'text'; + input.id = input.name = data.kind; + label.setAttribute('for', data.kind); + } else { + input.type = data.type; + if (data.type === 'email') { + input.id = input.name = 'email'; + label.setAttribute('for', 'email'); + } else if (input.type === 'tel') { + input.id = input.name = 'phone'; + label.setAttribute('for', 'phone'); + input.value = "+".concat(_hellotext.default.business.country.prefix); + input.setAttribute('data-default-value', "+".concat(_hellotext.default.business.country.prefix)); + } else { + input.name = input.id = "property_by_id[".concat(data.property, "]"); + label.setAttribute('for', "property_by_id[".concat(data.property, "]")); + } + } + var main = document.createElement('main'); + main.appendChild(label); + main.appendChild(input); + article.appendChild(main); + article.setAttribute('data-hellotext--form-target', 'inputContainer'); + input.setAttribute('data-hellotext--form-target', 'input'); + var errorContainer = document.createElement('div'); + errorContainer.setAttribute('data-error-container', ''); + article.appendChild(errorContainer); + return article; + } + }]); + return InputBuilder; +}(); +exports.InputBuilder = InputBuilder; \ No newline at end of file diff --git a/lib/builders/logo_builder.js b/lib/builders/logo_builder.js new file mode 100644 index 0000000..85c4619 --- /dev/null +++ b/lib/builders/logo_builder.js @@ -0,0 +1,38 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.LogoBuilder = void 0; +var _hellotext = _interopRequireDefault(require("../hellotext")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } +var id = 0; +function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } +var _template = /*#__PURE__*/_classPrivateFieldLooseKey("template"); +var LogoBuilder = /*#__PURE__*/function () { + function LogoBuilder() { + _classCallCheck(this, LogoBuilder); + } + _createClass(LogoBuilder, null, [{ + key: "build", + value: function build() { + var container = document.createElement('div'); + container.innerHTML = _classPrivateFieldLooseBase(this, _template)[_template](); + return container.firstElementChild; + } + }]); + return LogoBuilder; +}(); +exports.LogoBuilder = LogoBuilder; +function _template2() { + return "\n
\n ".concat(_hellotext.default.business.locale.white_label.powered_by, "\n \n \n Hellotext\n \n \n
\n "); +} +Object.defineProperty(LogoBuilder, _template, { + value: _template2 +}); \ No newline at end of file diff --git a/lib/builders/otp_builder.js b/lib/builders/otp_builder.js new file mode 100644 index 0000000..5a18218 --- /dev/null +++ b/lib/builders/otp_builder.js @@ -0,0 +1,39 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.OTPBuilder = void 0; +var _hellotext = _interopRequireDefault(require("../hellotext")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } +var id = 0; +function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } +var _template = /*#__PURE__*/_classPrivateFieldLooseKey("template"); +var OTPBuilder = /*#__PURE__*/function () { + function OTPBuilder() { + _classCallCheck(this, OTPBuilder); + } + _createClass(OTPBuilder, null, [{ + key: "build", + value: function build(submissionId, label) { + var element = _classPrivateFieldLooseBase(this, _template)[_template](submissionId, label); + var container = document.createElement('div'); + container.innerHTML = element; + return container; + } + }]); + return OTPBuilder; +}(); +exports.OTPBuilder = OTPBuilder; +function _template2(submissionId, label) { + return "\n
\n
\n

").concat(label, "

\n \n
\n \n
\n \n
\n
\n "); +} +Object.defineProperty(OTPBuilder, _template, { + value: _template2 +}); \ No newline at end of file diff --git a/lib/controllers/form_controller.js b/lib/controllers/form_controller.js new file mode 100644 index 0000000..c7f93e8 --- /dev/null +++ b/lib/controllers/form_controller.js @@ -0,0 +1,139 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _stimulus = require("@hotwired/stimulus"); +var _hellotext = _interopRequireDefault(require("../hellotext")); +var _models = require("../models"); +var _forms = _interopRequireDefault(require("../api/forms")); +var _otp_builder = require("../builders/otp_builder"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get.bind(); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); } +function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +var _default = /*#__PURE__*/function (_Controller) { + _inherits(_default, _Controller); + var _super = _createSuper(_default); + function _default() { + _classCallCheck(this, _default); + return _super.apply(this, arguments); + } + _createClass(_default, [{ + key: "initialize", + value: function initialize() { + this.form = new _models.Form(this.dataValue, this.element); + } + }, { + key: "connect", + value: function connect() { + _get(_getPrototypeOf(_default.prototype), "connect", this).call(this); + this.element.addEventListener('submit', this.submit.bind(this)); + if (document.activeElement.tagName !== 'INPUT') { + this.inputTargets[0].focus(); + } + } + }, { + key: "submit", + value: function () { + var _submit = _asyncToGenerator(function* (e) { + e.preventDefault(); + if (this.invalid) { + return this.showErrorMessages(); + } + this.clearErrorMessages(); + this.formData = Object.fromEntries(new FormData(this.element)); + this.buttonTarget.disabled = true; + var response = yield _forms.default.submit(this.form.id, this.formData); + this.buttonTarget.disabled = false; + if (response.failed) { + return; + } + this.buttonTarget.style.display = 'none'; + this.element.querySelectorAll('input').forEach(input => input.disabled = true); + var submission = yield response.json(); + if (submission.identified) { + this.completed(); + } else { + _hellotext.default.setSession(submission.session); + this.revealOTPContainer(submission.id); + } + }); + function submit(_x) { + return _submit.apply(this, arguments); + } + return submit; + }() + }, { + key: "revealOTPContainer", + value: function revealOTPContainer(submissionId) { + var paragraph = this.requiredInputs.find(input => input.name === 'email') ? _hellotext.default.business.locale.otp.sent_to_email : _hellotext.default.business.locale.otp.sent_to_phone; + this.element.appendChild(_otp_builder.OTPBuilder.build(submissionId, paragraph)); + } + }, { + key: "completed", + value: function completed() { + this.form.markAsCompleted(this.formData); + this.element.remove(); + } + + // private + }, { + key: "showErrorMessages", + value: function showErrorMessages() { + this.element.querySelectorAll('input:invalid').forEach(input => { + var parent = input.closest('article'); + parent.querySelector('[data-error-container]').innerText = input.validationMessage; + }); + } + }, { + key: "clearErrorMessages", + value: function clearErrorMessages() { + this.element.querySelectorAll('input').forEach(input => { + var parent = input.closest('article'); + parent.querySelector('[data-error-container]').innerText = ''; + }); + } + }, { + key: "inputTargetConnected", + value: function inputTargetConnected(target) { + if (target.getAttribute('data-default-value')) { + target.value = target.getAttribute('data-default-value'); + } + } + }, { + key: "requiredInputs", + get: function get() { + return this.inputTargets.filter(input => input.required); + } + }, { + key: "invalid", + get: function get() { + return !this.element.checkValidity(); + } + }]); + return _default; +}(_stimulus.Controller); +exports.default = _default; +_default.values = { + data: Object, + step: { + type: Number, + default: 1 + } +}; +_default.targets = ['inputContainer', 'input', 'button', 'otpContainer']; \ No newline at end of file diff --git a/lib/controllers/otp_controller.js b/lib/controllers/otp_controller.js new file mode 100644 index 0000000..f838ebe --- /dev/null +++ b/lib/controllers/otp_controller.js @@ -0,0 +1,114 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _stimulus = require("@hotwired/stimulus"); +var _hellotext = _interopRequireDefault(require("../hellotext")); +var _submissions = _interopRequireDefault(require("../api/submissions")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get.bind(); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); } +function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +var _default = /*#__PURE__*/function (_Controller) { + _inherits(_default, _Controller); + var _super = _createSuper(_default); + function _default() { + _classCallCheck(this, _default); + return _super.apply(this, arguments); + } + _createClass(_default, [{ + key: "initialize", + value: function initialize() { + _get(_getPrototypeOf(_default.prototype), "initialize", this).call(this); + this.attempts = 0; + this.onInputChange = this.onInputChange.bind(this); + this.throttleInterval = setInterval(() => { + this.attempts = 0; + }, 60000); + } + }, { + key: "connect", + value: function connect() { + _get(_getPrototypeOf(_default.prototype), "connect", this).call(this); + this.inputTarget.addEventListener('input', this.onInputChange); + } + }, { + key: "disconnect", + value: function disconnect() { + clearInterval(this.throttleInterval); + this.inputTarget.removeEventListener('input', this.onInputChange); + _get(_getPrototypeOf(_default.prototype), "disconnect", this).call(this); + } + }, { + key: "resend", + value: function () { + var _resend = _asyncToGenerator(function* () { + if (this.throttled) { + return alert(_hellotext.default.business.locale.otp.throttled); + } + this.resendButtonTarget.disabled = true; + var response = yield _submissions.default.resendOTP(this.submissionIdValue); + if (response.succeeded) { + this.resendButtonTarget.disabled = false; + } + alert(_hellotext.default.business.locale.otp.resend_successful); + this.attempts += 1; + }); + function resend() { + return _resend.apply(this, arguments); + } + return resend; + }() + }, { + key: "onInputChange", + value: function () { + var _onInputChange = _asyncToGenerator(function* () { + if (this.inputTarget.value.length !== 6) return; + this.inputTarget.disabled = true; + this.resendButtonTarget.disabled = true; + var response = yield _submissions.default.verifyOTP(this.submissionIdValue, this.inputTarget.value); + if (response.succeeded) { + this.dispatch('verified', { + detail: { + submissionId: this.submissionIdValue + } + }); + } else { + alert(_hellotext.default.business.locale.otp.invalid); + } + this.inputTarget.disabled = false; + this.resendButtonTarget.disabled = false; + }); + function onInputChange() { + return _onInputChange.apply(this, arguments); + } + return onInputChange; + }() // private + }, { + key: "throttled", + get: function get() { + return this.attempts >= 3; + } + }]); + return _default; +}(_stimulus.Controller); +exports.default = _default; +_default.values = { + submissionId: String +}; +_default.targets = ['input', 'resendButton']; \ No newline at end of file diff --git a/lib/core/configuration.js b/lib/core/configuration.js new file mode 100644 index 0000000..9be1948 --- /dev/null +++ b/lib/core/configuration.js @@ -0,0 +1,37 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Configuration = void 0; +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var Configuration = /*#__PURE__*/function () { + function Configuration() { + _classCallCheck(this, Configuration); + } + _createClass(Configuration, null, [{ + key: "assign", + value: function assign(_ref) { + var { + apiRoot, + autoGenerateSession + } = _ref; + this.apiRoot = apiRoot || this.apiRoot; + this.autoGenerateSession = autoGenerateSession; + return this; + } + }, { + key: "endpoint", + value: function endpoint(path) { + return "".concat(this.apiRoot, "/").concat(path); + } + }]); + return Configuration; +}(); +exports.Configuration = Configuration; +Configuration.apiRoot = 'https://api.hellotext.com/v1'; +Configuration.autoGenerateSession = true; \ No newline at end of file diff --git a/lib/core/event.js b/lib/core/event.js new file mode 100644 index 0000000..5b17b4b --- /dev/null +++ b/lib/core/event.js @@ -0,0 +1,73 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _errors = require("../errors"); +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var Event = /*#__PURE__*/function () { + function Event() { + _classCallCheck(this, Event); + this.subscribers = {}; + } + _createClass(Event, [{ + key: "addSubscriber", + value: function addSubscriber(eventName, callback) { + if (Event.invalid(eventName)) { + throw new _errors.InvalidEvent(eventName); + } + this.subscribers = _objectSpread(_objectSpread({}, this.subscribers), {}, { + [eventName]: this.subscribers[eventName] ? [...this.subscribers[eventName], callback] : [callback] + }); + } + }, { + key: "removeSubscriber", + value: function removeSubscriber(eventName, callback) { + if (Event.invalid(eventName)) { + throw new _errors.InvalidEvent(eventName); + } + if (this.subscribers[eventName]) { + this.subscribers[eventName] = this.subscribers[eventName].filter(cb => cb !== callback); + } + } + }, { + key: "dispatch", + value: function dispatch(eventName, data) { + var _this$subscribers$eve; + (_this$subscribers$eve = this.subscribers[eventName]) === null || _this$subscribers$eve === void 0 ? void 0 : _this$subscribers$eve.forEach(subscriber => { + subscriber(data); + }); + } + }, { + key: "listeners", + get: function get() { + return Object.keys(this.subscribers).length !== 0; + } + }], [{ + key: "valid", + value: function valid(name) { + return Event.exists(name); + } + }, { + key: "invalid", + value: function invalid(name) { + return !this.valid(name); + } + }, { + key: "exists", + value: function exists(name) { + return this.events.find(eventName => eventName === name) !== undefined; + } + }]); + return Event; +}(); +exports.default = Event; +Event.events = ['session-set', 'forms:collected', 'form:completed']; \ No newline at end of file diff --git a/lib/core/index.js b/lib/core/index.js new file mode 100644 index 0000000..c0ce972 --- /dev/null +++ b/lib/core/index.js @@ -0,0 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "Configuration", { + enumerable: true, + get: function get() { + return _configuration.Configuration; + } +}); +Object.defineProperty(exports, "Event", { + enumerable: true, + get: function get() { + return _event.default; + } +}); +var _event = _interopRequireDefault(require("./event")); +var _configuration = require("./configuration"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/lib/errors/index.js b/lib/errors/index.js new file mode 100644 index 0000000..9e00221 --- /dev/null +++ b/lib/errors/index.js @@ -0,0 +1,19 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "InvalidEvent", { + enumerable: true, + get: function get() { + return _invalid_event.InvalidEvent; + } +}); +Object.defineProperty(exports, "NotInitializedError", { + enumerable: true, + get: function get() { + return _not_initialized_error.NotInitializedError; + } +}); +var _invalid_event = require("./invalid_event"); +var _not_initialized_error = require("./not_initialized_error"); \ No newline at end of file diff --git a/lib/errors/invalid_event.js b/lib/errors/invalid_event.js new file mode 100644 index 0000000..712e1d1 --- /dev/null +++ b/lib/errors/invalid_event.js @@ -0,0 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.InvalidEvent = void 0; +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } +function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct.bind(); } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +var InvalidEvent = /*#__PURE__*/function (_Error) { + _inherits(InvalidEvent, _Error); + var _super = _createSuper(InvalidEvent); + function InvalidEvent(event) { + var _this; + _classCallCheck(this, InvalidEvent); + _this = _super.call(this, "".concat(event, " is not valid. Please provide a valid event name")); + _this.name = 'InvalidEvent'; + return _this; + } + return _createClass(InvalidEvent); +}( /*#__PURE__*/_wrapNativeSuper(Error)); +exports.InvalidEvent = InvalidEvent; \ No newline at end of file diff --git a/lib/errors/not_initialized_error.js b/lib/errors/not_initialized_error.js new file mode 100644 index 0000000..27d5302 --- /dev/null +++ b/lib/errors/not_initialized_error.js @@ -0,0 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.NotInitializedError = void 0; +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } +function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } +function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct.bind(); } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +var NotInitializedError = /*#__PURE__*/function (_Error) { + _inherits(NotInitializedError, _Error); + var _super = _createSuper(NotInitializedError); + function NotInitializedError() { + var _this; + _classCallCheck(this, NotInitializedError); + _this = _super.call(this, 'You need to initialize before tracking events. Call Hellotext.initialize and pass your public business id'); + _this.name = 'NotInitializedError'; + return _this; + } + return _createClass(NotInitializedError); +}( /*#__PURE__*/_wrapNativeSuper(Error)); +exports.NotInitializedError = NotInitializedError; \ No newline at end of file diff --git a/lib/event.js b/lib/event.js index 6fb9a2f..569424b 100644 --- a/lib/event.js +++ b/lib/event.js @@ -1,35 +1,162 @@ -"use strict"; +'use strict' -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } -function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } -function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } -var Event = /*#__PURE__*/function () { +Object.defineProperty(exports, '__esModule', { + value: true, +}) +exports.default = void 0 +var _invalidEvent = require('./errors/invalidEvent') +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object) + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object) + enumerableOnly && + (symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable + })), + keys.push.apply(keys, symbols) + } + return keys +} +function _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = null != arguments[i] ? arguments[i] : {} + i % 2 + ? ownKeys(Object(source), !0).forEach(function (key) { + _defineProperty(target, key, source[key]) + }) + : Object.getOwnPropertyDescriptors + ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) + : ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)) + }) + } + return target +} +function _defineProperty(obj, key, value) { + key = _toPropertyKey(key) + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true, + }) + } else { + obj[key] = value + } + return obj +} +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function') + } +} +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i] + descriptor.enumerable = descriptor.enumerable || false + descriptor.configurable = true + if ('value' in descriptor) descriptor.writable = true + Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps) + if (staticProps) _defineProperties(Constructor, staticProps) + Object.defineProperty(Constructor, 'prototype', { writable: false }) + return Constructor +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, 'string') + return typeof key === 'symbol' ? key : String(key) +} +function _toPrimitive(input, hint) { + if (typeof input !== 'object' || input === null) return input + var prim = input[Symbol.toPrimitive] + if (prim !== undefined) { + var res = prim.call(input, hint || 'default') + if (typeof res !== 'object') return res + throw new TypeError('@@toPrimitive must return a primitive value.') + } + return (hint === 'string' ? String : Number)(input) +} +var Event = /*#__PURE__*/ (function () { function Event() { - _classCallCheck(this, Event); + _classCallCheck(this, Event) + this.subscribers = {} } - _createClass(Event, null, [{ - key: "valid", - value: function valid(name) { - return Event.exists(name); - } - }, { - key: "invalid", - value: function invalid(name) { - return !this.valid(name); - } - }, { - key: "exists", - value: function exists(name) { - return this.events.find(eventName => eventName === name) !== undefined; - } - }]); - return Event; -}(); -exports.default = Event; -Event.events = ["session-set"]; \ No newline at end of file + _createClass( + Event, + [ + { + key: 'addSubscriber', + value: function addSubscriber(eventName, callback) { + if (Event.invalid(eventName)) { + throw new _invalidEvent.InvalidEvent(eventName) + } + this.subscribers = _objectSpread( + _objectSpread({}, this.subscribers), + {}, + { + [eventName]: this.subscribers[eventName] + ? [...this.subscribers[eventName], callback] + : [callback], + }, + ) + }, + }, + { + key: 'removeSubscriber', + value: function removeSubscriber(eventName, callback) { + if (Event.invalid(eventName)) { + throw new _invalidEvent.InvalidEvent(eventName) + } + if (this.subscribers[eventName]) { + this.subscribers[eventName] = this.subscribers[eventName].filter(cb => cb !== callback) + } + }, + }, + { + key: 'dispatch', + value: function dispatch(eventName, data) { + var _this$subscribers$eve + ;(_this$subscribers$eve = this.subscribers[eventName]) === null || + _this$subscribers$eve === void 0 + ? void 0 + : _this$subscribers$eve.forEach(subscriber => { + subscriber(data) + }) + }, + }, + { + key: 'listeners', + get: function get() { + return Object.keys(this.subscribers).length !== 0 + }, + }, + ], + [ + { + key: 'valid', + value: function valid(name) { + return Event.exists(name) + }, + }, + { + key: 'invalid', + value: function invalid(name) { + return !this.valid(name) + }, + }, + { + key: 'exists', + value: function exists(name) { + return this.events.find(eventName => eventName === name) !== undefined + }, + }, + ], + ) + return Event +})() +exports.default = Event +Event.events = ['session-set', 'forms:collected', 'form:completed'] diff --git a/lib/eventEmitter.js b/lib/eventEmitter.js index 8883a1e..b8a4086 100644 --- a/lib/eventEmitter.js +++ b/lib/eventEmitter.js @@ -1,49 +1,142 @@ -"use strict"; +'use strict' -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } -function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } -function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } -var EventEmitter = /*#__PURE__*/function () { +Object.defineProperty(exports, '__esModule', { + value: true, +}) +exports.default = void 0 +var _event = _interopRequireDefault(require('./event')) +var _invalidEvent = require('./errors/invalidEvent') +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj } +} +function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object) + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object) + enumerableOnly && + (symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable + })), + keys.push.apply(keys, symbols) + } + return keys +} +function _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = null != arguments[i] ? arguments[i] : {} + i % 2 + ? ownKeys(Object(source), !0).forEach(function (key) { + _defineProperty(target, key, source[key]) + }) + : Object.getOwnPropertyDescriptors + ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) + : ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)) + }) + } + return target +} +function _defineProperty(obj, key, value) { + key = _toPropertyKey(key) + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true, + }) + } else { + obj[key] = value + } + return obj +} +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function') + } +} +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i] + descriptor.enumerable = descriptor.enumerable || false + descriptor.configurable = true + if ('value' in descriptor) descriptor.writable = true + Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps) + if (staticProps) _defineProperties(Constructor, staticProps) + Object.defineProperty(Constructor, 'prototype', { writable: false }) + return Constructor +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, 'string') + return typeof key === 'symbol' ? key : String(key) +} +function _toPrimitive(input, hint) { + if (typeof input !== 'object' || input === null) return input + var prim = input[Symbol.toPrimitive] + if (prim !== undefined) { + var res = prim.call(input, hint || 'default') + if (typeof res !== 'object') return res + throw new TypeError('@@toPrimitive must return a primitive value.') + } + return (hint === 'string' ? String : Number)(input) +} +var EventEmitter = /*#__PURE__*/ (function () { function EventEmitter() { - _classCallCheck(this, EventEmitter); - this.subscribers = {}; + _classCallCheck(this, EventEmitter) + this.subscribers = {} } - _createClass(EventEmitter, [{ - key: "addSubscriber", - value: function addSubscriber(eventName, callback) { - this.subscribers = _objectSpread(_objectSpread({}, this.subscribers), {}, { - [eventName]: this.subscribers[eventName] ? [...this.subscribers[eventName], callback] : [callback] - }); - } - }, { - key: "removeSubscriber", - value: function removeSubscriber(eventName, callback) { - if (this.subscribers[eventName]) { - this.subscribers[eventName] = this.subscribers[eventName].filter(cb => cb !== callback); - } - } - }, { - key: "emit", - value: function emit(eventName, data) { - this.subscribers[eventName].forEach(subscriber => { - subscriber(data); - }); - } - }, { - key: "listeners", - get: function get() { - return Object.keys(this.subscribers).length !== 0; - } - }]); - return EventEmitter; -}(); -exports.default = EventEmitter; \ No newline at end of file + _createClass(EventEmitter, [ + { + key: 'addSubscriber', + value: function addSubscriber(eventName, callback) { + if (_event.default.invalid(eventName)) { + throw new _invalidEvent.InvalidEvent(eventName) + } + this.subscribers = _objectSpread( + _objectSpread({}, this.subscribers), + {}, + { + [eventName]: this.subscribers[eventName] + ? [...this.subscribers[eventName], callback] + : [callback], + }, + ) + }, + }, + { + key: 'removeSubscriber', + value: function removeSubscriber(eventName, callback) { + if (_event.default.invalid(eventName)) { + throw new _invalidEvent.InvalidEvent(eventName) + } + if (this.subscribers[eventName]) { + this.subscribers[eventName] = this.subscribers[eventName].filter(cb => cb !== callback) + } + }, + }, + { + key: 'emit', + value: function emit(eventName, data) { + var _this$subscribers$eve + ;(_this$subscribers$eve = this.subscribers[eventName]) === null || + _this$subscribers$eve === void 0 + ? void 0 + : _this$subscribers$eve.forEach(subscriber => { + subscriber(data) + }) + }, + }, + { + key: 'listeners', + get: function get() { + return Object.keys(this.subscribers).length !== 0 + }, + }, + ]) + return EventEmitter +})() +exports.default = EventEmitter diff --git a/lib/forms.js b/lib/forms.js new file mode 100644 index 0000000..362243d --- /dev/null +++ b/lib/forms.js @@ -0,0 +1,133 @@ +'use strict' + +Object.defineProperty(exports, '__esModule', { + value: true, +}) +exports.Forms = void 0 +var _hellotext = _interopRequireDefault(require('./hellotext.js')) +var _models = require('./models') +var _forms = _interopRequireDefault(require('./api/forms')) +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { default: obj } +} +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function') + } +} +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i] + descriptor.enumerable = descriptor.enumerable || false + descriptor.configurable = true + if ('value' in descriptor) descriptor.writable = true + Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps) + if (staticProps) _defineProperties(Constructor, staticProps) + Object.defineProperty(Constructor, 'prototype', { writable: false }) + return Constructor +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, 'string') + return typeof key === 'symbol' ? key : String(key) +} +function _toPrimitive(input, hint) { + if (typeof input !== 'object' || input === null) return input + var prim = input[Symbol.toPrimitive] + if (prim !== undefined) { + var res = prim.call(input, hint || 'default') + if (typeof res !== 'object') return res + throw new TypeError('@@toPrimitive must return a primitive value.') + } + return (hint === 'string' ? String : Number)(input) +} +function _classPrivateFieldLooseBase(receiver, privateKey) { + if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { + throw new TypeError('attempted to use private field on non-instance') + } + return receiver +} +var id = 0 +function _classPrivateFieldLooseKey(name) { + return '__private_' + id++ + '_' + name +} +var _formIdsToFetch = /*#__PURE__*/ _classPrivateFieldLooseKey('formIdsToFetch') +var Forms = /*#__PURE__*/ (function () { + function Forms() { + _classCallCheck(this, Forms) + Object.defineProperty(this, _formIdsToFetch, { + get: _get_formIdsToFetch, + set: void 0, + }) + this.forms = [] + this.includes = this.includes.bind(this) + this.excludes = this.excludes.bind(this) + } + _createClass(Forms, [ + { + key: 'collect', + value: function collect() { + var formsIdsToFetch = _classPrivateFieldLooseBase(this, _formIdsToFetch)[_formIdsToFetch] + if (formsIdsToFetch.length === 0) return + var promises = formsIdsToFetch.map(id => { + return _forms.default.get(id).then(response => response.json()) + }) + if (!_hellotext.default.business.enabledWhitelist) { + console.warn( + 'No whitelist has been configured. It is advised to whitelist the domain to avoid bots from submitting forms.', + ) + } + Promise.all(promises) + .then(forms => forms.forEach(form => this.add(form))) + .then(() => _hellotext.default.eventEmitter.emit('forms:collected', this)) + }, + }, + { + key: 'add', + value: function add(data) { + if (this.includes(data.id)) return + this.forms.push(new _models.Form(data)) + }, + }, + { + key: 'getById', + value: function getById(id) { + return this.forms.find(form => form.id === id) + }, + }, + { + key: 'getByIndex', + value: function getByIndex(index) { + return this.forms[index] + }, + }, + { + key: 'includes', + value: function includes(formId) { + return this.forms.some(form => form.id === formId) + }, + }, + { + key: 'excludes', + value: function excludes(id) { + return !this.includes(id) + }, + }, + { + key: 'length', + get: function get() { + return this.forms.length + }, + }, + ]) + return Forms +})() +exports.Forms = Forms +function _get_formIdsToFetch() { + return Array.from(document.querySelectorAll('[data-hello-form]')) + .map(form => form.dataset.helloForm) + .filter(this.excludes) +} diff --git a/lib/hellotext.js b/lib/hellotext.js index 66b8d71..c3e1574 100644 --- a/lib/hellotext.js +++ b/lib/hellotext.js @@ -4,12 +4,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; -var _event = _interopRequireDefault(require("./event")); -var _eventEmitter2 = _interopRequireDefault(require("./eventEmitter")); -var _response = _interopRequireDefault(require("./response")); -var _query2 = _interopRequireDefault(require("./query")); -var _notInitializedError = require("./errors/notInitializedError"); -var _invalidEvent = require("./errors/invalidEvent"); +var _core = require("./core"); +var _api = _interopRequireDefault(require("./api")); +var _models = require("./models"); +var _errors = require("./errors"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } @@ -25,15 +23,9 @@ function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototy var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } var _session = /*#__PURE__*/_classPrivateFieldLooseKey("session"); -var _business = /*#__PURE__*/_classPrivateFieldLooseKey("business"); var _config = /*#__PURE__*/_classPrivateFieldLooseKey("config"); -var _eventEmitter = /*#__PURE__*/_classPrivateFieldLooseKey("eventEmitter"); var _query = /*#__PURE__*/_classPrivateFieldLooseKey("query"); -var _notInitialized = /*#__PURE__*/_classPrivateFieldLooseKey("notInitialized"); var _mintAnonymousSession = /*#__PURE__*/_classPrivateFieldLooseKey("mintAnonymousSession"); -var _headers = /*#__PURE__*/_classPrivateFieldLooseKey("headers"); -var _setSessionCookie = /*#__PURE__*/_classPrivateFieldLooseKey("setSessionCookie"); -var _cookie = /*#__PURE__*/_classPrivateFieldLooseKey("cookie"); /** * @typedef {Object} Config * @property {Boolean} autogenerateSession @@ -54,21 +46,27 @@ var Hellotext = /*#__PURE__*/function () { var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { autogenerateSession: true }; - _classPrivateFieldLooseBase(this, _business)[_business] = business; - _classPrivateFieldLooseBase(this, _config)[_config] = config; - _classPrivateFieldLooseBase(this, _query)[_query] = new _query2.default(window.location.search); - if (_classPrivateFieldLooseBase(this, _query)[_query].has("preview")) return; - var session = _classPrivateFieldLooseBase(this, _query)[_query].get("session") || _classPrivateFieldLooseBase(this, _cookie)[_cookie]; - if (session && session !== "undefined" && session !== "null") { - _classPrivateFieldLooseBase(this, _session)[_session] = session; - _classPrivateFieldLooseBase(this, _setSessionCookie)[_setSessionCookie](); + this.business = new _models.Business(business); + this.forms = new _models.FormCollection(); + _classPrivateFieldLooseBase(this, _config)[_config] = _core.Configuration.assign(config); + _classPrivateFieldLooseBase(this, _query)[_query] = new _models.Query(); + addEventListener('load', () => { + this.forms.collect(); + }); + if (_classPrivateFieldLooseBase(this, _query)[_query].inPreviewMode) return; + if (_classPrivateFieldLooseBase(this, _query)[_query].session) { + _classPrivateFieldLooseBase(this, _session)[_session] = _models.Cookies.set('hello_session', _classPrivateFieldLooseBase(this, _query)[_query].session); } else if (config.autogenerateSession) { _classPrivateFieldLooseBase(this, _mintAnonymousSession)[_mintAnonymousSession]().then(response => { - _classPrivateFieldLooseBase(this, _session)[_session] = response.id; - _classPrivateFieldLooseBase(this, _setSessionCookie)[_setSessionCookie](); + _classPrivateFieldLooseBase(this, _session)[_session] = _models.Cookies.set('hello_session', response.id); }); } } + }, { + key: "setSession", + value: function setSession(value) { + _classPrivateFieldLooseBase(this, _session)[_session] = _models.Cookies.set('hello_session', value); + } /** * Tracks an action that has happened on the page @@ -82,25 +80,18 @@ var Hellotext = /*#__PURE__*/function () { value: function () { var _track = _asyncToGenerator(function* (action) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - if (_classPrivateFieldLooseBase(this, _notInitialized)[_notInitialized]) { - throw new _notInitializedError.NotInitializedError(); - } - if (_classPrivateFieldLooseBase(this, _query)[_query].has("preview")) { - return new _response.default(true, { - received: true - }); + if (this.notInitialized) { + throw new _errors.NotInitializedError(); } - var response = yield fetch(this.__apiURL + 'track/events', { - headers: _classPrivateFieldLooseBase(this, _headers)[_headers], - method: 'post', - body: JSON.stringify(_objectSpread(_objectSpread({ + return yield _api.default.events.create({ + headers: this.headers, + body: _objectSpread(_objectSpread({ session: this.session, action }, params), {}, { url: params && params.url || window.location.href - })) + }) }); - return new _response.default(response.status === 200, yield response.json()); }); function track(_x) { return _track.apply(this, arguments); @@ -115,10 +106,7 @@ var Hellotext = /*#__PURE__*/function () { }, { key: "on", value: function on(event, callback) { - if (_event.default.invalid(event)) { - throw new _invalidEvent.InvalidEvent(event); - } - _classPrivateFieldLooseBase(this, _eventEmitter)[_eventEmitter].addSubscriber(event, callback); + this.eventEmitter.addSubscriber(event, callback); } /** @@ -129,10 +117,7 @@ var Hellotext = /*#__PURE__*/function () { }, { key: "removeEventListener", value: function removeEventListener(event, callback) { - if (_event.default.invalid(event)) { - throw new _invalidEvent.InvalidEvent(event); - } - _classPrivateFieldLooseBase(this, _eventEmitter)[_eventEmitter].removeSubscriber(event, callback); + this.eventEmitter.removeSubscriber(event, callback); } /** @@ -142,8 +127,8 @@ var Hellotext = /*#__PURE__*/function () { }, { key: "session", get: function get() { - if (_classPrivateFieldLooseBase(this, _notInitialized)[_notInitialized]) { - throw new _notInitializedError.NotInitializedError(); + if (this.notInitialized) { + throw new _errors.NotInitializedError(); } return _classPrivateFieldLooseBase(this, _session)[_session]; } @@ -159,92 +144,55 @@ var Hellotext = /*#__PURE__*/function () { } // private + }, { + key: "notInitialized", + get: function get() { + return this.business.id === undefined; + } + }, { + key: "headers", + get: function get() { + if (this.notInitialized) { + throw new _errors.NotInitializedError(); + } + return { + Authorization: "Bearer ".concat(this.business.id), + Accept: 'application.json', + 'Content-Type': 'application/json' + }; + } }]); return Hellotext; }(); -function _get_notInitialized() { - return _classPrivateFieldLooseBase(this, _business)[_business] === undefined; -} function _mintAnonymousSession2() { return _mintAnonymousSession3.apply(this, arguments); } function _mintAnonymousSession3() { _mintAnonymousSession3 = _asyncToGenerator(function* () { - if (_classPrivateFieldLooseBase(this, _notInitialized)[_notInitialized]) { - throw new _notInitializedError.NotInitializedError(); + if (this.notInitialized) { + throw new _errors.NotInitializedError(); } - var trackingUrl = this.__apiURL + 'track/sessions'; - this.mintingPromise = yield fetch(trackingUrl, { - method: 'post', - headers: { - Authorization: "Bearer ".concat(_classPrivateFieldLooseBase(this, _business)[_business]) - } - }); - return this.mintingPromise.json(); + return _api.default.sessions(this.business.id).create(); }); return _mintAnonymousSession3.apply(this, arguments); } -function _get_headers() { - if (_classPrivateFieldLooseBase(this, _notInitialized)[_notInitialized]) { - throw new _notInitializedError.NotInitializedError(); - } - return { - Authorization: "Bearer ".concat(_classPrivateFieldLooseBase(this, _business)[_business]), - Accept: 'application.json', - 'Content-Type': 'application/json' - }; -} -function _setSessionCookie2() { - if (_classPrivateFieldLooseBase(this, _notInitialized)[_notInitialized]) { - throw new _notInitializedError.NotInitializedError(); - } - if (_classPrivateFieldLooseBase(this, _eventEmitter)[_eventEmitter].listeners) { - _classPrivateFieldLooseBase(this, _eventEmitter)[_eventEmitter].emit("session-set", _classPrivateFieldLooseBase(this, _session)[_session]); - } - document.cookie = "hello_session=".concat(_classPrivateFieldLooseBase(this, _session)[_session]); -} -function _get_cookie() { - var _document$cookie$matc; - return (_document$cookie$matc = document.cookie.match('(^|;)\\s*' + 'hello_session' + '\\s*=\\s*([^;]+)')) === null || _document$cookie$matc === void 0 ? void 0 : _document$cookie$matc.pop(); -} -Object.defineProperty(Hellotext, _cookie, { - get: _get_cookie, - set: void 0 -}); -Object.defineProperty(Hellotext, _setSessionCookie, { - value: _setSessionCookie2 -}); -Object.defineProperty(Hellotext, _headers, { - get: _get_headers, - set: void 0 -}); Object.defineProperty(Hellotext, _mintAnonymousSession, { value: _mintAnonymousSession2 }); -Object.defineProperty(Hellotext, _notInitialized, { - get: _get_notInitialized, - set: void 0 -}); -Hellotext.__apiURL = 'https://api.hellotext.com/v1/'; Object.defineProperty(Hellotext, _session, { writable: true, value: void 0 }); -Object.defineProperty(Hellotext, _business, { - writable: true, - value: void 0 -}); Object.defineProperty(Hellotext, _config, { writable: true, value: void 0 }); -Object.defineProperty(Hellotext, _eventEmitter, { - writable: true, - value: new _eventEmitter2.default() -}); Object.defineProperty(Hellotext, _query, { writable: true, value: void 0 }); +Hellotext.eventEmitter = new _core.Event(); +Hellotext.forms = void 0; +Hellotext.business = void 0; var _default = Hellotext; exports.default = _default; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index f4df6d8..ebbfa3c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,7 +4,14 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; +var _stimulus = require("@hotwired/stimulus"); var _hellotext = _interopRequireDefault(require("./hellotext")); +var _form_controller = _interopRequireDefault(require("./controllers/form_controller")); +var _otp_controller = _interopRequireDefault(require("./controllers/otp_controller")); +require("../styles/index.css"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +var application = _stimulus.Application.start(); +application.register('hellotext--form', _form_controller.default); +application.register('hellotext--otp', _otp_controller.default); var _default = _hellotext.default; exports.default = _default; \ No newline at end of file diff --git a/lib/locales/en.js b/lib/locales/en.js new file mode 100644 index 0000000..ad69ca8 --- /dev/null +++ b/lib/locales/en.js @@ -0,0 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _default = { + otp: { + sent_to_email: 'A One-Time Password has been sent to your email address', + sent_to_phone: 'An One-Time Password has been sent to your phone number', + resend_successful: 'The One-Time Password has been resent successfully', + invalid: 'Invalid One-Time Password', + resend: 'Resend OTP', + throttled: 'You have reached the maximum number of attempts. Please try again in 1 minute.' + }, + white_label: { + powered_by: "Powered by" + } +}; +exports.default = _default; \ No newline at end of file diff --git a/lib/locales/es.js b/lib/locales/es.js new file mode 100644 index 0000000..d36942f --- /dev/null +++ b/lib/locales/es.js @@ -0,0 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _default = { + otp: { + sent_to_email: 'Un código de un solo uso ha sido enviado a tu correo electrónico', + sent_to_phone: 'Un código de un solo uso ha sido enviado a tu número de teléfono', + resend_successful: 'El código de un solo uso ha sido reenviado exitosamente', + invalid: 'Código de un solo uso inválido', + resend: 'Reenviar OTP', + throttled: 'Has alcanzado el número máximo de intentos. Por favor intenta de nuevo en 1 minuto.' + }, + white_label: { + powered_by: "Desarrollado por" + } +}; +exports.default = _default; \ No newline at end of file diff --git a/lib/locales/index.js b/lib/locales/index.js new file mode 100644 index 0000000..4d46917 --- /dev/null +++ b/lib/locales/index.js @@ -0,0 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _en = _interopRequireDefault(require("./en")); +var _es = _interopRequireDefault(require("./es")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +var _default = { + en: _en.default, + es: _es.default +}; +exports.default = _default; \ No newline at end of file diff --git a/lib/models/business.js b/lib/models/business.js new file mode 100644 index 0000000..d1cde40 --- /dev/null +++ b/lib/models/business.js @@ -0,0 +1,57 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Business = void 0; +var _businesses = _interopRequireDefault(require("../api/businesses")); +var _locales = _interopRequireDefault(require("../locales")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var Business = /*#__PURE__*/function () { + function Business(id) { + _classCallCheck(this, Business); + this.id = id; + this.data = {}; + this.fetchPublicData(); + } + _createClass(Business, [{ + key: "subscription", + get: function get() { + return this.data.subscription; + } + }, { + key: "country", + get: function get() { + return this.data.country; + } + }, { + key: "enabledWhitelist", + get: function get() { + return this.data.whitelist !== 'disabled'; + } + }, { + key: "locale", + get: function get() { + return _locales.default[this.data.locale]; + } + }, { + key: "features", + get: function get() { + return this.data.features; + } + + // private + }, { + key: "fetchPublicData", + value: function fetchPublicData() { + _businesses.default.get(this.id).then(response => response.json()).then(data => this.data = data); + } + }]); + return Business; +}(); +exports.Business = Business; \ No newline at end of file diff --git a/lib/models/configuration.js b/lib/models/configuration.js new file mode 100644 index 0000000..2950577 --- /dev/null +++ b/lib/models/configuration.js @@ -0,0 +1,66 @@ +'use strict' + +Object.defineProperty(exports, '__esModule', { + value: true, +}) +exports.Configuration = void 0 +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function') + } +} +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i] + descriptor.enumerable = descriptor.enumerable || false + descriptor.configurable = true + if ('value' in descriptor) descriptor.writable = true + Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps) + if (staticProps) _defineProperties(Constructor, staticProps) + Object.defineProperty(Constructor, 'prototype', { writable: false }) + return Constructor +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, 'string') + return typeof key === 'symbol' ? key : String(key) +} +function _toPrimitive(input, hint) { + if (typeof input !== 'object' || input === null) return input + var prim = input[Symbol.toPrimitive] + if (prim !== undefined) { + var res = prim.call(input, hint || 'default') + if (typeof res !== 'object') return res + throw new TypeError('@@toPrimitive must return a primitive value.') + } + return (hint === 'string' ? String : Number)(input) +} +var Configuration = /*#__PURE__*/ (function () { + function Configuration() { + _classCallCheck(this, Configuration) + } + _createClass(Configuration, null, [ + { + key: 'assign', + value: function assign(_ref) { + var { apiRoot, autoGenerateSession } = _ref + this.apiRoot = apiRoot + this.autoGenerateSession = autoGenerateSession + return this + }, + }, + { + key: 'endpoint', + value: function endpoint(path) { + return ''.concat(this.apiRoot, '/').concat(path) + }, + }, + ]) + return Configuration +})() +exports.Configuration = Configuration +Configuration.apiRoot = 'http://api.lvh.me:3000/v1/' +Configuration.autoGenerateSession = true diff --git a/lib/models/cookies.js b/lib/models/cookies.js new file mode 100644 index 0000000..91cad4d --- /dev/null +++ b/lib/models/cookies.js @@ -0,0 +1,34 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Cookies = void 0; +var _hellotext = _interopRequireDefault(require("../hellotext")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var Cookies = /*#__PURE__*/function () { + function Cookies() { + _classCallCheck(this, Cookies); + } + _createClass(Cookies, null, [{ + key: "set", + value: function set(name, value) { + document.cookie = "".concat(name, "=").concat(value); + _hellotext.default.eventEmitter.dispatch('session-set', value); + return value; + } + }, { + key: "get", + value: function get(name) { + var _document$cookie$matc; + return (_document$cookie$matc = document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')) === null || _document$cookie$matc === void 0 ? void 0 : _document$cookie$matc.pop(); + } + }]); + return Cookies; +}(); +exports.Cookies = Cookies; \ No newline at end of file diff --git a/lib/models/form.js b/lib/models/form.js new file mode 100644 index 0000000..a6aa31a --- /dev/null +++ b/lib/models/form.js @@ -0,0 +1,148 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Form = void 0; +var _hellotext = _interopRequireDefault(require("../hellotext")); +var _input_builder = require("../builders/input_builder"); +var _logo_builder = require("../builders/logo_builder"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } +var id = 0; +function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } +var _findOrCreateComponent = /*#__PURE__*/_classPrivateFieldLooseKey("findOrCreateComponent"); +var Form = /*#__PURE__*/function () { + function Form(data) { + var element = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + _classCallCheck(this, Form); + Object.defineProperty(this, _findOrCreateComponent, { + value: _findOrCreateComponent2 + }); + this.data = data; + this.element = element || document.querySelector("[data-hello-form=\"".concat(this.id, "\"]")) || document.createElement('form'); + } + _createClass(Form, [{ + key: "mount", + value: function () { + var _mount = _asyncToGenerator(function* () { + var firstStep = this.data.steps[0]; + this.buildHeader(firstStep.header); + this.buildInputs(firstStep.inputs); + this.buildButton(firstStep.button); + this.buildFooter(firstStep.footer); + this.elementAttributes.forEach(attribute => { + this.element.setAttribute(attribute.name, attribute.value); + }); + if (!document.contains(this.element)) { + document.body.appendChild(this.element); + } + if (!_hellotext.default.business.features.white_label) { + this.element.prepend(_logo_builder.LogoBuilder.build()); + } + }); + function mount() { + return _mount.apply(this, arguments); + } + return mount; + }() + }, { + key: "buildHeader", + value: function buildHeader(header) { + var headerElement = _classPrivateFieldLooseBase(this, _findOrCreateComponent)[_findOrCreateComponent]('[data-form-header]', 'header'); + headerElement.innerHTML = header.content; + if (this.element.querySelector('[data-form-header]')) { + this.element.querySelector('[data-form-header]').replaceWith(headerElement); + } else { + this.element.prepend(headerElement); + } + } + }, { + key: "buildInputs", + value: function buildInputs(inputs) { + var inputsContainerElement = _classPrivateFieldLooseBase(this, _findOrCreateComponent)[_findOrCreateComponent]('[data-form-inputs]', 'main'); + var inputElements = inputs.map(input => _input_builder.InputBuilder.build(input)); + inputElements.forEach(inputElement => inputsContainerElement.appendChild(inputElement)); + if (this.element.querySelector('[data-form-inputs]')) { + this.element.querySelector('[data-form-inputs]').replaceWith(inputsContainerElement); + } else { + this.element.querySelector('[data-form-header]').insertAdjacentHTML('afterend', inputsContainerElement.outerHTML); + } + } + }, { + key: "buildButton", + value: function buildButton(button) { + var buttonElement = _classPrivateFieldLooseBase(this, _findOrCreateComponent)[_findOrCreateComponent]('[data-form-button]', 'button'); + buttonElement.innerText = button.text; + buttonElement.setAttribute('data-action', 'click->hellotext--form#submit'); + buttonElement.setAttribute('data-hellotext--form-target', 'button'); + if (this.element.querySelector('[data-form-button]')) { + this.element.querySelector('[data-form-button]').replaceWith(buttonElement); + } else { + this.element.querySelector('[data-form-inputs]').insertAdjacentHTML('afterend', buttonElement.outerHTML); + } + } + }, { + key: "buildFooter", + value: function buildFooter(footer) { + var element = _classPrivateFieldLooseBase(this, _findOrCreateComponent)[_findOrCreateComponent]('[data-form-footer]', 'footer'); + element.innerHTML = footer.content; + if (this.element.querySelector('[data-form-footer]')) { + this.element.querySelector('[data-form-footer]').replaceWith(element); + } else { + this.element.appendChild(element); + } + } + }, { + key: "markAsCompleted", + value: function markAsCompleted(data) { + localStorage.setItem("hello-form-".concat(this.id), 'completed'); + _hellotext.default.eventEmitter.dispatch('form:completed', _objectSpread({ + id: this.id + }, data)); + } + }, { + key: "id", + get: function get() { + return this.data.id; + } + }, { + key: "elementAttributes", + get: function get() { + return [{ + name: 'data-controller', + value: 'hellotext--form' + }, { + name: 'data-hello-form', + value: this.id + }, { + name: 'data-hellotext--form-data-value', + value: JSON.stringify(this.data) + }, { + name: 'data-action', + value: 'hellotext--otp:verified->hellotext--form#completed' + }]; + } + }]); + return Form; +}(); +exports.Form = Form; +function _findOrCreateComponent2(selector, tag) { + var existingElement = this.element.querySelector(selector); + if (existingElement) { + return existingElement.cloneNode(true); + } + var createdElement = document.createElement(tag); + createdElement.setAttribute(selector.replace('[', '').replace(']', ''), ''); + return createdElement; +} \ No newline at end of file diff --git a/lib/models/form_collection.js b/lib/models/form_collection.js new file mode 100644 index 0000000..a089457 --- /dev/null +++ b/lib/models/form_collection.js @@ -0,0 +1,96 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.FormCollection = void 0; +var _hellotext = _interopRequireDefault(require("../hellotext.js")); +var _api = _interopRequireDefault(require("../api")); +var _form = require("./form"); +var _errors = require("../errors"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } +var id = 0; +function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } +var _formIdsToFetch = /*#__PURE__*/_classPrivateFieldLooseKey("formIdsToFetch"); +var FormCollection = /*#__PURE__*/function () { + function FormCollection() { + _classCallCheck(this, FormCollection); + Object.defineProperty(this, _formIdsToFetch, { + get: _get_formIdsToFetch, + set: void 0 + }); + this.forms = []; + this.includes = this.includes.bind(this); + this.excludes = this.excludes.bind(this); + this.add = this.add.bind(this); + } + _createClass(FormCollection, [{ + key: "collect", + value: function collect() { + if (_hellotext.default.notInitialized) { + throw new _errors.NotInitializedError(); + } + var formsIdsToFetch = _classPrivateFieldLooseBase(this, _formIdsToFetch)[_formIdsToFetch]; + if (formsIdsToFetch.length === 0) return; + var promises = formsIdsToFetch.map(id => { + return _api.default.forms.get(id).then(response => response.json()); + }); + if (!_hellotext.default.business.enabledWhitelist) { + console.warn('No whitelist has been configured. It is advised to whitelist the domain to avoid bots from submitting forms.'); + } + Promise.all(promises).then(forms => forms.forEach(this.add)).then(() => _hellotext.default.eventEmitter.dispatch('forms:collected', this)); + } + }, { + key: "forEach", + value: function forEach(callback) { + this.forms.forEach(callback); + } + }, { + key: "map", + value: function map(callback) { + return this.forms.map(callback); + } + }, { + key: "add", + value: function add(data) { + if (this.includes(data.id)) return; + this.forms.push(new _form.Form(data)); + } + }, { + key: "getById", + value: function getById(id) { + return this.forms.find(form => form.id === id); + } + }, { + key: "getByIndex", + value: function getByIndex(index) { + return this.forms[index]; + } + }, { + key: "includes", + value: function includes(formId) { + return this.forms.some(form => form.id === formId); + } + }, { + key: "excludes", + value: function excludes(id) { + return !this.includes(id); + } + }, { + key: "length", + get: function get() { + return this.forms.length; + } + }]); + return FormCollection; +}(); +exports.FormCollection = FormCollection; +function _get_formIdsToFetch() { + return Array.from(document.querySelectorAll('[data-hello-form]')).map(form => form.dataset.helloForm).filter(this.excludes); +} \ No newline at end of file diff --git a/lib/models/index.js b/lib/models/index.js new file mode 100644 index 0000000..d4a501a --- /dev/null +++ b/lib/models/index.js @@ -0,0 +1,40 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "Business", { + enumerable: true, + get: function get() { + return _business.Business; + } +}); +Object.defineProperty(exports, "Cookies", { + enumerable: true, + get: function get() { + return _cookies.Cookies; + } +}); +Object.defineProperty(exports, "Form", { + enumerable: true, + get: function get() { + return _form.Form; + } +}); +Object.defineProperty(exports, "FormCollection", { + enumerable: true, + get: function get() { + return _form_collection.FormCollection; + } +}); +Object.defineProperty(exports, "Query", { + enumerable: true, + get: function get() { + return _query.Query; + } +}); +var _business = require("./business"); +var _form = require("./form"); +var _query = require("./query"); +var _cookies = require("./cookies"); +var _form_collection = require("./form_collection"); \ No newline at end of file diff --git a/lib/models/query.js b/lib/models/query.js new file mode 100644 index 0000000..4271692 --- /dev/null +++ b/lib/models/query.js @@ -0,0 +1,51 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Query = void 0; +var _cookies = require("./cookies"); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var Query = /*#__PURE__*/function () { + function Query() { + _classCallCheck(this, Query); + this.urlSearchParams = new URLSearchParams(window.location.search); + } + _createClass(Query, [{ + key: "get", + value: function get(param) { + return this.urlSearchParams.get(this.toHellotextParam(param)); + } + }, { + key: "has", + value: function has(param) { + return this.urlSearchParams.has(this.toHellotextParam(param)); + } + }, { + key: "inPreviewMode", + get: function get() { + return this.has('preview'); + } + }, { + key: "session", + get: function get() { + return this.get('session') || _cookies.Cookies.get('hello_session'); + } + }, { + key: "toHellotextParam", + value: function toHellotextParam(param) { + return "hello_".concat(param); + } + }], [{ + key: "inPreviewMode", + get: function get() { + return new this().inPreviewMode; + } + }]); + return Query; +}(); +exports.Query = Query; \ No newline at end of file diff --git a/lib/models/step.js b/lib/models/step.js new file mode 100644 index 0000000..cc75079 --- /dev/null +++ b/lib/models/step.js @@ -0,0 +1,45 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Step = void 0; +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +var Step = /*#__PURE__*/function () { + function Step(data) { + _classCallCheck(this, Step); + this.data = data; + } + _createClass(Step, [{ + key: "header", + get: function get() { + return this.data.header; + } + }, { + key: "inputs", + get: function get() { + return this.data.inputs; + } + }, { + key: "button", + get: function get() { + return this.data.button; + } + }, { + key: "footer", + get: function get() { + return this.data.footer; + } + }, { + key: "hasRequiredInputs", + get: function get() { + return this.inputs.some(input => input.required); + } + }]); + return Step; +}(); +exports.Step = Step; \ No newline at end of file diff --git a/lib/query.js b/lib/query.js index 8e939d6..5f5bf70 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1,35 +1,68 @@ -"use strict"; +'use strict' -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } -function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } -function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } -var Query = /*#__PURE__*/function () { +Object.defineProperty(exports, '__esModule', { + value: true, +}) +exports.default = void 0 +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function') + } +} +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i] + descriptor.enumerable = descriptor.enumerable || false + descriptor.configurable = true + if ('value' in descriptor) descriptor.writable = true + Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps) + if (staticProps) _defineProperties(Constructor, staticProps) + Object.defineProperty(Constructor, 'prototype', { writable: false }) + return Constructor +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, 'string') + return typeof key === 'symbol' ? key : String(key) +} +function _toPrimitive(input, hint) { + if (typeof input !== 'object' || input === null) return input + var prim = input[Symbol.toPrimitive] + if (prim !== undefined) { + var res = prim.call(input, hint || 'default') + if (typeof res !== 'object') return res + throw new TypeError('@@toPrimitive must return a primitive value.') + } + return (hint === 'string' ? String : Number)(input) +} +var Query = /*#__PURE__*/ (function () { function Query(urlQueries) { - _classCallCheck(this, Query); - this.urlSearchParams = new URLSearchParams(urlQueries); + _classCallCheck(this, Query) + this.urlSearchParams = new URLSearchParams(urlQueries) } - _createClass(Query, [{ - key: "get", - value: function get(param) { - return this.urlSearchParams.get(this.toHellotextParam(param)); - } - }, { - key: "has", - value: function has(param) { - return this.urlSearchParams.has(this.toHellotextParam(param)); - } - }, { - key: "toHellotextParam", - value: function toHellotextParam(param) { - return "hello_".concat(param); - } - }]); - return Query; -}(); -exports.default = Query; \ No newline at end of file + _createClass(Query, [ + { + key: 'get', + value: function get(param) { + return this.urlSearchParams.get(this.toHellotextParam(param)) + }, + }, + { + key: 'has', + value: function has(param) { + return this.urlSearchParams.has(this.toHellotextParam(param)) + }, + }, + { + key: 'toHellotextParam', + value: function toHellotextParam(param) { + return 'hello_'.concat(param) + }, + }, + ]) + return Query +})() +exports.default = Query diff --git a/lib/response.js b/lib/response.js index a234874..edd14ac 100644 --- a/lib/response.js +++ b/lib/response.js @@ -1,49 +1,89 @@ -"use strict"; +'use strict' -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = void 0; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } -function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } -function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } -function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } -var id = 0; -function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } -var _success = /*#__PURE__*/_classPrivateFieldLooseKey("success"); -var _response = /*#__PURE__*/_classPrivateFieldLooseKey("response"); -var Response = /*#__PURE__*/function () { +Object.defineProperty(exports, '__esModule', { + value: true, +}) +exports.default = void 0 +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function') + } +} +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i] + descriptor.enumerable = descriptor.enumerable || false + descriptor.configurable = true + if ('value' in descriptor) descriptor.writable = true + Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps) + if (staticProps) _defineProperties(Constructor, staticProps) + Object.defineProperty(Constructor, 'prototype', { writable: false }) + return Constructor +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, 'string') + return typeof key === 'symbol' ? key : String(key) +} +function _toPrimitive(input, hint) { + if (typeof input !== 'object' || input === null) return input + var prim = input[Symbol.toPrimitive] + if (prim !== undefined) { + var res = prim.call(input, hint || 'default') + if (typeof res !== 'object') return res + throw new TypeError('@@toPrimitive must return a primitive value.') + } + return (hint === 'string' ? String : Number)(input) +} +function _classPrivateFieldLooseBase(receiver, privateKey) { + if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { + throw new TypeError('attempted to use private field on non-instance') + } + return receiver +} +var id = 0 +function _classPrivateFieldLooseKey(name) { + return '__private_' + id++ + '_' + name +} +var _success = /*#__PURE__*/ _classPrivateFieldLooseKey('success') +var _response = /*#__PURE__*/ _classPrivateFieldLooseKey('response') +var Response = /*#__PURE__*/ (function () { function Response(success, response) { - _classCallCheck(this, Response); + _classCallCheck(this, Response) Object.defineProperty(this, _success, { writable: true, - value: void 0 - }); + value: void 0, + }) Object.defineProperty(this, _response, { writable: true, - value: void 0 - }); - _classPrivateFieldLooseBase(this, _success)[_success] = success; - _classPrivateFieldLooseBase(this, _response)[_response] = response; + value: void 0, + }) + _classPrivateFieldLooseBase(this, _success)[_success] = success + _classPrivateFieldLooseBase(this, _response)[_response] = response } - _createClass(Response, [{ - key: "data", - get: function get() { - return _classPrivateFieldLooseBase(this, _response)[_response]; - } - }, { - key: "failed", - get: function get() { - return _classPrivateFieldLooseBase(this, _success)[_success] === false; - } - }, { - key: "succeeded", - get: function get() { - return _classPrivateFieldLooseBase(this, _success)[_success] === true; - } - }]); - return Response; -}(); -exports.default = Response; \ No newline at end of file + _createClass(Response, [ + { + key: 'data', + get: function get() { + return _classPrivateFieldLooseBase(this, _response)[_response] + }, + }, + { + key: 'failed', + get: function get() { + return _classPrivateFieldLooseBase(this, _success)[_success] === false + }, + }, + { + key: 'succeeded', + get: function get() { + return _classPrivateFieldLooseBase(this, _success)[_success] === true + }, + }, + ]) + return Response +})() +exports.default = Response diff --git a/package.json b/package.json index 9be410f..d1969f9 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "jest-environment-jsdom": "^29.3.1", "prettier": "2.8.2", "webpack": "^5.75.0", - "webpack-cli": "^5.0.1" + "webpack-cli": "^5.0.1", + "style-loader": "^3.3.1", + "css-loader": "^6.5.0" }, "repository": { "type": "git", @@ -39,6 +41,7 @@ }, "dependencies": { "core-js": "3.27.2", - "whatwg-fetch": "^3.6.2" + "whatwg-fetch": "^3.6.2", + "@hotwired/stimulus": "^3.0.0" } } diff --git a/src/api/businesses.js b/src/api/businesses.js new file mode 100644 index 0000000..5375e7b --- /dev/null +++ b/src/api/businesses.js @@ -0,0 +1,18 @@ +import { Configuration } from '../core' + +export default class { + static get endpoint() { + return Configuration.endpoint('public/businesses') + } + + static async get(id) { + return fetch(`${this.endpoint}/${id}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${id}`, + Accept: 'application.json', + 'Content-Type': 'application/json', + }, + }) + } +} diff --git a/src/api/events.js b/src/api/events.js new file mode 100644 index 0000000..fa27318 --- /dev/null +++ b/src/api/events.js @@ -0,0 +1,24 @@ +import { Configuration } from '../core' + +import { Query } from '../models' +import { Response } from './response' + +export default class EventsAPI { + static get endpoint() { + return Configuration.endpoint('track/events') + } + + static async create({ headers, body }) { + if (Query.inPreviewMode) { + return new Response(true, { received: true }) + } + + const response = await fetch(this.endpoint, { + method: 'POST', + headers, + body: JSON.stringify(body), + }) + + return new Response(response.status === 200, await response.json()) + } +} diff --git a/src/api/forms.js b/src/api/forms.js new file mode 100644 index 0000000..185b5d4 --- /dev/null +++ b/src/api/forms.js @@ -0,0 +1,30 @@ +import Hellotext from '../hellotext' +import { Configuration } from '../core' + +import { Response } from './response' + +export default class FormsAPI { + static get endpoint() { + return Configuration.endpoint('public/forms') + } + + static async get(id) { + return fetch(`${this.endpoint}/${id}`, { + method: 'GET', + headers: Hellotext.headers, + }) + } + + static async submit(id, data) { + const response = await fetch(`${this.endpoint}/${id}/submissions`, { + method: 'POST', + headers: Hellotext.headers, + body: JSON.stringify({ + session: Hellotext.session, + ...data, + }), + }) + + return new Response(response.ok, response) + } +} diff --git a/src/api/index.js b/src/api/index.js new file mode 100644 index 0000000..fbd7ac1 --- /dev/null +++ b/src/api/index.js @@ -0,0 +1,24 @@ +import SessionsAPI from './sessions' +import BusinessesAPI from './businesses' +import EventsAPI from './events' +import FormsAPI from './forms' + +export default class API { + static sessions(businessId) { + return new SessionsAPI(businessId) + } + + static get businesses() { + return BusinessesAPI + } + + static get events() { + return EventsAPI + } + + static get forms() { + return FormsAPI + } +} + +export { Response } from './response' diff --git a/src/response.js b/src/api/response.js similarity index 58% rename from src/response.js rename to src/api/response.js index 1741c6b..ccdf11c 100644 --- a/src/response.js +++ b/src/api/response.js @@ -1,14 +1,17 @@ -export default class Response { +class Response { #success - #response constructor(success, response) { + this.response = response this.#success = success - this.#response = response } get data() { - return this.#response + return this.response + } + + async json() { + return await this.response.json() } get failed() { @@ -19,3 +22,5 @@ export default class Response { return this.#success === true } } + +export { Response } diff --git a/src/api/sessions.js b/src/api/sessions.js new file mode 100644 index 0000000..753d723 --- /dev/null +++ b/src/api/sessions.js @@ -0,0 +1,23 @@ +import { Configuration } from '../core' + +export default class SessionsAPI { + static get endpoint() { + return Configuration.endpoint('track/sessions') + } + + constructor(businessId) { + this.businessId = businessId + } + + async create() { + const response = await fetch(SessionsAPI.endpoint, { + method: 'POST', + headers: { + Authorization: `Bearer ${this.businessId}`, + Accept: 'application/json', + }, + }) + + return response.json() + } +} diff --git a/src/api/submissions.js b/src/api/submissions.js new file mode 100644 index 0000000..ac23774 --- /dev/null +++ b/src/api/submissions.js @@ -0,0 +1,30 @@ +import Hellotext from '../hellotext' +import { Configuration } from '../core' + +import { Response } from './response' + +class SubmissionsAPI { + static get endpoint() { + return Configuration.endpoint('public/submissions') + } + + static async resendOTP(id) { + const response = await fetch(`${this.endpoint}/${id}/otps`, { + method: 'POST', + headers: Hellotext.headers, + }) + + return new Response(response.ok, response) + } + + static async verifyOTP(id, otp) { + const response = await fetch(`${this.endpoint}/${id}/otps/${otp}/verify`, { + method: 'POST', + headers: Hellotext.headers, + }) + + return new Response(response.ok, response) + } +} + +export default SubmissionsAPI diff --git a/src/builders/input_builder.js b/src/builders/input_builder.js new file mode 100644 index 0000000..850f7fa --- /dev/null +++ b/src/builders/input_builder.js @@ -0,0 +1,55 @@ +import Hellotext from '../hellotext' + +class InputBuilder { + static build(data) { + const article = document.createElement('article') + const label = document.createElement('label') + const input = document.createElement('input') + + label.innerText = data.label + + input.type = data.type + input.required = data.required + input.placeholder = data.placeholder + + if (['first_name', 'last_name'].includes(data.kind)) { + input.type = 'text' + input.id = input.name = data.kind + label.setAttribute('for', data.kind) + } else { + input.type = data.type + + if (data.type === 'email') { + input.id = input.name = 'email' + label.setAttribute('for', 'email') + } else if (input.type === 'tel') { + input.id = input.name = 'phone' + label.setAttribute('for', 'phone') + input.value = `+${Hellotext.business.country.prefix}` + input.setAttribute('data-default-value', `+${Hellotext.business.country.prefix}`) + } else { + input.name = input.id = `property_by_id[${data.property}]` + label.setAttribute('for', `property_by_id[${data.property}]`) + } + } + + const main = document.createElement('main') + + main.appendChild(label) + main.appendChild(input) + + article.appendChild(main) + + article.setAttribute('data-hellotext--form-target', 'inputContainer') + input.setAttribute('data-hellotext--form-target', 'input') + + const errorContainer = document.createElement('div') + errorContainer.setAttribute('data-error-container', '') + + article.appendChild(errorContainer) + + return article + } +} + +export { InputBuilder } diff --git a/src/builders/logo_builder.js b/src/builders/logo_builder.js new file mode 100644 index 0000000..1dd6a66 --- /dev/null +++ b/src/builders/logo_builder.js @@ -0,0 +1,25 @@ +import Hellotext from '../hellotext' + +class LogoBuilder { + static build() { + const container = document.createElement('div') + container.innerHTML = this.#template() + + return container.firstElementChild + } + + static #template() { + return ` +
+ ${Hellotext.business.locale.white_label.powered_by} + + + Hellotext + + +
+ ` + } +} + +export { LogoBuilder } diff --git a/src/builders/otp_builder.js b/src/builders/otp_builder.js new file mode 100644 index 0000000..333eb5e --- /dev/null +++ b/src/builders/otp_builder.js @@ -0,0 +1,42 @@ +import Hellotext from '../hellotext' + +class OTPBuilder { + static build(submissionId, label) { + const element = this.#template(submissionId, label) + const container = document.createElement('div') + + container.innerHTML = element + + return container + } + + static #template(submissionId, label) { + return ` +
+
+

${label}

+ +
+ +
+ +
+
+ ` + } +} + +export { OTPBuilder } diff --git a/src/controllers/form_controller.js b/src/controllers/form_controller.js new file mode 100644 index 0000000..b05c115 --- /dev/null +++ b/src/controllers/form_controller.js @@ -0,0 +1,105 @@ +import { Controller } from '@hotwired/stimulus' + +import Hellotext from '../hellotext' +import { Form } from '../models' + +import FormsAPI from '../api/forms' + +import { OTPBuilder } from '../builders/otp_builder' + +export default class extends Controller { + static values = { + data: Object, + step: { type: Number, default: 1 }, + } + + static targets = ['inputContainer', 'input', 'button', 'otpContainer'] + + initialize() { + this.form = new Form(this.dataValue, this.element) + } + + connect() { + super.connect() + this.element.addEventListener('submit', this.submit.bind(this)) + + if (document.activeElement.tagName !== 'INPUT') { + this.inputTargets[0].focus() + } + } + + async submit(e) { + e.preventDefault() + + if (this.invalid) { + return this.showErrorMessages() + } + + this.clearErrorMessages() + + this.formData = Object.fromEntries(new FormData(this.element)) + this.buttonTarget.disabled = true + + const response = await FormsAPI.submit(this.form.id, this.formData) + this.buttonTarget.disabled = false + + if(response.failed) { + return + } + + this.buttonTarget.style.display = 'none' + this.element.querySelectorAll('input').forEach(input => (input.disabled = true)) + + const submission = await response.json() + + if(submission.identified) { + this.completed() + } else { + Hellotext.setSession(submission.session) + this.revealOTPContainer(submission.id) + } + } + + revealOTPContainer(submissionId) { + const paragraph = this.requiredInputs.find(input => input.name === 'email') + ? Hellotext.business.locale.otp.sent_to_email + : Hellotext.business.locale.otp.sent_to_phone + + this.element.appendChild(OTPBuilder.build(submissionId, paragraph)) + } + + completed() { + this.form.markAsCompleted(this.formData) + this.element.remove() + } + + // private + + showErrorMessages() { + this.element.querySelectorAll('input:invalid').forEach(input => { + const parent = input.closest('article') + parent.querySelector('[data-error-container]').innerText = input.validationMessage + }) + } + + clearErrorMessages() { + this.element.querySelectorAll('input').forEach(input => { + const parent = input.closest('article') + parent.querySelector('[data-error-container]').innerText = '' + }) + } + + inputTargetConnected(target) { + if (target.getAttribute('data-default-value')) { + target.value = target.getAttribute('data-default-value') + } + } + + get requiredInputs() { + return this.inputTargets.filter(input => input.required) + } + + get invalid() { + return !this.element.checkValidity() + } +} diff --git a/src/controllers/otp_controller.js b/src/controllers/otp_controller.js new file mode 100644 index 0000000..99c7ac9 --- /dev/null +++ b/src/controllers/otp_controller.js @@ -0,0 +1,77 @@ +import { Controller } from '@hotwired/stimulus' + +import Hellotext from '../hellotext' +import SubmissionsAPI from '../api/submissions' + +export default class extends Controller { + static values = { + submissionId: String, + } + + static targets = ['input', 'resendButton'] + + initialize() { + super.initialize() + + this.attempts = 0 + this.onInputChange = this.onInputChange.bind(this) + + this.throttleInterval = setInterval(() => { + this.attempts = 0 + }, 60000) + } + + connect() { + super.connect() + this.inputTarget.addEventListener('input', this.onInputChange) + } + + disconnect() { + clearInterval(this.throttleInterval) + this.inputTarget.removeEventListener('input', this.onInputChange) + + super.disconnect() + } + + async resend() { + if (this.throttled) { + return alert(Hellotext.business.locale.otp.throttled) + } + + this.resendButtonTarget.disabled = true + const response = await SubmissionsAPI.resendOTP(this.submissionIdValue) + + if (response.succeeded) { + this.resendButtonTarget.disabled = false + } + + alert(Hellotext.business.locale.otp.resend_successful) + this.attempts += 1 + } + + async onInputChange() { + if (this.inputTarget.value.length !== 6) return + + this.inputTarget.disabled = true + this.resendButtonTarget.disabled = true + + const response = await SubmissionsAPI.verifyOTP(this.submissionIdValue, this.inputTarget.value) + + if (response.succeeded) { + this.dispatch('verified', { + detail: { submissionId: this.submissionIdValue }, + }) + } else { + alert(Hellotext.business.locale.otp.invalid) + } + + this.inputTarget.disabled = false + this.resendButtonTarget.disabled = false + } + + // private + + get throttled() { + return this.attempts >= 3 + } +} diff --git a/src/core/configuration.js b/src/core/configuration.js new file mode 100644 index 0000000..41b782c --- /dev/null +++ b/src/core/configuration.js @@ -0,0 +1,16 @@ +class Configuration { + static apiRoot = 'https://api.hellotext.com/v1' + static autoGenerateSession = true + + static assign({ apiRoot, autoGenerateSession }) { + this.apiRoot = apiRoot || this.apiRoot + this.autoGenerateSession = autoGenerateSession + return this + } + + static endpoint(path) { + return `${this.apiRoot}/${path}` + } +} + +export { Configuration } diff --git a/src/core/event.js b/src/core/event.js new file mode 100644 index 0000000..f060c5b --- /dev/null +++ b/src/core/event.js @@ -0,0 +1,54 @@ +import { InvalidEvent } from '../errors' + +export default class Event { + static events = ['session-set', 'forms:collected', 'form:completed'] + + static valid(name) { + return Event.exists(name) + } + + static invalid(name) { + return !this.valid(name) + } + + static exists(name) { + return this.events.find(eventName => eventName === name) !== undefined + } + + constructor() { + this.subscribers = {} + } + + addSubscriber(eventName, callback) { + if (Event.invalid(eventName)) { + throw new InvalidEvent(eventName) + } + + this.subscribers = { + ...this.subscribers, + [eventName]: this.subscribers[eventName] + ? [...this.subscribers[eventName], callback] + : [callback], + } + } + + removeSubscriber(eventName, callback) { + if (Event.invalid(eventName)) { + throw new InvalidEvent(eventName) + } + + if (this.subscribers[eventName]) { + this.subscribers[eventName] = this.subscribers[eventName].filter(cb => cb !== callback) + } + } + + dispatch(eventName, data) { + this.subscribers[eventName]?.forEach(subscriber => { + subscriber(data) + }) + } + + get listeners() { + return Object.keys(this.subscribers).length !== 0 + } +} diff --git a/src/core/index.js b/src/core/index.js new file mode 100644 index 0000000..079826e --- /dev/null +++ b/src/core/index.js @@ -0,0 +1,2 @@ +export { default as Event } from './event' +export { Configuration } from './configuration' diff --git a/src/errors/index.js b/src/errors/index.js new file mode 100644 index 0000000..61d3216 --- /dev/null +++ b/src/errors/index.js @@ -0,0 +1,2 @@ +export { InvalidEvent } from './invalid_event' +export { NotInitializedError } from './not_initialized_error' diff --git a/src/errors/invalidEvent.js b/src/errors/invalid_event.js similarity index 100% rename from src/errors/invalidEvent.js rename to src/errors/invalid_event.js diff --git a/src/errors/notInitializedError.js b/src/errors/not_initialized_error.js similarity index 100% rename from src/errors/notInitializedError.js rename to src/errors/not_initialized_error.js diff --git a/src/event.js b/src/event.js deleted file mode 100644 index 62f67c4..0000000 --- a/src/event.js +++ /dev/null @@ -1,15 +0,0 @@ -export default class Event { - static events = ["session-set"] - - static valid(name) { - return Event.exists(name) - } - - static invalid(name) { - return !this.valid(name) - } - - static exists(name) { - return this.events.find((eventName) => eventName === name) !== undefined - } -} diff --git a/src/eventEmitter.js b/src/eventEmitter.js deleted file mode 100644 index 1cee928..0000000 --- a/src/eventEmitter.js +++ /dev/null @@ -1,28 +0,0 @@ -export default class EventEmitter { - constructor() { - this.subscribers = {} - } - - addSubscriber(eventName, callback) { - this.subscribers = { - ...this.subscribers, - [eventName]: this.subscribers[eventName] ? [...this.subscribers[eventName], callback] : [callback] - } - } - - removeSubscriber(eventName, callback) { - if(this.subscribers[eventName]) { - this.subscribers[eventName] = this.subscribers[eventName].filter((cb) => cb !== callback) - } - } - - emit(eventName, data) { - this.subscribers[eventName].forEach((subscriber) => { - subscriber(data) - }) - } - - get listeners() { - return Object.keys(this.subscribers).length !== 0 - } -} diff --git a/src/hellotext.js b/src/hellotext.js index 0f54d60..6c1a0f4 100644 --- a/src/hellotext.js +++ b/src/hellotext.js @@ -1,53 +1,55 @@ -import Event from "./event" -import EventEmitter from "./eventEmitter" -import Response from "./response"; -import Query from "./query"; +import { Event, Configuration } from './core' -import { NotInitializedError } from './errors/notInitializedError' -import { InvalidEvent } from "./errors/invalidEvent" +import API from './api' +import { Business, Query, Cookies, FormCollection } from './models' + +import { NotInitializedError } from './errors' /** * @typedef {Object} Config * @property {Boolean} autogenerateSession */ - class Hellotext { - static __apiURL = 'https://api.hellotext.com/v1/' - static #session - static #business static #config - static #eventEmitter = new EventEmitter() static #query + static eventEmitter = new Event() + static forms + static business + /** * initialize the module. * @param business public business id * @param { Config } config */ static initialize(business, config = { autogenerateSession: true }) { - this.#business = business - this.#config = config + this.business = new Business(business) + this.forms = new FormCollection() - this.#query = new Query(window.location.search) + this.#config = Configuration.assign(config) + this.#query = new Query() - if(this.#query.has("preview")) return + addEventListener('load', () => { + this.forms.collect() + }) - const session = this.#query.get("session") || this.#cookie + if (this.#query.inPreviewMode) return - if (session && session !== "undefined" && session !== "null") { - this.#session = session - this.#setSessionCookie() - } else if(config.autogenerateSession) { - this.#mintAnonymousSession() - .then(response => { - this.#session = response.id - this.#setSessionCookie() - }) + if (this.#query.session) { + this.#session = Cookies.set('hello_session', this.#query.session) + } else if (config.autogenerateSession) { + this.#mintAnonymousSession().then(response => { + this.#session = Cookies.set('hello_session', response.id) + }) } } + static setSession(value) { + this.#session = Cookies.set('hello_session', value) + } + /** * Tracks an action that has happened on the page * @@ -56,24 +58,19 @@ class Hellotext { * @returns {Promise} */ static async track(action, params = {}) { - if (this.#notInitialized) { throw new NotInitializedError() } - - if(this.#query.has("preview")) { - return new Response(true, { received: true }) + if (this.notInitialized) { + throw new NotInitializedError() } - const response = await fetch(this.__apiURL + 'track/events', { - headers: this.#headers, - method: 'post', - body: JSON.stringify({ + return await API.events.create({ + headers: this.headers, + body: { session: this.session, action, ...params, - url: (params && params.url) || window.location.href - }), + url: (params && params.url) || window.location.href, + }, }) - - return new Response(response.status === 200, await response.json()) } /** @@ -82,9 +79,7 @@ class Hellotext { * @param callback the callback. This method will be called with the payload */ static on(event, callback) { - if(Event.invalid(event)) { throw new InvalidEvent(event) } - - this.#eventEmitter.addSubscriber(event, callback) + this.eventEmitter.addSubscriber(event, callback) } /** @@ -93,9 +88,7 @@ class Hellotext { * @param callback the callback to remove */ static removeEventListener(event, callback) { - if(Event.invalid(event)) { throw new InvalidEvent(event) } - - this.#eventEmitter.removeSubscriber(event, callback) + this.eventEmitter.removeSubscriber(event, callback) } /** @@ -103,7 +96,9 @@ class Hellotext { * @returns {String} */ static get session() { - if (this.#notInitialized) { throw new NotInitializedError() } + if (this.notInitialized) { + throw new NotInitializedError() + } return this.#session } @@ -118,46 +113,29 @@ class Hellotext { // private - static get #notInitialized() { - return this.#business === undefined + static get notInitialized() { + return this.business.id === undefined } static async #mintAnonymousSession() { - if (this.#notInitialized) { throw new NotInitializedError() } - - const trackingUrl = this.__apiURL + 'track/sessions' - - this.mintingPromise = await fetch(trackingUrl, { - method: 'post', - headers: { Authorization: `Bearer ${this.#business}` }, - }) + if (this.notInitialized) { + throw new NotInitializedError() + } - return this.mintingPromise.json() + return API.sessions(this.business.id).create() } - static get #headers() { - if (this.#notInitialized) { throw new NotInitializedError() } + static get headers() { + if (this.notInitialized) { + throw new NotInitializedError() + } return { - Authorization: `Bearer ${this.#business}`, + Authorization: `Bearer ${this.business.id}`, Accept: 'application.json', 'Content-Type': 'application/json', } } - - static #setSessionCookie() { - if (this.#notInitialized) { throw new NotInitializedError() } - - if(this.#eventEmitter.listeners) { - this.#eventEmitter.emit("session-set", this.#session) - } - - document.cookie = `hello_session=${this.#session}` - } - - static get #cookie() { - return document.cookie.match('(^|;)\\s*' + 'hello_session' + '\\s*=\\s*([^;]+)')?.pop() - } } export default Hellotext diff --git a/src/index.js b/src/index.js index a8bf047..459eaee 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,13 @@ +import { Application } from '@hotwired/stimulus' import Hellotext from './hellotext' +import FormController from './controllers/form_controller' +import OTPController from './controllers/otp_controller' + +const application = Application.start() +application.register('hellotext--form', FormController) +application.register('hellotext--otp', OTPController) + +import '../styles/index.css' + export default Hellotext diff --git a/src/locales/en.js b/src/locales/en.js new file mode 100644 index 0000000..93cdde1 --- /dev/null +++ b/src/locales/en.js @@ -0,0 +1,13 @@ +export default { + otp: { + sent_to_email: 'A One-Time Password has been sent to your email address', + sent_to_phone: 'An One-Time Password has been sent to your phone number', + resend_successful: 'The One-Time Password has been resent successfully', + invalid: 'Invalid One-Time Password', + resend: 'Resend OTP', + throttled: 'You have reached the maximum number of attempts. Please try again in 1 minute.', + }, + white_label: { + powered_by: "Powered by", + } +} diff --git a/src/locales/es.js b/src/locales/es.js new file mode 100644 index 0000000..fb889d0 --- /dev/null +++ b/src/locales/es.js @@ -0,0 +1,13 @@ +export default { + otp: { + sent_to_email: 'Un código de un solo uso ha sido enviado a tu correo electrónico', + sent_to_phone: 'Un código de un solo uso ha sido enviado a tu número de teléfono', + resend_successful: 'El código de un solo uso ha sido reenviado exitosamente', + invalid: 'Código de un solo uso inválido', + resend: 'Reenviar OTP', + throttled: 'Has alcanzado el número máximo de intentos. Por favor intenta de nuevo en 1 minuto.', + }, + white_label: { + powered_by: "Desarrollado por", + } +} diff --git a/src/locales/index.js b/src/locales/index.js new file mode 100644 index 0000000..85bba9e --- /dev/null +++ b/src/locales/index.js @@ -0,0 +1,7 @@ +import en from './en' +import es from './es' + +export default { + en, + es, +} diff --git a/src/models/business.js b/src/models/business.js new file mode 100644 index 0000000..3482135 --- /dev/null +++ b/src/models/business.js @@ -0,0 +1,41 @@ +import API from '../api/businesses' + +import locales from '../locales' + +class Business { + constructor(id) { + this.id = id + this.data = {} + this.fetchPublicData() + } + + get subscription() { + return this.data.subscription + } + + get country() { + return this.data.country + } + + get enabledWhitelist() { + return this.data.whitelist !== 'disabled' + } + + get locale() { + return locales[this.data.locale] + } + + get features() { + return this.data.features + } + + // private + + fetchPublicData() { + API.get(this.id) + .then(response => response.json()) + .then(data => (this.data = data)) + } +} + +export { Business } diff --git a/src/models/cookies.js b/src/models/cookies.js new file mode 100644 index 0000000..306fc5a --- /dev/null +++ b/src/models/cookies.js @@ -0,0 +1,16 @@ +import Hellotext from '../hellotext' + +class Cookies { + static set(name, value) { + document.cookie = `${name}=${value}` + Hellotext.eventEmitter.dispatch('session-set', value) + + return value + } + + static get(name) { + return document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() + } +} + +export { Cookies } diff --git a/src/models/form.js b/src/models/form.js new file mode 100644 index 0000000..45d81e4 --- /dev/null +++ b/src/models/form.js @@ -0,0 +1,133 @@ +import Hellotext from '../hellotext' + +import { InputBuilder } from '../builders/input_builder' +import { LogoBuilder } from '../builders/logo_builder' + +class Form { + constructor(data, element = null) { + this.data = data + this.element = + element || + document.querySelector(`[data-hello-form="${this.id}"]`) || + document.createElement('form') + } + + async mount() { + const firstStep = this.data.steps[0] + + this.buildHeader(firstStep.header) + this.buildInputs(firstStep.inputs) + this.buildButton(firstStep.button) + this.buildFooter(firstStep.footer) + + this.elementAttributes.forEach(attribute => { + this.element.setAttribute(attribute.name, attribute.value) + }) + + if (!document.contains(this.element)) { + document.body.appendChild(this.element) + } + + if(!Hellotext.business.features.white_label) { + this.element.prepend(LogoBuilder.build()) + } + } + + buildHeader(header) { + const headerElement = this.#findOrCreateComponent('[data-form-header]', 'header') + headerElement.innerHTML = header.content + + if (this.element.querySelector('[data-form-header]')) { + this.element.querySelector('[data-form-header]').replaceWith(headerElement) + } else { + this.element.prepend(headerElement) + } + } + + buildInputs(inputs) { + const inputsContainerElement = this.#findOrCreateComponent('[data-form-inputs]', 'main') + const inputElements = inputs.map(input => InputBuilder.build(input)) + + inputElements.forEach(inputElement => inputsContainerElement.appendChild(inputElement)) + + if (this.element.querySelector('[data-form-inputs]')) { + this.element.querySelector('[data-form-inputs]').replaceWith(inputsContainerElement) + } else { + this.element + .querySelector('[data-form-header]') + .insertAdjacentHTML('afterend', inputsContainerElement.outerHTML) + } + } + + buildButton(button) { + const buttonElement = this.#findOrCreateComponent('[data-form-button]', 'button') + buttonElement.innerText = button.text + + buttonElement.setAttribute('data-action', 'click->hellotext--form#submit') + buttonElement.setAttribute('data-hellotext--form-target', 'button') + + if (this.element.querySelector('[data-form-button]')) { + this.element.querySelector('[data-form-button]').replaceWith(buttonElement) + } else { + this.element + .querySelector('[data-form-inputs]') + .insertAdjacentHTML('afterend', buttonElement.outerHTML) + } + } + + buildFooter(footer) { + const element = this.#findOrCreateComponent('[data-form-footer]', 'footer') + element.innerHTML = footer.content + + if (this.element.querySelector('[data-form-footer]')) { + this.element.querySelector('[data-form-footer]').replaceWith(element) + } else { + this.element.appendChild(element) + } + } + + markAsCompleted(data) { + localStorage.setItem(`hello-form-${this.id}`, 'completed') + Hellotext.eventEmitter.dispatch('form:completed', { id: this.id, ...data }) + } + + get id() { + return this.data.id + } + + get elementAttributes() { + return [ + { + name: 'data-controller', + value: 'hellotext--form', + }, + { + name: 'data-hello-form', + value: this.id, + }, + { + name: 'data-hellotext--form-data-value', + value: JSON.stringify(this.data), + }, + { + name: 'data-action', + value: 'hellotext--otp:verified->hellotext--form#completed', + }, + ] + } + + #findOrCreateComponent(selector, tag) { + const existingElement = this.element.querySelector(selector) + + if (existingElement) { + return existingElement.cloneNode(true) + } + + const createdElement = document.createElement(tag) + createdElement.setAttribute(selector.replace('[', '').replace(']', ''), '') + + return createdElement + } +} + +export { Form } diff --git a/src/models/form_collection.js b/src/models/form_collection.js new file mode 100644 index 0000000..c496571 --- /dev/null +++ b/src/models/form_collection.js @@ -0,0 +1,81 @@ +import Hellotext from '../hellotext.js' +import API from '../api' + +import { Form } from './form' + +import { NotInitializedError } from '../errors' + +class FormCollection { + constructor() { + this.forms = [] + + this.includes = this.includes.bind(this) + this.excludes = this.excludes.bind(this) + + this.add = this.add.bind(this) + } + + collect() { + if(Hellotext.notInitialized) { + throw new NotInitializedError() + } + + const formsIdsToFetch = this.#formIdsToFetch + if (formsIdsToFetch.length === 0) return + + const promises = formsIdsToFetch.map(id => { + return API.forms.get(id).then(response => response.json()) + }) + + if (!Hellotext.business.enabledWhitelist) { + console.warn( + 'No whitelist has been configured. It is advised to whitelist the domain to avoid bots from submitting forms.', + ) + } + + Promise.all(promises) + .then(forms => forms.forEach(this.add)) + .then(() => Hellotext.eventEmitter.dispatch('forms:collected', this)) + } + + forEach(callback) { + this.forms.forEach(callback) + } + + map(callback) { + return this.forms.map(callback) + } + + add(data) { + if (this.includes(data.id)) return + this.forms.push(new Form(data)) + } + + getById(id) { + return this.forms.find(form => form.id === id) + } + + getByIndex(index) { + return this.forms[index] + } + + includes(formId) { + return this.forms.some(form => form.id === formId) + } + + excludes(id) { + return !this.includes(id) + } + + get length() { + return this.forms.length + } + + get #formIdsToFetch() { + return Array.from(document.querySelectorAll('[data-hello-form]')) + .map(form => form.dataset.helloForm) + .filter(this.excludes) + } +} + +export { FormCollection } diff --git a/src/models/index.js b/src/models/index.js new file mode 100644 index 0000000..4cd6ff1 --- /dev/null +++ b/src/models/index.js @@ -0,0 +1,5 @@ +export { Business } from './business' +export { Form } from './form' +export { Query } from './query' +export { Cookies } from './cookies' +export { FormCollection } from './form_collection' diff --git a/src/models/query.js b/src/models/query.js new file mode 100644 index 0000000..72286b2 --- /dev/null +++ b/src/models/query.js @@ -0,0 +1,33 @@ +import { Cookies } from './cookies' + +class Query { + static get inPreviewMode() { + return new this().inPreviewMode + } + + constructor() { + this.urlSearchParams = new URLSearchParams(window.location.search) + } + + get(param) { + return this.urlSearchParams.get(this.toHellotextParam(param)) + } + + has(param) { + return this.urlSearchParams.has(this.toHellotextParam(param)) + } + + get inPreviewMode() { + return this.has('preview') + } + + get session() { + return this.get('session') || Cookies.get('hello_session') + } + + toHellotextParam(param) { + return `hello_${param}` + } +} + +export { Query } diff --git a/src/query.js b/src/query.js deleted file mode 100644 index 97f7276..0000000 --- a/src/query.js +++ /dev/null @@ -1,21 +0,0 @@ -export default class Query { - constructor(urlQueries) { - this.urlSearchParams = new URLSearchParams(urlQueries) - } - - get(param) { - return this.urlSearchParams.get( - this.toHellotextParam(param) - ) - } - - has(param) { - return this.urlSearchParams.has( - this.toHellotextParam(param) - ) - } - - toHellotextParam(param) { - return `hello_${param}` - } -} diff --git a/styles/index.css b/styles/index.css new file mode 100644 index 0000000..9b7cadb --- /dev/null +++ b/styles/index.css @@ -0,0 +1,32 @@ +form[data-hello-form] { + position: relative; + + article { + [data-error-container] { + display: none; + } + + &:has(input:invalid) { + [data-error-container] { + display: block; + } + } + } + + [data-logo-container] { + display: flex; + justify-content: center; + align-items: flex-end; + position: absolute; + right: 1rem; + bottom: 1rem; + + small { + margin: 0 .3rem; + } + + [data-hello-brand] { + width: 4rem; + } + } +} diff --git a/webpack.config.js b/webpack.config.js index db58570..5453f90 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,18 +1,24 @@ module.exports = { mode: 'production', - entry: ['whatwg-fetch', './lib/hellotext.js'], + entry: ['whatwg-fetch', '@hotwired/stimulus', './lib/index.js'], output: { - filename: 'hellotext.js' + filename: 'hellotext.js', }, module: { rules: [ { test: /\.m?js$/, exclude: [/node_modules/], - use: [{ - loader: 'babel-loader', - }] - } - ] - } -}; + use: [ + { + loader: 'babel-loader', + }, + ], + }, + { + test: /\.css$/i, + use: ['style-loader', 'css-loader'], + }, + ], + }, +} diff --git a/yarn.lock b/yarn.lock index 247cce8..c0d5892 100644 --- a/yarn.lock +++ b/yarn.lock @@ -968,6 +968,11 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@hotwired/stimulus@^3.0.0": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.2.tgz#071aab59c600fed95b97939e605ff261a4251608" + integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -2044,11 +2049,30 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +css-loader@^6.5.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba" + integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.33" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.5.4" + css.escape@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + cssom@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" @@ -2562,6 +2586,11 @@ iconv-lite@0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -3434,6 +3463,11 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3615,6 +3649,56 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== + +postcss-modules-local-by-default@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz#f1b9bd757a8edf4d8556e8d0f4f894260e3df78f" + integrity sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz#a43d28289a169ce2c15c00c4e64c0858e43457d5" + integrity sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz#49694cb4e7c649299fea510a29fa6577104bcf53" + integrity sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.33: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -3837,6 +3921,11 @@ semver@^7.3.5: dependencies: lru-cache "^6.0.0" +semver@^7.5.4: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + serialize-javascript@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" @@ -3892,6 +3981,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -3978,6 +4072,11 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +style-loader@^3.3.1: + version "3.3.4" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" + integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -4146,6 +4245,11 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + v8-to-istanbul@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4"