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 = '
';
for (let i = 0, l = invalidRules.length; i < l; i++)
html = html + '- ' + invalidRules[i].message + '
';
html = 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)
}
};