closedquestion-8.x-3.x-dev/assets/js/libraries/jquery/jquery.maEditor.js
assets/js/libraries/jquery/jquery.maEditor.js
/**
* maEditor
* based on boilerplate version 1.3
* @param {object} $ A jQuery object
**/
(function ($) {
"use strict"; //ECMA5 strict modus
$.maEditor = function (element, settings) {
/* define vars
*/
/* this object will be exposed to other objects */
var publicObj = this;
//the version number of the plugin
publicObj.version = "1.0";
/* this object holds functions used by the plugin boilerplate */
var _helper = {
/**
* Call hooks, additinal parameters will be passed on to registered plugins
* @param {string} name
*/
doHook: function (name) {
var i;
var pluginFunctionArgs = [];
/* remove first two arguments */
for (i = 1; i < arguments.length; i++) {
pluginFunctionArgs.push(arguments[i]);
}
/* call plugin functions */
if (_globals.plugins !== undefined) {
/* call plugins */
$.each(_globals.plugins, function (maEditor, extPlugin) {
if (
extPlugin.__hooks !== undefined &&
extPlugin.__hooks[name] !== undefined
) {
extPlugin.__hooks[name].apply(publicObj, pluginFunctionArgs);
}
});
}
/* trigger event on main element */
_globals.$element.trigger(name, pluginFunctionArgs);
},
/**
* Initializes the plugin
*/
doInit: function () {
_helper.doHook(
"maEditor.beforeInit",
publicObj,
element,
settings
);
publicObj.init();
_helper.doHook("maEditor.init", publicObj);
},
/**
* Loads an external script
* @param {string} libName
* @param {string} errorMessage
*/
loadScript: function (libName, errorMessage) {
/* remember libname */
_cdnFilesToBeLoaded.push(libName);
/* load script */
$.ajax({
type: "GET",
url: _globals.dependencies[libName].cdnUrl,
success: function () {
/* forget libname */
_cdnFilesToBeLoaded.splice(_cdnFilesToBeLoaded.indexOf(libName), 1); //remove element from _cdnFilesToBeLoaded array
/* call init function when all scripts are loaded */
if (_cdnFilesToBeLoaded.length === 0) {
_helper.doInit();
}
},
fail: function () {
console.error(errorMessage);
},
dataType: "script",
cache: "cache"
});
},
/**
* Checks dependencies based on the _globals.dependencies object
* @returns {boolean}
*/
checkDependencies: function () {
var dependenciesPresent = true;
for (var libName in _globals.dependencies) {
var errorMessage =
"jquery.maEditor: Library " +
libName +
" not found! This may give unexpected results or errors.";
var doesExist = $.isFunction(_globals.dependencies[libName])
? _globals.dependencies[libName]
: _globals.dependencies[libName].doesExist;
if (doesExist.call() === false) {
if (
$.isFunction(_globals.dependencies[libName]) === false &&
_globals.dependencies[libName].cdnUrl !== undefined
) {
/* cdn url provided: Load script from external source */
_helper.loadScript(libName, errorMessage);
}
else {
console.error(errorMessage);
dependenciesPresent = false;
}
}
}
return dependenciesPresent;
}
};
/* keeps track of external libs loaded via their CDN */
var _cdnFilesToBeLoaded = [];
/* this object holds all global variables */
var _globals = {};
/* handle settings */
var defaultSettings = {
"input": $(''),
"question_options": [],
"allowDuplicates": false,
"horizontalAlignment": false,
"onlyOrder": -1
};
_globals.settings = {};
if ($.isPlainObject(settings) === true) {
_globals.settings = $.extend(true, {}, defaultSettings, settings);
}
else {
_globals.settings = defaultSettings;
}
/* this object contains a number of functions to test for dependencies,
* doesExist function should return TRUE if the library/browser/etc is present
*/
_globals.dependencies = {
/* check for jQuery 1.6+ to be present */
"jquery1.6+": {
doesExist: function () {
var jqv, jqv_main, jqv_sub;
if (window.jQuery) {
jqv = jQuery().jquery.split(".");
jqv_main = parseInt(jqv[0], 10);
jqv_sub = parseInt(jqv[1], 10);
if (jqv_main > 1 || (jqv_main === 1 && jqv_sub >= 6)) {
return true;
}
}
return false;
},
cdnUrl: "http://code.jquery.com/jquery-git1.js"
}
};
_helper.checkDependencies();
//this object holds all plugins
_globals.plugins = {};
/* register DOM elements
* jQuerified elements start with $
*/
_globals.$element = $(element);
_globals.$attributeWrapper = _globals.$element.closest('.xmlJsonEditor_attribute_container');
_globals.template = '<div class="maEditor_source"></div>';
_globals.$matchTypeSelector = $('<select><option value="exact">' + Drupal.t('This is the complete answer.') + '</option><option value="part">' + Drupal.t('This is part of the answer.') + '</option></select>')
/**
* Init function
**/
publicObj.init = function () {
_globals.$element.append(_globals.template);
_globals.$element.append(_globals.$matchTypeSelector);
_globals.$element.after('<p class="xmlEditorAttributeFeedback" style="margin-bottom: 0;">The condition as it is stored in the database:</p>');
_globals.$sourceBox = _globals.$element.find('.maEditor_source');
_globals.$input = $(cfg('input'));
/* Add options */
$.each(cfg('question_options'), function () {
var $optionEl = getOptionElement(this.id, this.label, '');
_globals.$sourceBox.append($optionEl);
});
// Add events to options, match type select and input
_globals.$sourceBox.find('input[type=checkbox]').each(function () {
var $input = $(this);
$input.on('click', function () {
updateAnswerInput();
enableEditor();
});
});
_globals.$matchTypeSelector.on('change', function () {
updateAnswerInput();
});
_globals.$input.on('keyup', function () {
window.clearTimeout(_globals.$input.data('maEditor_timeout'));
var to = window.setTimeout(function () {
updateQuestionGUI();
}, 300);
_globals.$input.data('maEditor_timeout', to);
});
/* Hide preset select */
$('#xmlEditor_presets_select', _globals.$element.parent()).hide();
updateQuestionGUI();
};
/**
* Disables the editor. *
*/
function disableEditor() {
$('#maEditor_feedback').remove();
_globals.$element.addClass('xmlEditorAttributeFeedback_error');
_globals.$element.after('<p id="maEditor_feedback" class="xmlJsonEditor_feedback_description" style="color:red;">This advanced condition is not (yet) supported by the editor. The editor might not display the condition correctly. Please use below input.</p>');
$('.xmlEditorAttributeFeedback', _globals.$attributeWrapper).addClass('maEditor_visible');
$('#xmlEditor_presets_select', _globals.$attributeWrapper).addClass('maEditor_visible');
_globals.$input.addClass('maEditor_visible');
}
/**
* Enables the editor.
*/
function enableEditor() {
$('#maEditor_feedback').remove();
_globals.$element.removeClass('xmlEditorAttributeFeedback_error');
$('.xmlEditorAttributeFeedback', _globals.$attributeWrapper).removeClass('maEditor_visible');
$('#xmlEditor_presets_select', _globals.$attributeWrapper).removeClass('maEditor_visible');
_globals.$input.removeClass('maEditor_visible');
}
/**
* Creates an option jQuery DOM element.
*
* @param {string} dataId
* @param {string} label
* @param {string} classAttr
* @returns {object}
*/
function getOptionElement(dataId, label, classAttr) {
classAttr = classAttr ? ' ' + classAttr : '';
return $('<div class="maEditor_option' + classAttr + '"><input type="checkbox" name="maEditor" id="maEditor_' + dataId + '"><label for="maEditor_' + dataId + '">' + formatHTML(label) + '</label></div>');
}
/**
* Updates the answer input based on the question GUI.
*/
function updateAnswerInput() {
var re_items = [];
var matchType = _globals.$matchTypeSelector.val();
_globals.$sourceBox.find('input[type=checkbox]').each(function () {
var $input = $(this);
if ($input.is(':checked')) {
re_items.push($input.attr('id').replace(/maEditor_?/g, ''));
}
else if (matchType === 'part' && re_items[re_items.length - 1] !== '.*') {
re_items.push('.*');
}
});
// Fill input
_globals.$input.val('^' + re_items.join('') + '$');
// Keep input and editor in sync
_globals.$input.trigger('change');
}
/**
* Updates the question GUI based on the value of the input.
*/
function updateQuestionGUI() {
var shouldDisableEditor = false;
var re = _globals.$input.val();
var re_items = re.split('');
var matchType = 'exact';
$.each(re_items, function (i, re_item) {
if (new RegExp('[0-9a-zA-Z\u00C0-\u017F]', 'i').test(re_item)) {
$('#maEditor_' + re_item, _globals.$sourceBox).prop('checked', true);
}
else if (re_item === '.' || re_item === '*') {
matchType = 'part';
}
else if (['$', '^'].indexOf(re_item) < 0) {
shouldDisableEditor = true;
}
});
// Set match type and disable editor if necessarry.
_globals.$matchTypeSelector.val(matchType);
if (shouldDisableEditor) {
disableEditor();
}
else {
enableEditor();
}
}
/**
* Returns a setting
*
* @param {string} paramName
* @returns {mixed}
*/
function cfg(paramName) {
var returnVal;
if (paramName === 'onlyOrder') {
returnVal = parseInt(_globals.settings.onlyOrder, 10);
returnVal = isNaN(returnVal) ? -1 : returnVal;
}
else {
returnVal = _globals.settings[paramName];
}
return returnVal;
}
/**
* Remove duplicates from a list.
*
* @param {object} $ul
* A jQuery DOM list object.
*/
function sortableUniqueItems($ul) {
var seen = {};
$ul.find('li').each(function () {
// Remove duplicates.
var $li = $(this);
var id = $li.attr('data-id');
if (seen[id] === true) {
$li.remove();
}
else {
seen[id] = true;
}
});
}
/**
* Format 'available items' sortable.
*/
function formatAvailableItemsSortable() {
// Make sure sortable has unique items.
sortableUniqueItems(_globals.$sourceSortable);
// Unset margin left set by formatTargetItemsSortable.
_globals.$sourceSortable.find('li').css({"margin-left": ''});
}
/**
* Format target item sortables.
*/
function formatTargetItemsSortable() {
// Deal with 'Only select, order of options does not matter'
if (cfg('onlyOrder') === 2) {
$(".maEditor_target_sortable").each(function () {
sortableUniqueItems($(this)); // Make sure each target sortable has unique items.
});
}
}
/**
* Formats HTML
*
* @param {string} html
* @returns {string}
* The formatted contents.
*/
function formatHTML(html) {
/* Handle Media tags */
var inlineTag = '', imgHTML = 'img', mediaObj, i;
var html, styleAttr;
var matches = html.match(/\[\[.*?\]\]/g);
if (matches) {
for (i = 0; i < matches.length; i++) {
inlineTag = matches[i];
inlineTag = inlineTag.replace('[[', '').replace(']]', '');
mediaObj = JSON.parse(inlineTag);
if (mediaObj && mediaObj.attributes && (mediaObj.attributes['data-src'] || mediaObj.attributes['src'])) {
mediaObj.attributes['data-src'] = mediaObj.attributes['data-src'] ? mediaObj.attributes['data-src'] : mediaObj.attributes['src'];
styleAttr = mediaObj.attributes.style ? ' style="' + mediaObj.attributes.style + '"' : '';
imgHTML = '<img src="' + mediaObj.attributes['data-src'] + '" width="' + mediaObj.attributes['width'] + '" height="' + mediaObj.attributes['height'] + '"' + styleAttr + ' />';
}
html = html.replace(matches[i], imgHTML);
}
}
return html;
}
/* initialize maEditor
*/
if (_cdnFilesToBeLoaded.length === 0) {
_helper.doInit();
}
};
$.fn.maEditor = function (settings) {
return this.each(function () {
if (undefined === $(this).data("maEditor")) {
var plugin = new $.maEditor(this, settings);
$(this).data("maEditor", plugin);
}
});
};
})(jQuery);
