editor.js/modules/core.js
2017-08-30 06:48:52 +03:00

390 lines
8.6 KiB
JavaScript

/**
* Codex Editor Core
*
* @author Codex Team
* @version 1.1.3
*/
module.exports = (function (core) {
let editor = codex.editor;
/**
* @public
*
* Editor preparing method
* @return Promise
*/
core.prepare = function (userSettings) {
return new Promise(function (resolve, reject) {
if ( userSettings ) {
editor.settings.tools = userSettings.tools || editor.settings.tools;
}
if (userSettings.data) {
editor.state.blocks = userSettings.data;
}
if (userSettings.initialBlockPlugin) {
editor.settings.initialBlockPlugin = userSettings.initialBlockPlugin;
}
if (userSettings.sanitizer) {
editor.settings.sanitizer = userSettings.sanitizer;
}
editor.hideToolbar = userSettings.hideToolbar;
editor.settings.placeholder = userSettings.placeholder || '';
editor.nodes.holder = document.getElementById(userSettings.holderId || editor.settings.holderId);
if (typeof editor.nodes.holder === undefined || editor.nodes.holder === null) {
reject(Error("Holder wasn't found by ID: #" + userSettings.holderId));
} else {
resolve();
}
});
};
/**
* Logging method
* @param type = ['log', 'info', 'warn']
*/
core.log = function (msg, type, arg) {
type = type || 'log';
if (!arg) {
arg = msg || 'undefined';
msg = '[codex-editor]: %o';
} else {
msg = '[codex-editor]: ' + msg;
}
try{
if ( 'console' in window && window.console[ type ] ) {
if ( arg ) window.console[ type ]( msg, arg );
else window.console[ type ]( msg );
}
}catch(e) {}
};
/**
* @protected
*
* Helper for insert one element after another
*/
core.insertAfter = function (target, element) {
target.parentNode.insertBefore(element, target.nextSibling);
};
/**
* @const
*
* Readable DOM-node types map
*/
core.nodeTypes = {
TAG : 1,
TEXT : 3,
COMMENT : 8,
DOCUMENT_FRAGMENT: 11
};
/**
* @const
* Readable keys map
*/
core.keys = { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, DOWN: 40, RIGHT: 39, DELETE: 46, META: 91 };
/**
* @protected
*
* Check object for DOM node
*/
core.isDomNode = function (el) {
return el && typeof el === 'object' && el.nodeType && el.nodeType == this.nodeTypes.TAG;
};
/**
* Checks passed object for emptiness
* @require ES5 - Object.keys
* @param {object}
*/
core.isEmpty = function ( obj ) {
return Object.keys(obj).length === 0;
};
/**
* Native Ajax
* @param {String} settings.url - request URL
* @param {function} settings.beforeSend - returned value will be passed as context to the Success, Error and Progress callbacks
* @param {function} settings.success
* @param {function} settings.progress
*/
core.ajax = function (settings) {
if (!settings || !settings.url) {
return;
}
var XMLHTTP = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),
encodedString,
isFormData,
prop;
settings.async = true;
settings.type = settings.type || 'GET';
settings.data = settings.data || '';
settings['content-type'] = settings['content-type'] || 'application/json; charset=utf-8';
if (settings.type == 'GET' && settings.data) {
settings.url = /\?/.test(settings.url) ? settings.url + '&' + settings.data : settings.url + '?' + settings.data;
} else {
encodedString = '';
for(prop in settings.data) {
encodedString += (prop + '=' + encodeURIComponent(settings.data[prop]) + '&');
}
}
if (settings.withCredentials) {
XMLHTTP.withCredentials = true;
}
/**
* Value returned in beforeSend funtion will be passed as context to the other response callbacks
* If beforeSend returns false, AJAX will be blocked
*/
let responseContext,
beforeSendResult;
if (typeof settings.beforeSend === 'function') {
beforeSendResult = settings.beforeSend.call();
if (beforeSendResult === false) {
return;
}
}
XMLHTTP.open( settings.type, settings.url, settings.async );
/**
* If we send FormData, we need no content-type header
*/
isFormData = isFormData_(settings.data);
if (!isFormData) {
if (settings.type !== 'POST') {
XMLHTTP.setRequestHeader('Content-type', settings['content-type']);
} else {
XMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
}
}
XMLHTTP.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
responseContext = beforeSendResult || XMLHTTP;
if (typeof settings.progress === 'function') {
XMLHTTP.upload.onprogress = settings.progress.bind(responseContext);
}
XMLHTTP.onreadystatechange = function () {
if (XMLHTTP.readyState === 4) {
if (XMLHTTP.status === 200) {
if (typeof settings.success === 'function') {
settings.success.call(responseContext, XMLHTTP.responseText);
}
} else {
if (typeof settings.error === 'function') {
settings.error.call(responseContext, XMLHTTP.responseText, XMLHTTP.status);
}
}
}
};
if (isFormData) {
// Sending FormData
XMLHTTP.send(settings.data);
} else {
// POST requests
XMLHTTP.send(encodedString);
}
return XMLHTTP;
};
/**
* Appends script to head of document
* @return Promise
*/
core.importScript = function (scriptPath, instanceName) {
return new Promise(function (resolve, reject) {
let script;
/** Script is already loaded */
if ( !instanceName ) {
reject('Instance name is missed');
} else if ( document.getElementById(editor.scriptPrefix + instanceName) ) {
resolve(scriptPath);
}
script = document.createElement('SCRIPT');
script.async = true;
script.defer = true;
script.id = editor.scriptPrefix + instanceName;
script.onload = function () {
resolve(scriptPath);
};
script.onerror = function () {
reject(scriptPath);
};
script.src = scriptPath;
document.head.appendChild(script);
});
};
/**
* Function for checking is it FormData object to send.
* @param {Object} object to check
* @return boolean
*/
var isFormData_ = function (object) {
return object instanceof FormData;
};
/**
* Check block
* @param target
* @description Checks target is it native input
*/
core.isNativeInput = function (target) {
var nativeInputAreas = ['INPUT', 'TEXTAREA'];
return nativeInputAreas.indexOf(target.tagName) != -1;
};
/**
* Check if block is empty
* We should check block textContent, child native inputs and some exceptions like IMG and IFRAME
*
* @param block
* @returns {boolean}
*/
core.isBlockEmpty = function (block) {
const EXCEPTION_TAGS = ['IMG', 'IFRAME'];
var nativeInputs = block.querySelectorAll('textarea, input'),
nativeInputsAreEmpty = true,
textContentIsEmpty = !block.textContent.trim();
Array.prototype.forEach.call(nativeInputs, function (input) {
if (input.type == 'textarea' || input.type == 'text') {
nativeInputsAreEmpty = nativeInputsAreEmpty && !input.value.trim();
}
});
return textContentIsEmpty && nativeInputsAreEmpty && !EXCEPTION_TAGS.includes(block.tagName);
};
return core;
})({});