265 lines
7.2 KiB
JavaScript
265 lines
7.2 KiB
JavaScript
/**
|
|
* @file js/controllers/form/MultilingualInputHandler.js
|
|
*
|
|
* Copyright (c) 2014-2021 Simon Fraser University
|
|
* Copyright (c) 2000-2021 John Willinsky
|
|
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
|
*
|
|
* @class MultilingualInputHandler
|
|
* @ingroup js_controllers_form
|
|
*
|
|
* @brief Handler for toggling the pop-over on multi lingual inputs (text
|
|
* inputs and text areas, mostly).
|
|
*/
|
|
(function($) {
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
*
|
|
* @extends $.pkp.classes.Handler
|
|
*
|
|
* @param {jQueryObject} $popover the wrapped HTML element.
|
|
* @param {Object} options options to be passed
|
|
* into the validator plug-in.
|
|
*/
|
|
$.pkp.controllers.form.MultilingualInputHandler = function($popover, options) {
|
|
this.parent($popover, options);
|
|
|
|
// Bind to the focus of the primary language (the first input)
|
|
// open the pop-over
|
|
var $popoverNode = null;
|
|
|
|
if ($popover.hasClass('pkpTagit')) {
|
|
$popoverNode = $popover.find(':input').filter(':visible');
|
|
} else {
|
|
$popoverNode = $popover.find(':input').first();
|
|
}
|
|
|
|
$popoverNode
|
|
.focus(this.callbackWrapper(this.focusHandler_));
|
|
// Bind to the blur of any of the inputs to check if we should close.
|
|
$popover.find(':input').
|
|
blur(this.callbackWrapper(this.blurHandler_));
|
|
|
|
this.publishEvent('tinyMCEInitialized');
|
|
|
|
this.tinyMCEInitHandler_();
|
|
|
|
setTimeout(this.callbackWrapper(this.isIncomplete_), 500);
|
|
};
|
|
$.pkp.classes.Helper.inherits(
|
|
$.pkp.controllers.form.MultilingualInputHandler,
|
|
$.pkp.classes.Handler);
|
|
|
|
|
|
//
|
|
// Private helper methods.
|
|
//
|
|
/**
|
|
* Focus event handler. This is attached to all primary inputs.
|
|
*
|
|
* @param {HTMLElement} multilingualInput The primary multilingual
|
|
* element.
|
|
* @param {Event} event The focus event.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.form.MultilingualInputHandler.prototype.focusHandler_ =
|
|
function(multilingualInput, event) {
|
|
|
|
this.showPopover_();
|
|
};
|
|
|
|
|
|
/**
|
|
* Blur event handler. This is attached to all inputs inside this
|
|
* popover element.
|
|
*
|
|
* @param {HTMLElement} multilingualInput The element in the
|
|
* multilingual set to hide.
|
|
* @param {Event} event The event that triggered the action.
|
|
* @return {boolean} Return true to continue the event handling.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.form.MultilingualInputHandler.prototype.blurHandler_ =
|
|
function(multilingualInput, event) {
|
|
|
|
// Use a timeout to give the other element a chance to acquire the focus.
|
|
setTimeout(this.callbackWrapper(function() {
|
|
if (!this.hasElementInFocus_()) {
|
|
this.hidePopover_();
|
|
}
|
|
}), 100);
|
|
|
|
return true;
|
|
};
|
|
|
|
|
|
/**
|
|
* Hide this popover.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.form.MultilingualInputHandler.prototype.hidePopover_ =
|
|
function() {
|
|
var $popover = this.getHtmlElement();
|
|
$popover.removeClass('localization_popover_container_focus');
|
|
$popover.find('.localization_popover').hide();
|
|
this.isIncomplete_();
|
|
};
|
|
|
|
|
|
/**
|
|
* Show this popover.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.form.MultilingualInputHandler.prototype.showPopover_ =
|
|
function() {
|
|
var $popover = this.getHtmlElement();
|
|
$popover.addClass('localization_popover_container_focus');
|
|
|
|
// Hack alert: setting width in JS because they do not line up otherwise.
|
|
$popover.find('.localization_popover').width(
|
|
/** @type {number} */ ($popover.width()));
|
|
|
|
// Show the pop over.
|
|
$popover.find('.localization_popover').show();
|
|
};
|
|
|
|
|
|
/**
|
|
* Test if any of the elements inside this popover has focus.
|
|
* @return {boolean} True iff an element is in focus.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.form.MultilingualInputHandler.prototype.hasElementInFocus_ =
|
|
function() {
|
|
|
|
var $popover = this.getHtmlElement();
|
|
|
|
// Do the test.
|
|
if ($popover.has(document.activeElement).length) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Check if the field is missing a localization
|
|
*
|
|
* Fields are not considered to be missing a localization unless at least
|
|
* one of the localizations has an entry.
|
|
*
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.form.MultilingualInputHandler.prototype.isIncomplete_ =
|
|
function() {
|
|
|
|
var $popover = this.getHtmlElement(),
|
|
$inputs = [],
|
|
valuesCount = 0;
|
|
|
|
if (typeof tinyMCE === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
// Track current values in the tinyMCE control
|
|
if (this.getHtmlElement().find('.richContent').length) {
|
|
$popover.find('textarea').each(function() {
|
|
var id = $(this).attr('id'),
|
|
tinymce;
|
|
|
|
$inputs.push($(this));
|
|
tinymce = tinyMCE.EditorManager.get(/** @type {string} */(
|
|
$(this).attr('id')));
|
|
if (tinymce.getContent()) {
|
|
valuesCount++;
|
|
}
|
|
});
|
|
|
|
} else {
|
|
$inputs = $popover.find(':input');
|
|
$inputs.each(function() {
|
|
if ($(this).val()) {
|
|
valuesCount++;
|
|
}
|
|
});
|
|
}
|
|
|
|
if (valuesCount > 0 && valuesCount < $inputs.length) {
|
|
$popover.removeClass('localizationComplete')
|
|
.addClass('localizationIncomplete');
|
|
} else if (valuesCount === $inputs.length) {
|
|
$popover.removeClass('localizationIncomplete')
|
|
.addClass('localizationComplete');
|
|
} else {
|
|
$popover.removeClass('localizationIncomplete localizationComplete');
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Attach focus and blur event handlers to the tinyMCE window element, which
|
|
* show and hide the localization popover
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.form.MultilingualInputHandler.prototype.tinyMCEInitHandler_ =
|
|
function() {
|
|
|
|
if (!this.getHtmlElement().find('.richContent').length) {
|
|
return;
|
|
}
|
|
|
|
if (typeof tinyMCE === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
var htmlElement = this.getHtmlElement(),
|
|
tinyMCEObject = tinyMCE.EditorManager.get(/** @type {string} */(
|
|
htmlElement.find('textarea').first().attr('id')));
|
|
|
|
tinyMCEObject.on('focus', this.callbackWrapper(function() {
|
|
// We need also to close the multilingual popover when user clicks
|
|
// outside the popover element. The blur event is not enough because
|
|
// sometimes (with text selected in editor) Chrome will consider the
|
|
// tinyMCE editor as still active and that will avoid the popover to
|
|
// close (see the first check of the blur handler, just above).
|
|
//
|
|
// Firefox will also not completely focus on tinyMCE editors after
|
|
// coming back from fullscreen mode (the callback to focus the
|
|
// editor when set content will only trigger the focus handler that
|
|
// we attach here, but will not move the cursor inside the tinyMCE
|
|
// editor). Then, if user clicks outside the popover, it will not
|
|
// close because no blur event will be triggered.
|
|
this.trigger('callWhenClickOutside', {
|
|
container: this.getHtmlElement(),
|
|
callback: this.callbackWrapper(this.hidePopover_)
|
|
});
|
|
|
|
this.showPopover_();
|
|
}));
|
|
|
|
tinyMCEObject.on('blur', this.callbackWrapper(function() {
|
|
// Check if the active document element is still the tinyMCE
|
|
// editor. If true, return false. This will avoid closing the
|
|
// popover if user is just inserting an image or editing the
|
|
// html source, for example (both actions open a new window).
|
|
if ($(tinyMCEObject.getContainer()).find('iframe').attr('id') ==
|
|
$(document.activeElement).attr('id')) {
|
|
return false;
|
|
}
|
|
|
|
// Use a timeout to give the other element a chance to acquire the
|
|
// focus.
|
|
setTimeout(this.callbackWrapper(function() {
|
|
if (!this.hasElementInFocus_()) {
|
|
this.hidePopover_();
|
|
}
|
|
}), 0);
|
|
}));
|
|
};
|
|
|
|
|
|
}(jQuery));
|