Patterns: --------- integer[required|positive|negative|min:a|max:b|range:a,b|not:n1,n2,n3|confirmed:inputName] float[required|positive|negative|min:a|max:b|range:a,b|not:n1,n2,n3|confirmed:inputName] string[required|of:alpha,num,underscore,dash,spaces,comma,dot |not_of:alpha,num,underscore,dash,spaces,comma,dot|min:a|max:b|range:a,b|len:x |not:n1,n2,n3|confirmed:inputName] timeline[required|format:~x~|min:a|max:b|range:a,b|confirmed:inputName] email[required|min:a|max:b|range:a,b|in_domain:x,y,z|not_in_domain:m,n,o|confirmed:inputName] url[required|min:a|max:b|range:a,b|in_domain:x,y,z|not_in_domain:m,n,o|confirmed:inputName] in[required|items:n1,n2,n3] check[required] file[required|ext:n1,n2,n3|min:a|max:b|range:a,b|s_min:a|s_max:b|s_range:a,b|len:n] regex[pattern:~exp~|min:a|max:b|range:a,b|len:x|confirmed:inputName] array[required|min:a|max:b|range:a,b|len:x] Extra rules for integer, float and timeline: -------------------------------------------- min_input:inputName,inOut,fn max_input:inputName,inOut,fn Error message order: -------------------- data-v-[patternName]-[ruleKey]-[config.locale] data-v-[patternName]-[ruleKey] spa.resource.get(patternName + '.' + ruleKey) table: ------ { id: '', class: '', attr: {k1: 'v1', k2: 'v2'}, columns: [ { id: '', class: '', attr: {k3: 'v3', k4: 'v4'}, data: '', }, ], } simple spa.setOptions() ----------------------- spa.setOptions({ locale: 'en', debug: false, timelineFormat: 'D-M-YYYY', trimValues: true, ajaxOnError: function (jqXHR) { //jqXHR, textStatus, errorThrown spa.dialog.messageError( spa.resource.get('ajax.errorTitle'), spa.resource.get('ajax.errorBody') + jqXHR.status); }, ajaxLaravelHeader: false, ajaxHeader: undefined, //for util.token tokenValue: $('#_token input').val(), tokenName: '__RequestVerificationToken', dialogCurrentDriver: 'alert', dialogDrivers: { alert: {}, sweetAlert: {}, //from services bootstrap3ModalService: bootstrap3ModalService, bootstrap3PanelService: bootstrap3PanelService, }, flashCurrentDriver: '', flashDrivers: { //from services spaNotifyBootstrap3: spaNotifyBootstrap3, spaNotifyRemark: spaNotifyRemark, }, /* 'silent' 'listOfErrors' 'dialog' 'dialog.alert' 'dialog.sweetAlert' 'flash' */ validationCurrentDriver: 'dialog', validationDrivers: { silent: {}, listOfErrors: {}, //from services bootstrap3ValidationDriver: bootstrap3ValidationDriver, bootstrap43HybridValidationDriver: bootstrap43HybridValidationDriver, }, reValidationDelay: 500, listOfErrorsSelector: '#spacer-listOfErrors', validationCustomRules: { 'min_input': {}, 'max_input': {}, }, validationPre: undefined, //validationPreGlobal(form, isSilent, exclude) validationPreGlobal: undefined, validationIsLite: false, //momentLocales without en: because en is loaded by default momentLocales: {}, datepickerOptions: {}, datatableOptions: {}, /* * options: * 1 => cookie * 2 => segment * 3 => query */ localeMethod: 2, localeQueryKey: 'lang', defaultLocale: 'en', //example var _html = $('html'); locale: _html.attr('lang'), localeMethod: _html.data('locale-method'), defaultLocale: _html.data('default-locale'), //end of example btnLoadingCurrentDriver: 'spin', btnLoadingDrivers: { spin: {}, } }); extend config: dialogDrivers: { sweetAlert: (function () { function iconMapper(icon) { if (icon === 'danger') return 'error'; return icon; } return { message: function (title, msg, status, icon, fn) { swal({ title: title, text: msg, icon: iconMapper(icon), button: spa.resource.get('btn.ok') }).then(function () { if (fn) fn(); }); }, confirm: function (title, msg, status, icon, fn1, fn2) { swal({ title: title, text: msg, icon: iconMapper(icon), dangerMode: true, buttons: [spa.resource.get('btn.no'), spa.resource.get('btn.yes')] }).then(function (value) { if (value) fn1(); else if (fn2) fn2(); }); }, prompt: function (title, msg, status, icon, fn1, fn2) { swal({ title: title, text: msg, icon: iconMapper(icon), content: "input", buttons: [spa.resource.get('btn.cancel'), spa.resource.get('btn.continue')] }).then(function (value) { if (value === null) { if (fn2) fn2(); } else fn1(value); }); } } })(), }, validationDrivers: { bootstrap43HybridValidationDriver: { onError: function (vo) { let invalidRules = vo.pattern.invalidRules; let helpBlock = vo.input.jquery.addClass('is-invalid').closest('[class*="form-group"]').addClass('has-error has-danger').find('.invalid-feedback'); if (helpBlock.length !== 0) { let html = ''; if (invalidRules.length === 1) html = invalidRules[0].message; else { html = ''; } helpBlock.html(html); } if (vo.confirmInput) { let found = false; for (let i = 0, l = invalidRules.length; i < l; i++) if (invalidRules[i].type === spa.ruleTypes.confirmed) { found = true; break; } if (found) vo.confirmInput.jquery.addClass('is-invalid'); } }, clearError: function (inputJq) { inputJq.removeClass('is-invalid') .closest('[class*="form-group"]') .removeClass('has-error has-danger') .find('.invalid-feedback') .html(''); //confirm input let ci = inputJq.data('_spa-ci'); if (ci) ci.removeClass('is-invalid'); } } }, btnLoadingDrivers: { spin: (function () { function set(btn) { btn = sis(btn); if (btn.length) { btn.data(Tags.loadingHtml, btn.html()); //fix the width //btn.css('min-width', btn.css('width')); //btn.html(spa.resource.get('icon.loading')); //spa.dom.setDisable(btn, true); return btn; } } function unset(btn, html) { btn = sis(btn); if (btn.length) { btn.html(html ? html : btn.data(Tags.loadingHtml)); //spa.dom.setDisable(btn, false); //btn.css('min-width', 'auto'); return btn; } } return { set, unset, } })(), }, validationCustomRules: { ruleName: { validate: function (vo, index) { var rule = vo.pattern.rules[index]; //return true or false }, getErrorMessage: function (vo, index) { var rule = vo.invalidRules[index]; //return string }, }, } var spa = { dialogDefaults, patternTypes, ruleTypes, tags, cachingTags, setOptions(o) setDialogCD(d) setValidationCD(d) setFlashCD(d) setBtnLoadingCD(d) getLocale() setLocale(l) isLocale(l) isDebug() getTimelineFormat() dialog: { message(title, msg, status, icon, fn) confirm(title, msg, status, icon, fn1, fn2) prompt(title, msg, status, icon, fn1, fn2, defaultValue) messageSuccess(title, msg, fn) messageError(title, msg, fn) confirmDelete(fn, title, msg) /* * pos: top-left, top-center, top-right * pos: bottom-left, bottom-center, bottom-right */ flash(title, message, status, icon, timeout, position) flashSuccess(title, message, icon, timeout, position) flashError(title, message, icon, timeout, position) }, validation: { getErrorList() validate(form, isSilent, exclude), resetValidation(form), isValidInput(input, isSilent), //value validators isInteger(value) isFloat(value) isEmail(value) isUrl(value) isPositiveNumber(value) isNegativeNumber(value) isStringOf(value, patternsArray) isStringContainsOneOfRegex(value, patternsArray) isStringContainsOneOfArray(value, array, caseSensitive) isStringIn(value, array, caseSensitive) isTimeline(value, format) isTimelineBeforeOrEqual(value, boundary, format) isTimelineAfterOrEqual(value, boundary, format) isTimelineBefore(value, boundary, format) isTimelineAfter(value, boundary, format) isTimelineBetween(value, min, max, format, boundaries) isRegMatch(value, reg) isFunction(value) isArray(value) }, ajax: { request(verb, url, data, fn, isUpload, options, button) post(url, data, fn, button) get(url, data, fn, button) delete(url, data, fn, button) put(url, data, fn, button) postBtn(button, url, data, fn) getBtn(button, url, data, fn) deleteBtn(button, url, data, fn) putBtn(button, url, data, fn) }, array: { insertLast(array, item) insertFirst(array, item) insertAt(array, index, item) removeLast(array) removeFirst(array) removeAt(array, index) removeWhere(array, fn) searchGetBool(array, fn) searchGetIndex(array, fn) searchGetItem(array, fn) foreach(enumerable, fn) foreachReversed(array, fn) toString(array) copy(array) filter(array, fn) map(array, fn) wrap(array) }, dom: { click(btn, fn) getFriendlyName(input) setDisable(item, status) setLoadingButton(btn) unsetLoadingButton(btn, html) //option is {id: '', class: '', attr: {k1: v1, k2, v2}} addHtmlAttr(jquery, option) }, form: { submitAjax(form, submitter, validate, fn) submit(form, submitter, validate) registerNormalSubmitter(form, submitter, validate) registerAjaxSubmitter(form, submitter, validate, fn) getFormInputsAsObject(form) }, input: { getValue(input) getFiles(input) setValue(input, value) select: { getSelectedOption(select) getSelectedValue(select) getSelectedText(select) getSelectedIndex(select) setSelectedIndex(select, index) setSelectedValue(select, value) setFirstOptionSelected(select) clearOptions(select) addOptions(select, options, removeCurrent, firstOptionLabel) addOption(select, option, isSelected) }, checkable: { isChecked(input) set(input, status) getRadioGroupValue(name) getCheckboxGroupArray(name) } }, resource: { //replace: {pa1: 'val2', pa2: 'val2'} get(key, replace) //{key: '', en: '', ar: ''} set(resource) setArray(resourceArray) //replace: {pa1: 'val2', pa2: 'val2'} replace(str, _replace) }, table: { /* * row { * id: '', * class: '', * attr: {k1: 'v1', k2: 'v2'}, * columns: [ * { * id: '', * class: '', * attr: {k3: 'v3', k4: 'v4'}, * data: '', * }, * ], * } */ addRows(table, rows) removeRows(table, rows) removeChecked(table, inputName) removeAll(table) moveRows(from, to, rows) moveChecked(from, to, inputName) count(table, rows) updatePaginationTotal(value, rowCountElement) }, util: { randomString(length, num, small, capital) debounce(delay, fn) timer(interval, fn) cancelTimer(id) executeLater(delay, fn) cancelExecuteLater(id) getTimelineDiff(timeline1, timeline2, format) formatDate(date, format) token: { addToObj(obj) addToForm(form) } }, web: { reload(ReloadFromServer) redirect(url) newTab(url) getUrl(url) pathHas(key, path) getUrlWithoutQueryString(url) getPath(url) getPathWithoutQueryString(url) getQueryStringAsObject(url) getQueryString(name, url) updateQueryString(obj, url) removeQueryString(key, url) localize(url, locale, method) urlSegments(index, url) }, init: { datepicker(input, options) datepickerOnChange(s, fn) select2(input, options) dataTable(table, options) createCounter(input, counter, max) autoComplete(input, url, extraData) showMsg(selector, dataAttribute, context) deleteAndRedirect(btnSelector, deleteUrl, redirectUrl, title, msg) listDelete(btnSelector, idAttribute, url, tableSelector, title, msg) } };