artabro/wire/modules/Page/PageFrontEdit/PageFrontEdit.js

463 lines
12 KiB
JavaScript
Raw Permalink Normal View History

2024-08-27 11:35:37 +02:00
function PageFrontEditInit($) {
var buttons = $('.pw-edit-buttons'); // wrapper for fixed position edit buttons
var ckeditors = {}; // instances of ckeditor
var tinymces = {}; // instances of tinymce
var isTouch = (('ontouchstart' in window) || (navigator.MaxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));
var busy = false;
/**
* Set whether or not system is busy (processing is occurring)
*
* @param bool value
*
*/
function setBusy(value) {
busy = value;
if(busy) {
$('body').addClass('pw-busy');
} else {
$('body').removeClass('pw-busy');
}
}
/**
* Load a CSS files
*
* @param string file URL to the file
*
*/
function loadCSS(file) {
$('<link/>', {
rel: 'stylesheet',
type: 'text/css',
href: file
}).appendTo('head');
}
/**
* Handler for ckeditor blur and change events
*
*/
function ckeBlurEvent(event) {
var editor = event.editor;
if(editor.checkDirty()) {
// value changed
var el = $(editor.element.$);
if(el.length) {
el.closest(".pw-edit").addClass('pw-changed');
// console.log('cke-changed');
}
}
};
/**
* Event when editable area is double clicked or touched
*
*/
function inlineEditEvent(e, t, orig, copy) {
if(t.hasClass('pw-editing') || busy) return;
var copyID = copy.attr('id');
var name = t.attr('data-name');
//if(e.target.nodeName == 'A') return; // don't interfere with links
t.addClass('pw-editing pw-edited');
if(!copy.data('prev')) copy.data('prev', copy.html());
orig.hide();
copy.show();
buttons.show();
// init ckeditor, if used for this field
if(t.hasClass('pw-edit-InputfieldCKEditor') && typeof CKEDITOR !== 'undefined') {
if(typeof ckeditors[copyID] == "undefined") {
var editor = CKEDITOR.inline(copyID, ProcessWire.config['InputfieldCKEditor_' + name]);
ckeditors[copyID] = editor;
editor.on('blur', function(e) {
t.removeClass('pw-editing');
ckeBlurEvent(e);
});
editor.on('change', ckeBlurEvent);
}
} else if(t.hasClass('pw-edit-InputfieldTinyMCE')) {
if(typeof tinymces[copyID] === 'undefined') {
InputfieldTinyMCE.init('#' + copyID, 'PageFrontEdit');
var editor = tinymce.get(copyID);
tinymces[copyID] = editor;
editor.on('dirty change', function(e) {
t.addClass('pw-changed');
});
}
}
setTimeout(function() {
copy.trigger('focus');
}, 250);
};
/**
* Initialize an editable region
*
* @param t The editable region with class "pw-edit"
*
*/
function inlineInitEditableRegion(t) {
var orig = t.children('.pw-edit-orig');
var copy = t.children('.pw-edit-copy');
var name = t.attr('data-name');
copy.hide();
orig.show();
if(isTouch) orig.on('pwdoubletap', function(e) {
inlineEditEvent(e, t, orig, copy);
return false;
});
orig.on('dblclick', function(e) {
inlineEditEvent(e, t, orig, copy);
return false;
});
if(t.is('span')) { // single-line text
// via @canrau
copy.on('keydown', function(e) {
if(e.keyCode == 13){
e.preventDefault();
$(this).trigger('blur');
}
});
}
// check if orig has clickable links within it
// if so, differentiate between single and double clicks
// so that we can enable those links to still work while also supporting dblclick
if(orig.find('a').length) {
var clicks = 0, timer = null, allowClick = false;
orig.on('click', 'a', function() {
var $a = jQuery(this);
if(allowClick) {
allowClick = false;
return true;
}
clicks++;
if(clicks === 1) {
timer = setTimeout(function() {
clicks = 0;
allowClick = true;
$a[0].click(); // JS, not jQuery click() event
return true;
}, 700);
} else {
clearTimeout(timer); // prevent single-click action
allowClick = false;
clicks = 0;
orig.trigger('dblclick');
}
return false;
});
orig.on('dblclick', 'a', function() {
return false;
});
}
// handler for non-cke/mce blur event
if(!t.hasClass('pw-edit-InputfieldCKEditor') && !t.hasClass('pw-edit-InputfieldTinyMCE')) {
copy.on('blur', function() {
var copy = $(this);
var t = copy.closest('.pw-editing');
if(t.length == 0) return;
if(copy.html() != copy.data('prev')) {
t.addClass('pw-changed');
// console.log('changed');
}
t.removeClass('pw-editing');
});
}
}
/**
* Cancel all pending changes and restore original values
*
*/
function inlineAbandonAllChanges() {
$('.pw-edited').each(function() {
var t = $(this);
var copy = t.children('.pw-edit-copy');
var orig = t.children('.pw-edit-orig');
copy.hide().html(copy.data('prev'));
orig.show();
copy.data('prev', null);
t.removeClass('pw-changed pw-edited pw-editing');
});
buttons.hide();
}
/**
* Event for when the "cancel" button is clicked
*
*/
function inlineCancelClickEvent() {
if($('.pw-changed').length > 0) {
if(confirm(ProcessWire.config.PageFrontEdit.labels.cancelConfirm)) {
inlineAbandonAllChanges();
buttons.hide();
} else {
// leave as-is
}
} else {
inlineAbandonAllChanges();
}
return false;
}
/**
* Event for when the "save" button is clicked
*
*/
function inlineSaveClickEvent() {
if(busy) return;
setBusy(true);
var pageID = parseInt($('#Inputfield_id').val());
var langID = parseInt($('#pw-edit-lang').val());
var btnSave = $('.pw-edit-save');
var btnCancel = $('.pw-edit-cancel');
var btnSaving = $('.pw-edit-saving');
var btnSaved = $('.pw-edit-saved');
var edited = $('.pw-changed');
var postData = {
action: 'PageFrontEditSave',
id: pageID,
language: langID,
fields: {}
};
var postToken = $('input._post_token');
var csrfName = postToken.attr('name');
var csrfValue = postToken.val();
postData[csrfName] = csrfValue;
edited.each(function() {
var t = $(this);
var name = t.attr('data-name');
var page = parseInt(t.attr('data-page'));
var orig = t.children('.pw-edit-orig');
var copy = t.children('.pw-edit-copy');
var key = page + '__' + name;
if(t.hasClass('pw-edit-InputfieldCKEditor')) {
var editor = ckeditors[copy.attr('id')];
editor.getSelection().reset();
editor.getSelection().removeAllRanges();
/*
if(editor.focusManager.hasFocus) {
// CKEditor has documented bug that causes JS error on editor.getData(), so this prevents it
editor.focusManager.focus(true);
editor.focus();
}
*/
postData.fields[key] = editor.getData();
} else if(t.hasClass('pw-edit-InputfieldTinyMCE')) {
var editor = tinymces[copy.attr('id')];
postData.fields[key] = editor.getContent();
} else {
var textarea = document.createElement('textarea');
textarea.innerHTML = copy[0].innerHTML;
postData.fields[key] = textarea.value;
}
});
btnSave.hide();
btnCancel.hide();
btnSaving.show();
for(var copyID in tinymces) {
InputfieldTinyMCE.destroyEditors($('#' + copyID));
}
$('.InputfieldTinyMCELoaded').removeClass('InputfieldTinyMCELoaded');
$('.pw-edit-InputfieldTinyMCE').removeClass('pw-editing pw-edited');
tinymces = {}
// post save data to server
$.post(ProcessWire.config.PageFrontEdit.pageURL, postData, function(data) {
btnSaving.hide();
if(data.status > 0) {
// success
edited.each(function() {
var t = $(this);
var name = t.attr('data-name');
var page = t.attr('data-page');
var orig = t.children('.pw-edit-orig');
var copy = t.children('.pw-edit-copy');
var key = page + '__' + name;
t.removeClass('pw-editing pw-edited pw-changed');
orig.html(data.formatted[key]);
copy.html(data.unformatted[key]);
copy.data('prev', null);
copy.hide().trigger('pw-reloaded');
orig.show().trigger('pw-reloaded');
});
btnSaved.show();
setTimeout(function() {
buttons.fadeOut('fast', function() {
btnSaved.hide();
btnSave.show();
btnCancel.show();
setBusy(false);
});
}, 1000);
} else {
// error
setBusy(false);
alert(data.error);
btnSave.show();
btnCancel.show();
buttons.hide();
$('.pw-editing, .pw-edited').each(function() {
var t = $(this);
t.removeClass('pw-editing pw-edited pw-changed');
var orig = t.children('.pw-edit-orig');
var copy = t.children('.pw-edit-copy');
copy.hide();
orig.show();
});
}
for(var copyID in ckeditors) {
var instance = ckeditors[copyID];
instance.destroy();
}
ckeditors = {};
$('.pw-edit-InputfieldTinyMCE').each(function() {
inlineInitEditableRegion($(this));
});
});
}
/**
* Initialize all modal edit regions
*
* @param t
*
*/
function modalInitEditableRegions() {
var regions = $('.pw-edit-modal');
if(!regions.length) return;
$(document).on('pw-modal-closed', function(e, eventData) {
if(eventData.abort) return; // modal.js populates 'abort' if "x" button was clicked
var target = $(e.target);
if(!target.hasClass('pw-edit-modal')) return;
var targetID = target.attr('id');
var viewURL = $('#pw-url').val();
viewURL += (viewURL.indexOf('?') > -1 ? '&' : '?') + 'pw_edit_fields=' + target.attr('data-fields');
setBusy(true);
target.load(viewURL + ' #' + targetID, {}, function() {
var t = $(this);
var children = t.children();
if(children.length) {
var html = t.children().html();
t.html(html);
}
t.trigger('pw-reloaded');
setBusy(false);
});
});
}
/**
* Initialize PageFrontEdit
*
*/
function init() {
if(isTouch) buttons.addClass('pw-edit-buttons-touch');
// test if font-awesome needs to be loaded
var test = $('#pw-fa-test');
var width = test.width();
if(width < 10) loadCSS(ProcessWire.config.PageFrontEdit.files.fa);
test.hide();
// load PageFrontEdit.css
loadCSS(ProcessWire.config.PageFrontEdit.files.css);
$('body').addClass('pw-' + ProcessWire.config.PageFrontEdit.adminTheme);
// setup editable regions
$('.pw-edit:not(.pw-edit-modal)').each(function() {
inlineInitEditableRegion($(this));
});
// initialize modal edit regions
modalInitEditableRegions();
if($('.pw-edit-InputfieldTinyMCE').length) {
var file1 = ProcessWire.config.PageFrontEdit.files.tinymce1;
var file2 = ProcessWire.config.PageFrontEdit.files.tinymce2;
jQuery.getScript(file1, function() {
tinymce.baseURL = TINYMCE_BASEURL;
tinymce.suffix = '.min';
jQuery.getScript(file2, function() {
}).fail(function(jqxhr, settings, exception) {
alert('failed to load ' + file2 + ': ' + exception);
});
}).fail(function(jqxhr, settings, exception) {
alert('failed to load ' + file1 + ': ' + exception);
});
}
// load ckeditor, modal and plugins, if needed
if($('.pw-edit-InputfieldCKEditor').length) {
jQuery.getScript(ProcessWire.config.PageFrontEdit.files.ckeditor, function() {
jQuery.getScript(ProcessWire.config.PageFrontEdit.files.modal, function() {
for(var name in ProcessWire.config.InputfieldCKEditor.plugins) {
var file = ProcessWire.config.InputfieldCKEditor.plugins[name];
CKEDITOR.plugins.addExternal(name, file, '');
}
}).fail(function(jqxhr, settings, exception) {
alert('failed to load modal.js: ' + exception);
});
}).fail(function(jqxhr, settings, exception) {
alert('failed to load ckeditor.js: ' + exception);
});
} else {
jQuery.getScript(ProcessWire.config.PageFrontEdit.files.modal)
.fail(function(jqxhr, settings, exception) {
alert('failed to load modal.js: ' + exception);
});
}
// click action to cancel edits
$('.pw-edit-cancel').on('click', inlineCancelClickEvent);
// click action to save edits
$('.pw-edit-save').on('click', function() {
$('.pw-editing:not(.pw-edit-InputfieldCKEditor)').trigger('blur');
setTimeout(function() {
inlineSaveClickEvent();
}, 250);
});
}
init();
}
jQuery(document).ready(function($) {
PageFrontEditInit($);
});