1024 lines
31 KiB
JavaScript
1024 lines
31 KiB
JavaScript
/**
|
|
*
|
|
* A lightweight wizard UI component that supports accessibility and HTML5 in JavaScript Vanilla.
|
|
*
|
|
* @link https://github.com/AdrianVillamayor/Wizard-JS
|
|
* @author Adrii[https://github.com/AdrianVillamayor]
|
|
*
|
|
* @class Wizard
|
|
*/
|
|
|
|
class Wizard1 {
|
|
|
|
constructor(args) {
|
|
|
|
let opts = {
|
|
"wz_class": "",
|
|
"wz_nav": ".wizard-nav",
|
|
"wz_ori": ".horizontal",
|
|
"wz_nav_style": "dots",
|
|
"wz_content": ".wizard-content",
|
|
"wz_buttons": ".wizard-buttons",
|
|
"wz_button": ".wizard-btn",
|
|
"wz_button_style": ".btn",
|
|
"wz_step": ".wizard-step",
|
|
"wz_form": ".wizard-form",
|
|
"wz_next": ".next",
|
|
"wz_prev": ".prev",
|
|
"wz_finish": ".finish",
|
|
"wz_highlight": ".highlight",
|
|
|
|
"nav": true,
|
|
"buttons": true,
|
|
"highlight": true,
|
|
|
|
"current_step": 0,
|
|
"steps": 0,
|
|
"highlight_time": 1000,
|
|
"navigation": "all",
|
|
"next": "Next",
|
|
"prev": "Prev",
|
|
"finish": "Submit",
|
|
|
|
"highlight_type": { "error": "error", "warning": "warning", "success": "success", "info": "info" },
|
|
|
|
"i18n": {
|
|
"empty_wz": "No item has been found with which to generate the Wizard.",
|
|
"empty_nav": "Nav does not exist or is empty.",
|
|
"empty_content": "Content does not exist or is empty.",
|
|
"empty_html": "Undefined or null content cannot be added.",
|
|
"empty_update": "Nothing to update.",
|
|
"no_nav": "Both the nav and the buttons are disabled, there is no navigation system.",
|
|
"form_validation": "One or more of the form fields are invalid.",
|
|
"diff_steps": "Discordance between the steps of nav and content.",
|
|
"random": "There has been a problem, check the configuration and use of the wizard.",
|
|
"already_definded": "This item is already defined",
|
|
"title": "Step"
|
|
}
|
|
};
|
|
|
|
this.prefabOpts(opts, args);
|
|
|
|
this.wz_class = this.options.wz_class;
|
|
this.wz_active = "active";
|
|
this.wz_ori = this.options.wz_ori;
|
|
this.wz_nav = this.options.wz_nav;
|
|
this.wz_nav_style = this.options.wz_nav_style;
|
|
this.wz_content = this.options.wz_content;
|
|
this.wz_buttons = this.options.wz_buttons;
|
|
this.wz_button = this.options.wz_button;
|
|
this.wz_button_style = this.options.wz_button_style;
|
|
this.wz_step = this.options.wz_step;
|
|
this.wz_form = this.options.wz_form;
|
|
this.wz_next = this.options.wz_next;
|
|
this.wz_prev = this.options.wz_prev;
|
|
this.wz_finish = this.options.wz_finish;
|
|
this.wz_highlight = this.options.wz_highlight;
|
|
|
|
this.buttons = this.options.buttons;
|
|
this.nav = this.options.nav;
|
|
this.highlight = this.options.highlight;
|
|
|
|
this.highlight_time = this.options.highlight_time;
|
|
this.highlight_type = this.options.highlight_type;
|
|
|
|
this.steps = this.options.steps;
|
|
this.current_step = this.options.current_step;
|
|
this.last_step = this.current_step;
|
|
this.navigation = this.options.navigation;
|
|
this.prev = this.options.prev;
|
|
this.next = this.options.next;
|
|
this.finish = this.options.finish;
|
|
this.form = false;
|
|
this.locked = false;
|
|
this.locked_step = null;
|
|
}
|
|
|
|
/**
|
|
* Initializes the wizard
|
|
*
|
|
* @customevent wz.ready - Indicates that wizard has loaded and is accessible.
|
|
* @property {object} target - wz_class
|
|
* @property {object} elem - DOM element
|
|
*
|
|
* @throws empty_wz - Not found any element to generate the Wizard with.
|
|
* @throws empty_nav - Nav does not exist or is empty.
|
|
* @throws empty_content - Content does not exist or is empty.
|
|
* @throws diff_steps - Discordance between the steps of nav and content.
|
|
* @throws random - There has been a problem check the configuration and use of the wizard.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
init() {
|
|
try {
|
|
const wz_check = ($_.exists(document.querySelector(this.wz_class))) ? document.querySelector(this.wz_class) : $_.throwException(this.options.i18n.empty_wz);
|
|
|
|
if ($_.str2bool(wz_check.getAttribute("data-wizard")) && wz_check.getAttribute("data-wizard") === this.wz_active) {
|
|
console.warn(`${this.wz_class} : ${this.options.i18n.already_definded}`);
|
|
return false;
|
|
}
|
|
|
|
$_.cleanEvents(document.querySelector(this.wz_class), true);
|
|
|
|
const wz = ($_.exists(document.querySelector(this.wz_class))) ? document.querySelector(this.wz_class) : $_.throwException(this.options.i18n.empty_wz);
|
|
|
|
if ($_.str2bool(this.buttons) === false && $_.str2bool(this.nav) === false) {
|
|
console.warn(this.options.i18n.no_nav);
|
|
}
|
|
|
|
wz.classList.add((this.wz_ori).replace(".", ""));
|
|
|
|
if (wz.tagName === "FORM") {
|
|
this.form = true;
|
|
}
|
|
|
|
this.check2Prepare(wz)
|
|
|
|
switch (this.navigation) {
|
|
case "all":
|
|
case "nav":
|
|
this.setNavEvent()
|
|
this.setBtnEvent()
|
|
|
|
break;
|
|
case "buttons":
|
|
this.setBtnEvent();
|
|
break;
|
|
}
|
|
|
|
wz.style.display = ($_.hasClass(wz, "vertical")) ? "flex" : "block";
|
|
|
|
wz.setAttribute("data-wizard", this.wz_active)
|
|
|
|
document.dispatchEvent(new CustomEvent("wz.ready", {
|
|
detail: {
|
|
"target": this.wz_class,
|
|
"elem": document.querySelector(this.wz_class)
|
|
}
|
|
}));
|
|
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check and update each section of the wizard.
|
|
*
|
|
* @customevent wz.update - Indicates that wizard has updaded and is accessible.
|
|
* @property {object} target - wz_class
|
|
* @property {object} elem - DOM element
|
|
*
|
|
* @throws empty_wz - Not found any element to generate the Wizard with.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
update() {
|
|
const wz = ($_.exists(document.querySelector(this.wz_class))) ? document.querySelector(this.wz_class) : $_.throwException(this.options.i18n.empty_wz);
|
|
|
|
if (($_.str2bool(wz.getAttribute("data-wizard")) === false) && wz.getAttribute("data-wizard") != this.wz_active) {
|
|
$_.throwException(this.options.i18n.empty_wz);
|
|
}
|
|
|
|
this.check2Prepare(wz)
|
|
|
|
this.content_update = false;
|
|
|
|
document.querySelector(this.wz_class).dispatchEvent(new CustomEvent("wz.update", {
|
|
detail: {
|
|
"target": this.wz_class,
|
|
"elem": document.querySelector(this.wz_class)
|
|
}
|
|
}));
|
|
|
|
}
|
|
|
|
/**
|
|
* Restart the wizard
|
|
*
|
|
* @event wz.reset
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
reset() {
|
|
this.setCurrentStep(0);
|
|
|
|
const wz = document.querySelector(this.wz_class);
|
|
const nav = wz.querySelector(this.wz_nav);
|
|
const content = wz.querySelector(this.wz_content);
|
|
|
|
if ($_.str2bool(this.buttons) !== false) {
|
|
let buttons = wz.querySelector(this.wz_buttons);
|
|
|
|
let next = buttons.querySelector(this.wz_button + this.wz_next);
|
|
let prev = buttons.querySelector(this.wz_button + this.wz_prev);
|
|
let finish = buttons.querySelector(this.wz_button + this.wz_finish);
|
|
|
|
this.checkButtons(next, prev, finish)
|
|
}
|
|
|
|
let $wz_nav = nav.querySelectorAll(this.wz_step);
|
|
$_.removeClassList($wz_nav, "active");
|
|
|
|
let $wz_content = content.querySelectorAll(this.wz_step);
|
|
$_.removeClassList($wz_content, "active");
|
|
|
|
nav.querySelector(`${this.wz_step}[data-step="${this.getCurrentStep()}"]`).classList.add("active");
|
|
content.querySelector(`${this.wz_step}[data-step="${this.getCurrentStep()}"]`).classList.add("active");
|
|
|
|
wz.dispatchEvent(new Event("wz.reset"));
|
|
}
|
|
|
|
/**
|
|
* Locks the wizard in the active step
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
lock() {
|
|
this.locked = true;
|
|
this.locked_step = this.getCurrentStep();
|
|
}
|
|
|
|
/**
|
|
* Unlock wizard
|
|
*
|
|
* @event wz.unlock
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
unlock() {
|
|
this.locked = false;
|
|
this.locked_step = null;
|
|
|
|
document.querySelector(this.wz_class).dispatchEvent(new Event("wz.unlock"));
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate the steps and define a standard for each step.
|
|
*
|
|
* @param {object} $wz_nav - Nav element
|
|
* @param {object} $wz_nav_steps - Steps elements inside Nav
|
|
* @param {object} $wz_content_steps - Steps elements inside Content
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
prefabSteps($wz_content_steps, $wz_nav, $wz_nav_steps) {
|
|
let active_index = this.getCurrentStep();
|
|
|
|
for (let i = 0; i < $wz_content_steps.length; i++) {
|
|
let $this = $wz_content_steps[i];
|
|
|
|
$this.setAttribute("data-step", i);
|
|
|
|
if ($_.str2bool(this.nav) !== false) {
|
|
$wz_nav_steps[i].setAttribute("data-step", i);
|
|
}
|
|
};
|
|
|
|
if ($_.str2bool(this.nav) !== false) {
|
|
$_.removeClassList($wz_nav_steps, "active");
|
|
$wz_nav_steps[active_index].classList.add("active");
|
|
$wz_nav.classList.add(this.wz_nav_style);
|
|
}
|
|
|
|
$_.removeClassList($wz_content_steps, "active");
|
|
$wz_content_steps[active_index].classList.add("active");
|
|
|
|
// if (this.form) this.update2Form();
|
|
|
|
this.setButtons();
|
|
}
|
|
|
|
/**
|
|
* Adds the form tag and converts the wizard into a <form>
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
update2Form() {
|
|
let wz = document.querySelector(this.wz_class);
|
|
let wz_content = wz.querySelector(this.wz_content);
|
|
|
|
if (wz_content.tagName !== "FORM") {
|
|
let wz_content_class = wz_content.getAttribute("class");
|
|
let wz_content_content = wz_content.innerHTML
|
|
|
|
wz_content.remove();
|
|
const $form = document.createElement("form");
|
|
|
|
$form.setAttribute("method", "POST");
|
|
$form.setAttribute("class", wz_content_class + " " + (this.wz_form).replace(".", ""));
|
|
|
|
$form.innerHTML = wz_content_content;
|
|
|
|
wz.appendChild($form)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks and validates each input/select/textarea of the active step.
|
|
* If the step has no inputs, the checks will be ignored.
|
|
*
|
|
* @throws random - There has been a problem check the configuration and use of the wizard.
|
|
*
|
|
* @return {boolean} - If everything is OK, it returns false
|
|
*/
|
|
|
|
checkForm() {
|
|
let wz = document.querySelector(this.wz_class);
|
|
let wz_content = wz.querySelector(this.wz_content);
|
|
|
|
let steps = wz_content.querySelectorAll(this.wz_step);
|
|
let target = steps[this.getCurrentStep()];
|
|
let validation = false;
|
|
|
|
let inputs = target.querySelectorAll("input,textarea,select");
|
|
|
|
if (inputs.length > 0) {
|
|
validation = this.formValidator(wz_content, inputs);
|
|
}
|
|
|
|
return validation;
|
|
}
|
|
|
|
/**
|
|
* Generating, styling and shaping the Nav
|
|
*
|
|
* @param {object} $wz - Wizard element
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
setNav($wz) {
|
|
let wz_nav = $wz.querySelector(this.wz_nav);
|
|
|
|
if ($_.exists(wz_nav) !== false && $_.str2bool(this.nav) !== false) {
|
|
wz_nav.remove()
|
|
wz_nav = $wz.querySelector(this.wz_nav);
|
|
}
|
|
|
|
if ($_.exists(wz_nav) === false && $_.str2bool(this.nav) !== false) {
|
|
|
|
let wz_content = $wz.querySelector(this.wz_content);
|
|
let steps = wz_content.querySelectorAll(this.wz_step);
|
|
|
|
const nav = document.createElement("ASIDE");
|
|
nav.classList.add((this.wz_nav).replace(".", ""));
|
|
|
|
const wz_content_steps = wz_content.querySelectorAll(this.wz_step);
|
|
const steps_length = wz_content_steps.length;
|
|
|
|
for (let i = 0; i < steps_length; i++) {
|
|
const nav_step = document.createElement("DIV");
|
|
let title = (steps[i].hasAttribute("data-title")) ? steps[i].getAttribute("data-title") : `${this.options.i18n.title} ${i}`;
|
|
nav_step.classList.add((this.wz_step).replace(".", ""));
|
|
|
|
if (this.navigation === "buttons") nav_step.classList.add("nav-buttons");
|
|
|
|
const dot = document.createElement("SPAN");
|
|
dot.classList.add('dot');
|
|
nav_step.appendChild(dot);
|
|
|
|
const span = document.createElement("SPAN");
|
|
span.innerHTML = title;
|
|
nav_step.appendChild(span);
|
|
|
|
nav.appendChild(nav_step);
|
|
}
|
|
|
|
$wz.prepend(nav);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generating, styling and shaping Buttons
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
setButtons() {
|
|
let wz = document.querySelector(this.wz_class);
|
|
let wz_btns = wz.querySelector(this.wz_buttons);;
|
|
|
|
|
|
if ($_.exists(wz_btns) !== false && $_.str2bool(this.buttons) !== false) {
|
|
wz_btns.remove()
|
|
wz_btns = wz.querySelector(this.wz_buttons);;
|
|
}
|
|
|
|
if ($_.exists(wz_btns) === false && $_.str2bool(this.buttons) !== false) {
|
|
const buttons = document.createElement("ASIDE");
|
|
buttons.classList.add((this.wz_buttons).replace(".", ""));
|
|
|
|
let btn_style = (this.wz_button_style).replaceAll(".", "");
|
|
btn_style = btn_style.split(" ");
|
|
|
|
const prev = document.createElement("BUTTON");
|
|
prev.innerHTML = this.prev;
|
|
prev.classList.add((this.wz_button).replace(".", ""));
|
|
prev.classList.add(...btn_style);
|
|
prev.classList.add((this.wz_prev).replace(".", ""));
|
|
|
|
if (this.navigation === "nav") prev.style.display = "none";
|
|
|
|
buttons.appendChild(prev);
|
|
|
|
const next = document.createElement("BUTTON");
|
|
next.innerHTML = this.next;
|
|
next.classList.add((this.wz_button).replace(".", ""));
|
|
next.classList.add(...btn_style);
|
|
next.classList.add((this.wz_next).replace(".", ""));
|
|
|
|
if (this.navigation === "nav") next.style.display = "none";
|
|
|
|
buttons.appendChild(next);
|
|
|
|
const finish = document.createElement("BUTTON");
|
|
finish.innerHTML = this.finish;
|
|
finish.classList.add((this.wz_button).replace(".", ""));
|
|
finish.classList.add(...btn_style);
|
|
finish.classList.add((this.wz_finish).replace(".", ""));
|
|
buttons.appendChild(finish);
|
|
|
|
this.checkButtons(next, prev, finish)
|
|
|
|
wz.appendChild(buttons);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generating, styling and shaping Buttons
|
|
*
|
|
* @param {object} next - Next button element
|
|
* @param {object} prev - Prev button element
|
|
* @param {object} finish - Finish button element
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
checkButtons(next, prev, finish) {
|
|
let current_step = this.getCurrentStep();
|
|
let n_steps = this.steps - 1;
|
|
|
|
if (current_step == 0) {
|
|
prev.setAttribute("disabled", true);
|
|
} else {
|
|
prev.removeAttribute("disabled");
|
|
}
|
|
|
|
if (current_step == n_steps) {
|
|
next.setAttribute("disabled", true);
|
|
finish.style.display = "block";
|
|
} else {
|
|
finish.style.display = "none";
|
|
next.removeAttribute("disabled");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Common function for wizard checks and prefab.
|
|
*
|
|
* @param {object} wz - Wizard element
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
check2Prepare(wz) {
|
|
|
|
this.setNav(wz);
|
|
|
|
const wz_content = ($_.exists(wz.querySelector(this.wz_content))) ? wz.querySelector(this.wz_content) : $_.throwException(this.options.i18n.empty_content);
|
|
const wz_content_steps = wz_content.querySelectorAll(this.wz_step);
|
|
const wz_content_steps_length = (wz_content_steps.length > 0) ? wz_content_steps.length : $_.throwException(this.options.i18n.empty_content);
|
|
|
|
let wz_nav = undefined;
|
|
let wz_nav_steps = undefined;
|
|
|
|
if ($_.str2bool(this.nav) !== false) {
|
|
wz_nav = ($_.exists(wz.querySelector(this.wz_nav))) ? wz.querySelector(this.wz_nav) : $_.throwException(this.options.i18n.empty_nav);
|
|
wz_nav_steps = wz_nav.querySelectorAll(this.wz_step);
|
|
const wz_nav_steps_length = (wz_nav_steps.length > 0) ? wz_nav_steps.length : $_.throwException(this.options.i18n.empty_nav);
|
|
|
|
if (wz_nav_steps_length != wz_content_steps_length) {
|
|
$_.throwException(this.options.i18n.diff_steps);
|
|
}
|
|
}
|
|
|
|
this.steps = wz_content_steps_length;
|
|
|
|
this.prefabSteps(wz_content_steps, wz_nav, wz_nav_steps);
|
|
}
|
|
|
|
/**
|
|
* Click event handler for Buttons and Nav.
|
|
*
|
|
* @param {object} e - Event
|
|
*
|
|
* @event wz.btn.prev - In case the wizard goes backwards, the wz.btn.prev event will be fired.
|
|
* @event wz.btn.next - In case the wizard advances, the nextWizard event will be fired.
|
|
* @event wz.nav.forward - In case of moving forward with the navbar, the forwardNavWizard event will be fired.
|
|
* @event wz.nav.backward - In case of moving backward with the navbar, the backwardNavWizard event will be fired.
|
|
* @event wz.lock - In case it is blocked, it will fire the wz.lock event.
|
|
*
|
|
* @customevent wz.error - If the form is not correctly filled in, the wz.error event will be fired.
|
|
* @property {string} id - Eror id
|
|
* @property {string} msg - Error message
|
|
* @property {object} target - Contains all the elements that have given error
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
onClick(e) {
|
|
const $this = e
|
|
const wz = document.querySelector(this.wz_class);
|
|
|
|
if (this.locked && this.locked_step === this.getCurrentStep()) {
|
|
wz.dispatchEvent(new Event("wz.lock"));
|
|
return false;
|
|
}
|
|
|
|
const parent = $_.getParent($this, this.wz_class);
|
|
|
|
const nav = parent.querySelector(this.wz_nav);
|
|
const content = parent.querySelector(this.wz_content);
|
|
|
|
const is_btn = ($_.hasClass($this, this.wz_button));
|
|
const is_nav = ($_.hasClass($this, this.wz_step));
|
|
|
|
|
|
let step = ($_.str2bool($this.getAttribute("data-step")) !== false) ? parseInt($this.getAttribute("data-step")) : this.getCurrentStep();
|
|
|
|
if (is_btn) {
|
|
if ($_.hasClass($this, this.wz_prev)) {
|
|
step = step - 1;
|
|
wz.dispatchEvent(new Event("wz.btn.prev"));
|
|
} else if ($_.hasClass($this, this.wz_next)) {
|
|
step = step + 1;
|
|
wz.dispatchEvent(new Event("wz.btn.next"));
|
|
}
|
|
}
|
|
|
|
let step_action = step > this.getCurrentStep()
|
|
|
|
if (is_nav) {
|
|
if (step_action) {
|
|
wz.dispatchEvent(new Event("wz.nav.forward"));
|
|
} else if (step < this.getCurrentStep()) {
|
|
wz.dispatchEvent(new Event("wz.nav.backward"));
|
|
}
|
|
}
|
|
|
|
if (this.form && this.navigation != "buttons") {
|
|
if (step_action) {
|
|
if ((step !== this.getCurrentStep() + 1)) {
|
|
if (step >= this.last_step) {
|
|
step = this.last_step;
|
|
} else {
|
|
step = this.getCurrentStep() + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.form) {
|
|
const check_form = this.checkForm()
|
|
if (check_form.error === true) {
|
|
|
|
if (step_action) {
|
|
wz.dispatchEvent(new CustomEvent("wz.error", {
|
|
detail: {
|
|
"id": "form_validaton",
|
|
"msg": this.options.i18n.form_validation,
|
|
"target": check_form.target
|
|
}
|
|
}));
|
|
}
|
|
|
|
this.last_step = this.getCurrentStep();
|
|
if (this.getCurrentStep() < step) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($_.str2bool(step)) {
|
|
this.setCurrentStep(step)
|
|
}
|
|
|
|
if ($_.str2bool(this.buttons) !== false) {
|
|
const buttons = parent.querySelector(this.wz_buttons);
|
|
const next = buttons.querySelector(this.wz_button + this.wz_next);;
|
|
const prev = buttons.querySelector(this.wz_button + this.wz_prev);
|
|
const finish = buttons.querySelector(this.wz_button + this.wz_finish);
|
|
|
|
this.checkButtons(next, prev, finish)
|
|
}
|
|
|
|
if ($_.str2bool(this.nav) !== false) {
|
|
const $wz_nav = nav.querySelectorAll(this.wz_step);
|
|
$_.removeClassList($wz_nav, "active");
|
|
nav.querySelector(`${this.wz_step}[data-step="${this.getCurrentStep()}"]`).classList.add("active");
|
|
}
|
|
|
|
const $wz_content = content.querySelectorAll(this.wz_step);
|
|
$_.removeClassList($wz_content, "active");
|
|
content.querySelector(`${this.wz_step}[data-step="${this.getCurrentStep()}"]`).classList.add("active");
|
|
}
|
|
|
|
|
|
/**
|
|
* Notifies that the wizard has been completed.
|
|
*
|
|
* @param {object} e - Event
|
|
*
|
|
* @event wz.form.submit - If the wizard is a form it will fire submitWizard
|
|
* @event wz.end - If the wizard is not a form it will fire endWizard
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
onClickFinish(e) {
|
|
if (this.form) {
|
|
const check_form = this.checkForm()
|
|
if (check_form.error !== true) {
|
|
document.querySelector(this.wz_class).dispatchEvent(new Event("wz.form.submit"));
|
|
}
|
|
} else {
|
|
document.querySelector(this.wz_class).dispatchEvent(new Event("wz.end"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the active step
|
|
*
|
|
* @param {int} step - The active step
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
setCurrentStep(step) {
|
|
this.current_step = this.setStep(step);
|
|
}
|
|
|
|
/**
|
|
* Return the active step
|
|
*
|
|
* @return {int} The active step
|
|
*/
|
|
|
|
getCurrentStep() {
|
|
return this.current_step;
|
|
}
|
|
|
|
/**
|
|
* Check and match the steps of the wizard.
|
|
*
|
|
* @param {int} step - Step
|
|
*
|
|
* @return {int} Step
|
|
*/
|
|
|
|
setStep(step) {
|
|
let parent = document.querySelector(this.wz_class);
|
|
let content = parent.querySelector(this.wz_content);
|
|
|
|
let check_content = content.querySelector(`${this.wz_step}[data-step="${step}"]`);
|
|
|
|
if ($_.exists(check_content) === false) {
|
|
let content_length = (content.querySelectorAll(this.wz_step).length) - 1;
|
|
|
|
let diff = $_.closetNubmer(content_length, step)
|
|
|
|
step = diff;
|
|
}
|
|
|
|
this.last_step = (step > this.last_step) ? step : this.last_step;
|
|
|
|
return parseInt(step);
|
|
}
|
|
|
|
/**
|
|
* Set Nav events
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
setNavEvent() {
|
|
let _self = this;
|
|
let el = document.querySelector(this.wz_class);
|
|
|
|
$_.delegate(el, "click", `${this.wz_nav} ${this.wz_step}`, function (event) {
|
|
event.preventDefault()
|
|
_self.onClick(this)
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Set Button events
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
setBtnEvent() {
|
|
let _self = this;
|
|
let el = document.querySelector(this.wz_class);
|
|
|
|
$_.delegate(el, "click", `${this.wz_buttons} ${this.wz_button}`, function clickFunction(event) {
|
|
event.preventDefault()
|
|
|
|
if ($_.hasClass(event.target, _self.wz_finish)) {
|
|
_self.onClickFinish(this)
|
|
} else {
|
|
_self.onClick(this)
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Set options of wizard
|
|
*
|
|
* @param {object} options - List of options to build the wizard
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
set_options(options) {
|
|
this.options = options;
|
|
}
|
|
|
|
/**
|
|
* Check and match the options of the wizard with args definieds
|
|
*
|
|
* @param {object} options - List of options to build the wizard
|
|
* @param {object} args - List of arguments modifying the base options
|
|
*
|
|
* @return {void}
|
|
*/
|
|
|
|
prefabOpts(options, args) {
|
|
Object.entries(args).forEach(([key, value]) => {
|
|
if (typeof value === 'object') {
|
|
Object.entries(value).forEach(([key_1, value_1]) => {
|
|
options[key][key_1] = value_1;
|
|
});
|
|
} else {
|
|
options[key] = value;
|
|
}
|
|
});
|
|
|
|
this.set_options(options);
|
|
}
|
|
|
|
/**
|
|
* Checks the fields of the active step, in case there is an error it generates a highlight.
|
|
* Returns an array with all the fields that have given error.
|
|
*
|
|
* @param {object} wz_content - Active wizard content
|
|
* @param {object} formData - All inputs, textarea and select of the active content
|
|
*
|
|
* @return {object}
|
|
* @property {bool} error - There is an error or not
|
|
* @property {array} target - Contains all the elements that have given error
|
|
*/
|
|
|
|
formValidator(wz_content, formData) {
|
|
|
|
let error = false;
|
|
let target = [];
|
|
|
|
for (let e of formData) {
|
|
if ($_.hasClass(e, "required") || $_.exists(e.getAttribute("required"))) {
|
|
|
|
let check = false
|
|
switch (e.tagName) {
|
|
case "INPUT":
|
|
check = $_.dispatchInput(wz_content, e);
|
|
break;
|
|
case "SELECT":
|
|
case "TEXTAREA":
|
|
check = $_.isEmpty(e.value);
|
|
break;
|
|
}
|
|
|
|
if (check === false) {
|
|
error = true;
|
|
target.push(e);
|
|
|
|
if ($_.str2bool(this.highlight) === true) {
|
|
$_.highlight(e, this.wz_highlight, this.highlight_type['error'], this.highlight_time);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
"error": error,
|
|
"target": target
|
|
};
|
|
}
|
|
};
|
|
|
|
|
|
const $_ = {
|
|
hasClass: function (e, className) {
|
|
className = className.replace(".", "");
|
|
return new RegExp('(\\s|^)' + className + '(\\s|$)').test(e.className);
|
|
},
|
|
|
|
getParent: function (elem, selector) {
|
|
let parent = undefined;
|
|
|
|
while (elem.parentNode.tagName !== "BODY" && parent === undefined) {
|
|
elem = elem.parentNode;
|
|
|
|
if (elem.matches(selector)) {
|
|
parent = elem;
|
|
}
|
|
}
|
|
|
|
return parent;
|
|
},
|
|
|
|
delegate: function (el, evt, sel, handler) {
|
|
el.addEventListener(evt, function (event) {
|
|
let t = event.target;
|
|
while (t && t !== this) {
|
|
if (t.matches(sel)) {
|
|
handler.call(t, event);
|
|
}
|
|
t = t.parentNode;
|
|
}
|
|
});
|
|
},
|
|
|
|
removeClassList: function (e, className) {
|
|
for (let element of e) {
|
|
element.classList.remove(className);
|
|
};
|
|
},
|
|
|
|
objToString: function (obj, delimiter = ";") {
|
|
let str = '';
|
|
for (const p in obj) {
|
|
if (obj.hasOwnProperty(p)) {
|
|
str += p + ':' + obj[p] + delimiter;
|
|
}
|
|
}
|
|
return str;
|
|
},
|
|
|
|
isHidden: function (el) {
|
|
const style = window.getComputedStyle(el);
|
|
return (style.display === 'none')
|
|
},
|
|
|
|
str2bool: function (value) {
|
|
const str = String(value);
|
|
switch (str.toLowerCase()) {
|
|
case 'false':
|
|
case 'no':
|
|
case 'n':
|
|
case '':
|
|
case 'null':
|
|
case 'undefined':
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
},
|
|
|
|
exists: function (element) {
|
|
return (typeof (element) != 'undefined' && element != null);
|
|
},
|
|
|
|
throwException: function (message) {
|
|
let err;
|
|
try {
|
|
throw new Error('wz.error');
|
|
} catch (e) {
|
|
err = e;
|
|
}
|
|
if (!err) return;
|
|
|
|
let aux = err.stack.split("\n");
|
|
aux.splice(0, 2); //removing the line that we force to generate the error (var err = new Error();) from the message
|
|
aux = aux.join('\n"');
|
|
throw message + ' \n' + aux;
|
|
},
|
|
|
|
closetNubmer: function (length, step) {
|
|
let counts = [];
|
|
|
|
for (let index = 0; index <= length; index++) {
|
|
counts.push(index)
|
|
}
|
|
|
|
let closet = counts.reduce(function (prev, curr) {
|
|
return (Math.abs(curr - step) < Math.abs(prev - step) ? curr : prev);
|
|
});
|
|
|
|
return closet;
|
|
},
|
|
|
|
highlight: function (e, highlight_class, highlight_type, highlight_time) {
|
|
let target = highlight_class.replace(".", "");
|
|
let classHigh = `${target}-${highlight_type}`;
|
|
|
|
e.classList.add(classHigh)
|
|
|
|
setTimeout(function () {
|
|
document.querySelectorAll(`[class*="${target}"]`)
|
|
.forEach((el) => {
|
|
for (let i = el.classList.length - 1; i >= 0; i--) {
|
|
let className = el.classList[i];
|
|
if (className.startsWith(`${target}`)) {
|
|
el.classList.remove(className);
|
|
}
|
|
}
|
|
});
|
|
}, highlight_time);
|
|
},
|
|
|
|
dispatchInput: function (wz_content, e) {
|
|
let type = e.getAttribute("type");
|
|
let check = false;
|
|
|
|
switch (type) {
|
|
case "email":
|
|
check = $_.isEmail(e.value);
|
|
break;
|
|
case "url":
|
|
check = $_.isValidURL(e.value);
|
|
break;
|
|
case "checkbox":
|
|
let name = e.getAttribute("name");
|
|
|
|
if (name.includes("[]")) {
|
|
checkbox_check = wz_content.querySelectorAll(`input[type="checkbox"][name="${e.getAttribute("name")}"]:checked`);
|
|
check = (checkbox_check.length > 0);
|
|
} else {
|
|
check = e.checked;
|
|
}
|
|
|
|
break;
|
|
|
|
case "radio":
|
|
let radio_check = wz_content.querySelectorAll(`input[type="radio"][name="${e.getAttribute("name")}"]:checked`);
|
|
check = (radio_check.length > 0);
|
|
break;
|
|
|
|
default:
|
|
check = $_.isEmpty(e.value);
|
|
break;
|
|
}
|
|
|
|
return check
|
|
},
|
|
|
|
isEmpty: function (val) {
|
|
val = val.trim();
|
|
return (val != undefined && val != null && val.length > 0);
|
|
},
|
|
|
|
isEmail: function (email) {
|
|
const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
return regex.test(email);
|
|
},
|
|
|
|
isValidURL: function (str) {
|
|
let url;
|
|
|
|
try {
|
|
url = new URL(str);
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
|
|
return url.protocol === "http:" || url.protocol === "https:";
|
|
},
|
|
|
|
cleanEvents: function (el, withChildren = false) {
|
|
if ($_.exists(el)) {
|
|
if (withChildren) {
|
|
el.parentNode.replaceChild(el.cloneNode(true), el);
|
|
} else {
|
|
const newEl = el.cloneNode(false);
|
|
while (el.hasChildNodes()) newEl.appendChild(el.firstChild);
|
|
el.parentNode.replaceChild(newEl, el);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|