463 lines
12 KiB
JavaScript
463 lines
12 KiB
JavaScript
|
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($);
|
||
|
});
|