Prepare plugins (#148)

* prepare plugins improvements

* prepare plugins asyncronically

* update version

* restore data from cache when plugin is unavailable

* added comments to the complicated solution

* new module tools.js for methods working with plugins

* remove ArrayOfCachedData

* build updated

* remove trash
This commit is contained in:
khaydarov 2017-02-05 17:50:48 +03:00 committed by Peter Savchenko
parent 8f9844ff83
commit f5314dfab6
15 changed files with 292 additions and 82 deletions

View file

@ -39,8 +39,8 @@
"no-trailing-spaces": 2,
"no-mixed-spaces-and-tabs": 2,
"padded-blocks": [2, "always"],
"space-before-blocks": 2,
"space-before-function-paren": [2, {
"space-before-blocks": 1,
"space-before-function-paren": [1, {
"anonymous": "always",
"named": "never"
}],
@ -63,6 +63,7 @@
"module": true,
"require": true,
"window": true,
"console" : true,
"codex": true,
"VERSION" : true,
"Promise" : true,

View file

@ -29,7 +29,6 @@
min-height: 350px;
}
.ce-block__content a {
color: #186baa;
}
@ -390,6 +389,16 @@
padding: 0;
}
.cdx-unavailable-block {
display: block;
margin: 10px 0;
padding: 80px;
background-color: #fff7f7;
text-align: center;
border-radius: 3px;
color: #ce5f5f;
}
/**
* Typographycs
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -14,6 +14,7 @@ module.exports = (function (editor) {
var init = function () {
editor.core = require('./modules/core');
editor.tools = require('./modules/tools');
editor.ui = require('./modules/ui');
editor.transport = require('./modules/transport');
editor.renderer = require('./modules/renderer');
@ -75,9 +76,9 @@ module.exports = (function (editor) {
* Output state
*/
editor.state = {
jsonOutput: [],
blocks : [],
inputs : []
jsonOutput : [],
blocks : [],
inputs : []
};
/**
@ -127,7 +128,7 @@ module.exports = (function (editor) {
.then(editor.ui.make)
.then(editor.ui.addTools)
.then(editor.ui.bindEvents)
.then(editor.ui.preparePlugins)
.then(editor.tools.prepare)
.then(editor.transport.prepare)
.then(editor.renderer.makeBlocksFromData)
.then(editor.ui.saveInputs)

View file

@ -176,8 +176,35 @@
}
},
data : {
items: [],
count: 0
// items: [
// {
// type : 'paragraph',
// data : {
// text : 'Привет от CodeX'
// }
// },
// {
// type : 'tweet',
// data : {
// "media" : true,
// "conversation" : false,
// "user" : null,
// "id" : 12312312312,
// "text" : null,
// "created_at" : null,
// "status_url" : 'ertertert',
// "caption" : null
// },
// cover : false
// },
// {
// type : 'paragraph',
// data : {
// text : 'Пишите нам на team@ifmo.su'
// }
// },
// ],
// count: 2
}
});
</script>

View file

@ -130,6 +130,17 @@ module.exports = (function (core) {
};
/**
* Checks passed object for emptiness
* @require ES5 - Object.keys
* @param {object}
*/
core.isEmpty = function ( obj ) {
return Object.keys(obj).length === 0;
};
/**
* Native Ajax
*/

View file

@ -311,6 +311,19 @@ module.exports = (function (draw) {
};
/**
* Unavailable plugin block
*/
draw.unavailableBlock = function () {
var wrapper = document.createElement('DIV');
wrapper.classList.add('cdx-unavailable-block');
return wrapper;
};
return draw;
})({});

View file

@ -17,7 +17,7 @@ module.exports = (function (renderer) {
/**
* If redactor is empty, add first paragraph to start writing
*/
if (!editor.state.blocks.items.length) {
if (editor.core.isEmpty(editor.state.blocks) || !editor.state.blocks.items.length) {
editor.ui.addInitialBlock();
return;
@ -121,7 +121,10 @@ module.exports = (function (renderer) {
return Promise.resolve().then(function () {
return blocksList[index];
return {
tool : blocksList[index],
position : index
};
});
@ -132,19 +135,22 @@ module.exports = (function (renderer) {
*
* @uses render method of each plugin
*
* @param {object} blockData looks like
* { header : {
* text: '',
* type: 'H3', ...
* }
* }
* @return {object} with type and Element
* @param {Object} toolData.tool
* { header : {
* text: '',
* type: 'H3', ...
* }
* }
* @param {Number} toolData.position - index in input-blocks array
* @return {Object} with type and Element
*/
renderer.createBlockFromData = function (blockData) {
renderer.createBlockFromData = function ( toolData ) {
/** New parser */
var pluginName = blockData.type,
cover = blockData.cover;
var block,
tool = toolData.tool,
pluginName = tool.type,
cover = tool.cover;
/** Get first key of object that stores plugin name */
// for (var pluginName in blockData) break;
@ -163,8 +169,23 @@ module.exports = (function (renderer) {
}
/** New Parser */
var block = editor.tools[pluginName].render(blockData.data);
if ( editor.tools[pluginName].available === false ) {
block = editor.draw.unavailableBlock();
block.innerHTML = editor.tools[pluginName].loadingMessage;
/**
* Saver will extract data from initial block data by position in array
*/
block.dataset.inputPosition = toolData.position;
} else {
/** New Parser */
block = editor.tools[pluginName].render(tool.data);
}
/** is first-level block stretched */
var stretched = editor.tools[pluginName].isStretched || false;

View file

@ -105,29 +105,45 @@ module.exports = (function (saver) {
/** Result saver */
var blockContent = block.childNodes[0],
pluginsContent = blockContent.childNodes[0],
savedData = editor.tools[pluginName].save(pluginsContent),
output;
savedData,
position,
output,
coverFlag = false;
/** If plugin wasn't available then return data from cache */
if ( editor.tools[pluginName].available === false ) {
position = pluginsContent.dataset.inputPosition;
savedData = codex.editor.state.blocks.items[position].data;
coverFlag = codex.editor.state.blocks.items[position].cover;
} else {
savedData = editor.tools[pluginName].save(pluginsContent);
coverFlag = block.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE);
if (editor.tools[pluginName].validate) {
var result = editor.tools[pluginName].validate(savedData);
/**
* Do not allow invalid data
*/
if (!result)
return;
}
}
output = {
type: pluginName,
data: savedData
};
if (editor.tools[pluginName].validate) {
var result = editor.tools[pluginName].validate(savedData);
/**
* Do not allow invalid data
*/
if (!result)
return;
}
/** Marks Blocks that will be in main page */
output.cover = block.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE);
output.cover = coverFlag;
editor.state.jsonOutput.push(output);

144
modules/tools.js Normal file
View file

@ -0,0 +1,144 @@
/**
* Module working with plugins
*/
module.exports = (function () {
let editor = codex.editor;
/**
* Initialize plugins before using
* Ex. Load scripts or call some internal methods
* @return Promise
*/
function prepare() {
return new Promise(function (resolve_, reject_) {
Promise.resolve()
/**
* Compose a sequence of plugins that requires preparation
*/
.then(function () {
let pluginsRequiresPreparation = [],
allPlugins = editor.tools;
for ( let pluginName in allPlugins ) {
let plugin = allPlugins[pluginName];
if (plugin.prepare && typeof plugin.prepare != 'function' || !plugin.prepare) {
continue;
}
pluginsRequiresPreparation.push(plugin);
}
return pluginsRequiresPreparation;
})
/** Wait plugins while they prepares */
.then(waitAllPluginsPreparation_)
.then(function () {
editor.core.log('Plugins loaded', 'info');
resolve_();
}).catch(function (error) {
reject_(error);
});
});
}
/**
* @param {array} plugins - list of tools that requires preparation
* @return {Promise} resolved while all plugins will be ready or failed
*/
function waitAllPluginsPreparation_(plugins) {
/**
* @calls allPluginsProcessed__ when all plugins prepared or failed
*/
return new Promise (function (allPluginsProcessed__) {
/**
* pluck each element from queue
* First, send resolved Promise as previous value
* Each plugins "prepare" method returns a Promise, that's why
* reduce current element will not be able to continue while can't get
* a resolved Promise
*
* If last plugin is "prepared" then go to the next stage of initialization
*/
plugins.reduce(function (previousValue, plugin, iteration) {
return previousValue.then(function () {
/**
* Wait till plugins prepared
* @calls pluginIsReady__ when plugin is ready or failed
*/
return new Promise ( function (pluginIsReady__) {
callPluginsPrepareMethod_( plugin )
.then( pluginIsReady__ )
.then( function () {
plugin.available = true;
})
.catch(function (error) {
editor.core.log(`Plugin «${plugin.type}» was not loaded. Preparation failed because %o`, 'warn', error);
plugin.available = false;
plugin.loadingMessage = error;
/** Go ahead even some plugin has problems */
pluginIsReady__();
})
.then(function () {
/** If last plugin has problems then just ignore and continue */
if (iteration == plugins.length - 1) {
allPluginsProcessed__();
}
});
});
});
}, Promise.resolve() );
});
}
var callPluginsPrepareMethod_ = function (plugin) {
return plugin.prepare( plugin.config || {} );
};
return {
prepare: prepare
};
}());

View file

@ -309,44 +309,6 @@ module.exports = (function (ui) {
};
/**
* Initialize plugins before using
* Ex. Load scripts or call some internal methods
* @return Promise
*/
ui.preparePlugins = function () {
return new Promise(function (resolve, reject) {
let pluginName,
plugin;
for ( pluginName in editor.tools ) {
plugin = editor.tools[pluginName];
if (typeof plugin.prepare != 'function') {
continue;
}
plugin.prepare(plugin.config || {}).then(function () {
resolve();
}).catch(function (error) {
reject(error);
});
}
});
};
ui.addBlockHandlers = function (block) {
if (!block) return;

View file

@ -1,6 +1,6 @@
{
"name": "codex.editor",
"version": "1.4.1",
"version": "1.4.3",
"description": "Codex Editor. Native JS, based on API and Open Source",
"main": "index.js",
"scripts": {

View file

@ -63,7 +63,12 @@ module.exports = {
}),
/** Минифицируем CSS и JS */
new webpack.optimize.UglifyJsPlugin(),
new webpack.optimize.UglifyJsPlugin({
/** Disable warning messages. Cant disable uglify for 3rd party libs such as html-janitor */
compress: {
warnings: false
}
}),
/** Block biuld if errors found */
new webpack.NoErrorsPlugin(),

File diff suppressed because one or more lines are too long