Core.UI = Core.UI || {};
Core.UI.Bind = class extends Core.Singleton {
/**
* Get class name.
* @return {string}
*/
static getClassName() {
return 'Core.UI.Bind';
}
/**
* Add a method that is called anytime a bind is called. Used to add functionality
* to other projects.
* @param {callable} method
*/
static addBindMethod(method) {
if (!this.bindMethods) {
this.bindMethods = [];
}
this.bindMethods.push(method);
}
/**
* @param {string} id
* @param {object} parameters
* @param {jQuery} parameters.$container
* @param {jQuery} parameters.$popup
* @param {object} parameters.buttons
* @param {object} parameters.events
* @param {object} parameters.instance
*/
init(id, parameters) {
this.id = id;
this.parameters = this.parameters || {};
if (!parameters.instance) {
parameters.instance = Page.activePage;
}
for (const name in parameters) {
if (parameters[name]) {
this.parameters[name] = parameters[name];
}
}
this.bindElements();
this.bindButtons();
this.bindEvents();
if (Core.UI.Bind.bindMethods && !parameters.skipExtraMethods) {
for (let i = 0; i < Core.UI.Bind.bindMethods.length; i++) {
parameters.skipExtraMethods = true;
Core.UI.Bind.bindMethods[i](this.parameters.$container, parameters);
}
}
return true;
}
/**
* Reinit container bind, if applicable.
* @param {string} id
* @param {object} parameters
*/
reinit(id, parameters) {
return this.init(id, parameters);
}
/**
* Execute event.
* @param {object} e
* @param {jQuery} $element
* @returns
*/
executeEvent(e, $element) {
if (!e || e.isDefaultPrevented()) {
return;
}
if (!this.parameters.events[e.type]) {
return;
}
let filtered = this.parameters.events[e.type].filter($element);
if (filtered === null) {
console.error('Filter returned null, missing return key?');
}
if (!filtered) {
return;
}
this.parameters.events[e.type].action($element);
e.preventDefault();
e.stopPropagation();
}
/**
* Bind misc events.
*/
bindEvents() {
if (!this.parameters.events) {
return;
}
let me = this;
this.parameters.$container
.off(
Object.keys(this.parameters.events).join(' '),
'*',
function (e) {
me.executeEvent(e, $(this));
}
)
.on(
Object.keys(this.parameters.events).join(' '),
'*',
function (e) {
me.executeEvent(e, $(this));
}
);
}
/**
* Execute button click.
* @param {jQuery} $button
* @param {string} className
*/
executeButtonClick($button, className) {
let data = {};
if (this.parameters.$popup) {
let $form = this.parameters.$popup.find('form');
if ($form.exists()) {
for (let name in $form.form('get values')) {
data[name] = $form.form('get value', name);
}
}
}
for (let name in $button.data()) {
if (name == 'modulePopup' || name == 'module_popup') {
continue;
}
data[_.snakeCase(name)] = $button.data(name);
}
delete data.parameters;
this.parameters.buttons[className].call($button, data, $button);
if (this.parameters.$popup) {
$('body').trigger('click');
}
}
/**
* Bind container actions.
*/
bindButtons() {
let me = this;
this.parameters.$container
.off(
'click mouseover',
'.action.button'
)
.on(
'click mouseover',
'.action.button',
function (e) {
let $button = $(this);
let on = 'hover';
if ($button.data('on')) {
on = $button.data('on');
}
if ($button.hasClass('inactive')) {
on = 'hover';
}
if (on === 'click' && e.type == 'click') {
for (let className in me.parameters.buttons) {
if ($button.hasClass(className)) {
e.preventDefault();
e.stopPropagation();
me.executeButtonClick($button, className);
$('body').popup('hide all');
return;
}
}
}
if ($button.data('html')) {
if ($button.popup('exists')) {
return;
}
$button.popup({
on: $button.data('popup-on') || on,
offset: 5,
onShow: function () {
if ($button.hasClass('inactive')) {
return true;
}
let $popup = $(this);
console.log(Core.Singleton.references);
console.log(me.parameters);
Core.UI.Bind.get(
$popup,
Object.assign(
me.parameters,
{
$container: $popup,
$popup: $popup
}
)
);
if (Page.keys.control) {
//$popup.find('.action.button').first().trigger('click');
//return false;
}
return true;
},
onVisible: function () {
if ($button.hasClass('inactive')) {
return true;
}
let $popup = $(this);
let $form = $popup.find('form');
if ($form.isVisible()) {
$form.rebind('submit', function (e) {
e.preventDefault();
$popup.find('.action.button').first().trigger('click');
return false;
});
}
},
onHidden: function () {
$(this).popup('destroy');
}
});
if ((on === 'hover' || $button.data('popup-on') === 'hover') && e.type === 'mouseover') {
$button.popup('show');
} else if (on === 'click' && e.type === 'click') {
$button.popup('show');
}
}
}
);
}
/**
* Bind form input changes.
*/
bindInputWatches() {
let me = this;
this.parameters.$container.find('input, textarea').each(function () {
let $input = $(this);
$input.data(
'old-val',
($input.isCheckbox() ? $input.isChecked() * 1 : $input.val())
);
$input
.off('focus blur')
.on(
'focus blur',
function(e) {
let $input = $(this);
if (e.type == 'focus') {
if (typeof $input.data('value') !== 'undefined') {
$input.val($input.data('value'));
}
} else if (e.type == 'blur' && typeof $input.data('value') !== 'undefined') {
if (typeof $input.data('new-val') !== 'undefined') {
setTimeout(
() => $input.val($input.data('new-val')),
100
);
} else if (typeof $input.data('old-val') !== 'undefined') {
setTimeout(
() => $input.val($input.data('old-val')),
100
);
}
}
}
);
});
this.parameters.$container
.off(
'change',
'input, textarea'
)
.on(
'change',
'input, textarea',
function (e, postSave) {
e.stopPropagation();
let $input = $(this);
if (!$input.data('uri')) {
if ($input.isCheckbox()) {
me.toggleFields();
}
return;
}
let data = {
id: $input.data('id'),
fields: {},
attributes: {},
error_return_code: 2,
configuration: Page.getParameters()
};
for (let name in $input.data()) {
if (name == 'modulePopup' || name == 'module_popup') {
continue;
}
if ($.isFunction($input.data(name))) {
continue;
}
data.attributes[_.snakeCase(name)] = $input.data(name);
}
data.fields[$input.attr('name')] = ($input.isCheckbox() ? $input.isChecked() * 1 : $input.val());
$input.data('new-val', data.fields[$input.attr('name')]);
if ($input.isCheckbox() && $input.data('values')) {
data.fields[$input.attr('name')] = $input.data('values')[$input.isChecked() * 1];
}
$input.data('value', $input.val());
if ($input.data('call-before-change')) {
let call = $input.data('call-before-change');
if ($.isFunction(call)) {
call($input);
} else {
eval(call);
}
}
let savingToast = $('body').toast({
displayTime: 0,
message: 'Saving...',
closeOnClick: false,
transition: {
showMethod: 'fade',
showDuration: 3000,
}
});
Core.API.call({
url: `PUT ${$input.data('uri')}`,
data: data,
callback: (data) => {
try {
savingToast.toast('close');
} catch (err) {
savingToast.toast({ displayTime: 100 });
}
if (data.return == 2) {
if (!$input.isCheckbox()) {
$input[0].select();
if ($input.attr('type') != 'number' && $input.attr('type') != 'hidden') {
$input.selectRange(0, 99999);
}
}
document.execCommand('copy');
$('body').toast({
displayTime: 10000,
showProgress: 'top',
classProgress: 'red',
className: {
toast: 'ui message'
},
closeIcon: true,
title: 'Failed to ' + _.startCase($input.attr('name')).toLowerCase() + '.',
message: (data.message ? data.message + '
(What you tried to save was copied to the clipboard.)' : ''),
closeOnClick: false,
class: 'red'
});
if ($input.isCheckbox()) {
if ($input.data('old-val') == 1) {
$input.parent().checkbox('set checked');
} else {
$input.parent().checkbox('set unchecked');
}
$input.data('new-val', ($input.isCheckbox() ? $input.isChecked() * 1 : $input.val()));
} else {
$input.val($input.data('old-val'));
$input.data('new-val', $input.data('old-val'));
if ($input.parent().parent().hasClass('calendar')) {
$input.parent().parent().calendar('set date', $input.data('old-val'), false, false);
}
if ($input.parent().hasClass('dropdown')) {
$input.parent().dropdown('set selected', $input.data('old-val'));
}
}
} else {
$input.data('old-val', ($input.isCheckbox() ? $input.isChecked() * 1 : $input.val()));
if ($input.isCheckbox()) {
me.toggleFields();
}
}
if ($.isFunction(postSave)) {
postSave();
}
if ($input.data('call-after-change')) {
let call = $input.data('call-after-change');
if ($.isFunction(call)) {
call($input, data);
} else {
eval(call);
}
}
if ($input.data('refresh-on-change')) {
new RefreshOnChange($input.data('refresh-on-change')).process();
}
},
$context: me.parameters.$container,
fadeParameters: {
hide: true
}
});
}
);
}
/**
* Toggle fields on checkbox change.
*/
toggleFields() {
let forms = {};
this.parameters.$container.find('form').each(function () {
let $form = $(this);
let classes = [];
$form[0].classList.forEach(function (className) {
classes.push(className);
});
classes.splice(0, 2);
forms[classes[0]] = $form;
});
for (let formName in forms) {
let $form = forms[formName];
for (let fieldName in $form.form('get values')) {
let $input = null;
try {
$input = $form.form('get field', fieldName);
} catch (e) {
continue;
}
if ($input.prop('type') == 'checkbox') {
let current = Boolean($input.is(':checked'));
if ($input.data('toggle-field')) {
if ($input.data('toggle-direction') == 'inverted') {
if (current) {
$form.form().find($input.data('toggle-field')).slideUp();
} else {
$form.form().find($input.data('toggle-field')).slideDown();
}
} else {
if (current) {
$form.form().find($input.data('toggle-field')).slideDown();
} else {
$form.form().find($input.data('toggle-field')).slideUp();
}
}
}
}
}
}
}
/**
* Bind all the basic UI elements.
*/
bindElements() {
if (Core.UI.LockWidget) {
Core.UI.LockWidget.init(this.parameters.$container);
}
this.parameters.$container.find('.ui.accordion').accordion();
this.parameters.$container.find('.ui.checkbox').checkbox();
this.parameters.$container.find('.ui.slider').each(function() {
let $slider = $(this);
let saveTimer = null;
$slider.slider({
min: $slider.data('min'),
max: $slider.data('max'),
start: $slider.data('start'),
end: $slider.data('end'),
step: $slider.data('step'),
showLabelTicks: true,
onChange: function() {
if (saveTimer) {
clearTimeout(saveTimer);
}
saveTimer = setTimeout(
() => {
$slider.parent().find(`input[name="${$slider.attr('id')}"]`).trigger('change');
saveTimer = null;
},
500
);
},
onMove: function() {
}
});
});
this.parameters.$container.find('.ui.dropdown').each(function () {
let $dropdown = $(this);
let parameters = {
fullTextSearch: true,
ignoreSearchCase: true,
ignoreDiacritics: true,
on: $dropdown.data('on') || 'click',
action: $dropdown.data('action') || 'activate'
};
if ($dropdown.data('allow-additions')) {
parameters.allowAdditions = true;
parameters.hideAdditions = false;
parameters.forceSelection = false;
}
$dropdown.dropdown(parameters);
});
if ($.timeago) {
$.timeago.settings.allowFuture = true;
this.parameters.$container.find('time.timeago').timeago();
}
this.parameters.$container.find('input.calendar').each(function () {
let $input = $(this);
if ($input.parent().hasClass('field')) {
$input.replaceWith(`