src/Embed.js
import CDN from './CDN.js';
export class Formio {
static baseUrl;
static projectUrl;
static language;
static config = {};
static cdn = null;
static proxy = true;
static version = 'FORMIO_VERSION';
static async setBaseUrl(url) {
Formio.baseUrl = url;
}
static async setProjectUrl(url) {
Formio.projectUrl = url;
}
static debug(...args) {
if (Formio.config.debug) {
console.log(...args);
}
}
static clearCache() {
if (Formio.FormioClass) {
Formio.FormioClass.clearCache();
}
}
static global(prop) {
const globalValue = window[prop];
if (globalValue && globalValue.proxy) {
return null;
}
Formio.debug(`Getting global ${prop}`, globalValue);
return globalValue;
}
static createElement(type, attrs, children) {
const element = document.createElement(type);
Object.keys(attrs).forEach(key => {
element.setAttribute(key, attrs[key]);
});
(children || []).forEach(child => {
element.appendChild(Formio.createElement(child.tag, child.attrs, child.children));
});
return element;
}
static async addScript(wrapper, src, name) {
if (!src) {
return Promise.resolve();
}
if (typeof src !== 'string' && src.length) {
return Promise.all(src.map(ref => Formio.addScript(wrapper, ref)));
}
if (name && Formio.global(name)) {
Formio.debug(`${name} already loaded.`);
return Promise.resolve(Formio.global(name));
}
Formio.debug('Adding Script', src);
wrapper.appendChild(Formio.createElement('script', {
src
}));
if (!name) {
return Promise.resolve();
}
return new Promise((resolve) => {
Formio.debug(`Waiting to load ${name}`);
const wait = setInterval(() => {
if (Formio.global(name)) {
clearInterval(wait);
Formio.debug(`${name} loaded.`);
resolve(Formio.global(name));
}
}, 100);
});
}
static async addStyles(wrapper, href) {
if (!href) {
return;
}
if (typeof href !== 'string' && href.length) {
href.forEach(ref => Formio.addStyles(wrapper, ref));
return;
}
Formio.debug('Adding Styles', href);
wrapper.appendChild(Formio.createElement('link', {
rel: 'stylesheet',
href
}));
}
static async submitDone(instance, submission) {
Formio.debug('Submision Complete', submission);
const successMessage = (Formio.config.success || '').toString();
if (successMessage && successMessage.toLowerCase() !== 'false' && instance.element) {
instance.element.innerHTML = `<div class="alert-success" role="alert">${successMessage}</div>`;
}
let returnUrl = Formio.config.redirect;
// Allow form based configuration for return url.
if (
!returnUrl &&
(
instance._form &&
instance._form.settings &&
(
instance._form.settings.returnUrl ||
instance._form.settings.redirect
)
)
) {
Formio.debug('Return url found in form configuration');
returnUrl = instance._form.settings.returnUrl || instance._form.settings.redirect;
}
if (returnUrl) {
const formSrc = instance.formio ? instance.formio.formUrl : '';
const hasQuery = !!returnUrl.match(/\?/);
const isOrigin = returnUrl.indexOf(location.origin) === 0;
returnUrl += hasQuery ? '&' : '?';
returnUrl += `sub=${submission._id}`;
if (!isOrigin && formSrc) {
returnUrl += `&form=${encodeURIComponent(formSrc)}`;
}
Formio.debug('Return URL', returnUrl);
window.location.href = returnUrl;
if (isOrigin) {
window.location.reload();
}
}
}
// Return the full script if the builder is being used.
static formioScript(script, builder) {
if (Formio.fullAdded || builder) {
Formio.fullAdded = true;
return script.replace('formio.form', 'formio.full');
}
return script;
}
// eslint-disable-next-line max-statements
static async init(element, builder = false) {
Formio.cdn = new CDN(Formio.config.cdn);
Formio.config.libs = Formio.config.libs || {
uswds: {
fa: true,
js: `${Formio.cdn.uswds}/uswds.min.js`,
css: `${Formio.cdn.uswds}/uswds.min.css`,
},
fontawesome: {
css: `${Formio.cdn['font-awesome']}/css/font-awesome.min.css`
},
bootstrap: {
css: `${Formio.cdn.bootstrap}/css/bootstrap.min.css`
}
};
const id = Formio.config.id || `formio-${Math.random().toString(36).substring(7)}`;
// Create a new wrapper and add the element inside of a new wrapper.
const wrapper = Formio.createElement('div', {
'id': `"${id}-wrapper"`
});
element.parentNode.insertBefore(wrapper, element);
element.parentNode.removeChild(element);
wrapper.appendChild(element);
// Load the renderer styles.
await Formio.addStyles(wrapper, Formio.config.embedCSS || `${Formio.cdn.js}/formio.embed.css`);
// Add a loader.
wrapper.appendChild(Formio.createElement('div', {
'class': 'formio-loader'
}, [{
tag: 'div',
attrs: {
class: 'loader-wrapper'
},
children: [{
tag: 'div',
attrs: {
class: 'loader text-center'
}
}]
}]));
Formio.FormioClass = await Formio.addScript(
wrapper,
Formio.formioScript(Formio.config.script || `${Formio.cdn.js}/formio.form.min.js`, builder),
'Formio'
);
Formio.FormioClass.setBaseUrl(Formio.baseUrl || Formio.config.base);
Formio.FormioClass.setProjectUrl(Formio.projectUrl || Formio.config.project);
Formio.FormioClass.language = Formio.language;
// Add premium modules
if (Formio.global('premium')) {
Formio.debug('Using premium module.');
Formio.FormioClass.use(Formio.global('premium'));
}
if (Formio.global('vpat')) {
Formio.debug('Using vpat module.');
Formio.FormioClass.use(Formio.global('vpat'));
}
if (Formio.config.template) {
if (Formio.config.includeLibs) {
await Formio.addStyles(wrapper, Formio.config.libs[Formio.config.template].css);
await Formio.addScript(wrapper, Formio.config.libs[Formio.config.template].js);
if (Formio.config.libs[Formio.config.template].fa) {
await Formio.addStyles(wrapper, Formio.config.libs.fontawesome.css);
}
}
if (Formio.cdn[Formio.config.template]) {
const templateSrc = `${Formio.cdn[Formio.config.template]}/${Formio.config.template}.min`;
await Formio.addStyles(wrapper, `${templateSrc}.css`);
Formio.debug(`Using ${Formio.config.template}`);
Formio.FormioClass.use(await Formio.addScript(wrapper, `${templateSrc}.js`, Formio.config.template));
}
}
else if (Formio.global('uswds')) {
Formio.debug('Using uswds module.');
Formio.FormioClass.use(Formio.global('uswds'));
}
// Default bootstrap + fontawesome.
else if (Formio.config.includeLibs) {
await Formio.addStyles(wrapper, Formio.config.libs.fontawesome.css);
await Formio.addStyles(wrapper, Formio.config.libs.bootstrap.css);
}
if (Formio.config.premium) {
await Formio.addStyles(wrapper, Formio.config.premium.css);
Formio.debug('Using premium');
Formio.FormioClass.use(await Formio.addScript(wrapper, Formio.config.premium.js, 'premium'));
}
await Formio.addStyles(wrapper, Formio.formioScript(Formio.config.style || `${Formio.cdn.js}/formio.form.min.css`, builder));
if (Formio.config.before) {
await Formio.config.before(Formio.FormioClass, element, Formio.config);
}
Formio.FormioClass.license = true;
return wrapper;
}
static async createForm(element, form, options) {
const wrapper = await Formio.init(element);
return Formio.FormioClass.createForm(element, form, {
...options,
...{ noLoader: true }
}).then((instance) => {
Formio.debug('Form created', instance);
// Remove the loader.
Formio.debug('Removing loader');
wrapper.removeChild(wrapper.querySelector('.formio-loader'));
// Set the default submission data.
if (Formio.config.submission) {
Formio.debug('Setting submission', Formio.config.submission);
instance.submission = Formio.config.submission;
}
// Allow them to provide additional configs.
Formio.debug('Triggering embed event');
Formio.FormioClass.events.emit('formEmbedded', instance);
// Trigger the after handler.
if (Formio.config.after) {
Formio.debug('Calling ready callback');
Formio.config.after(instance, Formio.config);
}
return instance;
});
}
static async builder(element, form, options) {
const wrapper = await Formio.init(element, true);
return Formio.FormioClass.builder(element, form, options).then((instance) => {
Formio.debug('Builder created', instance);
Formio.debug('Removing loader');
wrapper.removeChild(wrapper.querySelector('.formio-loader'));
Formio.debug('Triggering embed event');
Formio.FormioClass.events.emit('builderEmbedded', instance);
if (Formio.config.after) {
Formio.debug('Calling ready callback');
Formio.config.after(instance, Formio.config);
}
return instance;
});
}
}
export class Form {
constructor(element, form, options) {
this.form = form;
this.element = element;
this.options = options || {};
this.init();
this.instance = {
proxy: true,
ready: this.ready,
destroy: () => {}
};
}
init() {
this.element.innerHTML = '';
this.ready = this.create().then((instance) => {
this.instance = instance;
this.form = instance.form;
return instance;
});
}
create() {
return Formio.createForm(this.element, this.form, this.options);
}
setDisplay(display) {
if (this.instance.proxy) {
return this.ready;
}
this.form.display = display;
this.init();
return this.ready;
}
}
export class FormBuilder extends Form {
create() {
return Formio.builder(this.element, this.form, this.options);
}
}
Formio.Form = Form;
Formio.FormBuilder = FormBuilder;