Merge branch 'master' into release.1.6.0

This commit is contained in:
Taly Guryn 2017-03-15 17:25:22 +03:00
commit 37a51c3faf
29 changed files with 759 additions and 640 deletions

View file

@ -112,6 +112,7 @@
margin-right: 17px;
border-radius: 16px;
text-align: center;
vertical-align: top;
cursor: pointer;
font-size: 14px;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -28,7 +28,6 @@ module.exports = (function (editor) {
editor.notifications = require('./modules/notifications');
editor.parser = require('./modules/parser');
editor.sanitizer = require('./modules/sanitizer');
editor.anchors = require('./modules/anchors');
editor.listeners = require('./modules/listeners');
editor.destroyer = require('./modules/destroyer');
editor.paste = require('./modules/paste');
@ -37,13 +36,11 @@ module.exports = (function (editor) {
/**
* @public
*
* holds initial settings
*/
editor.settings = {
tools : ['paragraph', 'header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile'],
textareaId: 'codex-editor',
uploadImagesUrl: '/editor/transport/',
// Type of block showing on empty editor
initialBlockPlugin: 'paragraph'
@ -129,9 +126,7 @@ module.exports = (function (editor) {
editor.core.prepare(userSettings)
// If all ok, make UI, bind events and parse initial-content
.then(editor.ui.make)
.then(editor.ui.addTools)
.then(editor.ui.bindEvents)
.then(editor.ui.prepare)
.then(editor.tools.prepare)
.then(editor.paste.prepare)
.then(editor.transport.prepare)

View file

@ -51,6 +51,9 @@
<script src="plugins/embed/embed.js"></script>
<link rel="stylesheet" href="plugins/embed/embed.css">
<script src="plugins/raw/raw.js"></script>
<link rel="stylesheet" href="plugins/raw/raw.css">
<script>
codex.editor.start({
holderId : "codex-editor",
@ -68,8 +71,8 @@
showInlineToolbar: true,
allowRenderOnPaste: true
},
heading_styled: {
type: 'heading_styled',
header: {
type: 'header',
iconClassname: 'ce-icon-header',
appendCallback: header.appendCallback,
makeSettings: header.makeSettings,
@ -120,8 +123,8 @@
enableLineBreaks: true,
allowedToPaste: true,
},
quote_styled: {
type: 'quote_styled',
quote: {
type: 'quote',
iconClassname: 'ce-icon-quote',
makeSettings: quote.makeSettings,
prepare: quote.prepare,
@ -151,14 +154,15 @@
displayInToolbox: true,
renderOnPastePatterns: image.pastePatterns,
config: {
uploadUrl : '/club/fetch'
uploadImage : '/writing/uploadImage',
uploadFromUrl : '/club/fetch'
}
},
instagram: {
type: 'instagram',
iconClassname: 'ce-icon-instagram',
prepare: instagram.prepare,
render: instagram.reneder,
render: instagram.render,
validate: instagram.validate,
save: instagram.save,
destroy: instagram.destroy,
@ -186,6 +190,17 @@
destroy: embed.destroy,
validate: embed.validate,
renderOnPastePatterns: embed.pastePatterns,
},
raw: {
type: 'raw',
displayInToolbox: true,
iconClassname: 'raw-plugin-icon',
render: rawPlugin.render,
save: rawPlugin.save,
validate: rawPlugin.validate,
destroy: rawPlugin.destroy,
enableLineBreaks: true,
allowPasteHTML: true
}
},
data : {
@ -200,20 +215,18 @@
type : 'paragraph',
data : {
text : 'Пишите нам на team@ifmo.su'
},
anchor: 'Update',
}
},
{
type : 'list',
data : {
type : 'OL',
items : [1,3,4]
},
},
}
}
],
count: 3
}
});
</script>
</html>

View file

@ -1,47 +1,91 @@
/**
* Codex Editor callbacks module
* @module Codex Editor Callbacks module
* @description Module works with editor added Elements
*
* @author Codex Team
* @version 1.3.7
* @version 1.3.12
*/
module.exports = (function (callbacks) {
let editor = codex.editor;
/**
* used by UI module
* @description Routes all keydowns on document
* @param {Object} event
*/
callbacks.globalKeydown = function (event) {
switch (event.keyCode) {
case editor.core.keys.ENTER : editor.callback.enterKeyPressed(event); break;
case editor.core.keys.ENTER : enterKeyPressed_(event); break;
}
};
/**
* used by UI module
* @description Routes all keydowns on redactors area
* @param {Object} event
*/
callbacks.redactorKeyDown = function (event) {
switch (event.keyCode) {
case editor.core.keys.TAB : editor.callback.tabKeyPressed(event); break;
case editor.core.keys.ENTER : editor.callback.enterKeyPressedOnRedactorZone(event); break;
case editor.core.keys.ESC : editor.callback.escapeKeyPressed(event); break;
default : editor.callback.defaultKeyPressed(event); break;
case editor.core.keys.TAB : tabKeyPressedOnRedactorsZone_(event); break;
case editor.core.keys.ENTER : enterKeyPressedOnRedactorsZone_(event); break;
case editor.core.keys.ESC : escapeKeyPressedOnRedactorsZone_(event); break;
default : defaultKeyPressedOnRedactorsZone_(event); break;
}
};
/**
* used by UI module
* @description Routes all keyup events
* @param {Object} event
*/
callbacks.globalKeyup = function (event) {
switch (event.keyCode) {
case editor.core.keys.UP :
case editor.core.keys.LEFT :
case editor.core.keys.RIGHT :
case editor.core.keys.DOWN : editor.callback.arrowKeyPressed(event); break;
case editor.core.keys.DOWN : arrowKeyPressed_(event); break;
}
};
callbacks.tabKeyPressed = function (event) {
/**
* @param {Object} event
* @private
*
* Handles behaviour when tab pressed
* @description if Content is empty show toolbox (if it is closed) or leaf tools
* uses Toolbars toolbox module to handle the situation
*/
var tabKeyPressedOnRedactorsZone_ = function (event) {
var blockIsEmpty = !editor.content.currentNode.textContent.trim();
/**
* Wait for solution. Would like to know the behaviour
* @todo Add spaces
*/
event.preventDefault();
var nativeInputs = editor.content.currentNode.querySelectorAll('textarea, input'),
nativeInputsAreEmpty = true,
textContentIsEmpty = !editor.content.currentNode.textContent.trim();
Array.prototype.map.call(nativeInputs, function (input) {
if (input.type == 'textarea' || input.type == 'text') {
nativeInputsAreEmpty = nativeInputsAreEmpty && !input.value.trim();
}
});
var blockIsEmpty = textContentIsEmpty && nativeInputsAreEmpty;
if (!blockIsEmpty) {
@ -65,14 +109,14 @@ module.exports = (function (callbacks) {
}
event.preventDefault();
};
/**
* @param {Event} event
*/
callbacks.enterKeyPressed = function () {
* Handles global EnterKey Press
* @see enterPressedOnBlock_
* @param {Object} event
*/
var enterKeyPressed_ = function () {
if (editor.content.editorAreaHightlighted) {
@ -82,17 +126,44 @@ module.exports = (function (callbacks) {
*/
editor.caret.inputIndex = -1;
editor.callback.enterPressedOnBlock();
enterPressedOnBlock_();
}
};
/**
* ENTER key handler
* Makes new paragraph block
* Callback for enter key pressing in first-level block area
*
* @param {Event} event
* @private
*
* @description Inserts new block with initial type from settings
*/
callbacks.enterKeyPressedOnRedactorZone = function (event) {
var enterPressedOnBlock_ = function () {
var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;
editor.content.insertBlock({
type : NEW_BLOCK_TYPE,
block : editor.tools[NEW_BLOCK_TYPE].render()
}, true );
editor.toolbar.move();
editor.toolbar.open();
};
/**
* ENTER key handler
*
* @param {Object} event
* @private
*
* @description Makes new block with initial type from settings
*/
var enterKeyPressedOnRedactorsZone_ = function (event) {
if (event.target.contentEditable == 'true') {
@ -224,7 +295,14 @@ module.exports = (function (callbacks) {
};
callbacks.escapeKeyPressed = function (event) {
/**
* Escape behaviour
* @param event
* @private
*
* @description Closes toolbox and toolbar. Prevents default behaviour
*/
var escapeKeyPressedOnRedactorsZone_ = function (event) {
/** Close all toolbar */
editor.toolbar.close();
@ -237,9 +315,12 @@ module.exports = (function (callbacks) {
};
/**
* @param {Event} event
*/
callbacks.arrowKeyPressed = function () {
* @param {Event} event
* @private
*
* closes and moves toolbar
*/
var arrowKeyPressed_ = function (event) {
editor.content.workingNodeChanged();
@ -250,9 +331,13 @@ module.exports = (function (callbacks) {
};
/**
* @param {Event} event
*/
callbacks.defaultKeyPressed = function () {
* @private
* @param {Event} event
*
* @description Closes all opened bars from toolbar.
* If block is mark, clears highlightning
*/
var defaultKeyPressedOnRedactorsZone_ = function () {
editor.toolbar.close();
@ -265,20 +350,30 @@ module.exports = (function (callbacks) {
};
/**
* Handler when clicked on redactors area
*
* @protected
* @param event
*
* @description Detects clicked area. If it is first-level block area, marks as detected and
* on next enter press will be inserted new block
* Otherwise, save carets position (input index) and put caret to the editable zone.
*
* @see detectWhenClickedOnFirstLevelBlockArea_
*
*/
callbacks.redactorClicked = function (event) {
callbacks.detectWhenClickedOnFirstLevelBlockArea();
detectWhenClickedOnFirstLevelBlockArea_();
editor.content.workingNodeChanged(event.target);
editor.ui.saveInputs();
var selectedText = editor.toolbar.inline.getSelectionText(),
firstLevelBlock;
/**
* If selection range took off, then we hide inline toolbar
*/
/** If selection range took off, then we hide inline toolbar */
if (selectedText.length === 0) {
editor.toolbar.inline.close();
@ -302,10 +397,6 @@ module.exports = (function (callbacks) {
/** If we have any inputs */
if (editor.state.inputs.length) {
/**
* @todo Refactor
*/
/** getting firstlevel parent of input */
firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]);
@ -340,26 +431,19 @@ module.exports = (function (callbacks) {
}
/**
* Move toolbar to the right position and open
*/
editor.toolbar.move();
editor.toolbar.open();
} else {
/**
* Move toolbar to the new position and open
*/
editor.toolbar.move();
editor.toolbar.open();
/** Close all panels */
editor.toolbar.settings.close();
editor.toolbar.toolbox.close();
}
/**
* Move toolbar and open
*/
editor.toolbar.move();
editor.toolbar.open();
var inputIsEmpty = !editor.content.currentNode.textContent.trim(),
currentNodeType = editor.content.currentNode.dataset.tool,
@ -388,12 +472,15 @@ module.exports = (function (callbacks) {
/**
* This method allows to define, is caret in contenteditable element or not.
* Otherwise, if we get TEXT node from range container, that will means we have input index.
*
* @private
*
* @description Otherwise, if we get TEXT node from range container, that will means we have input index.
* In this case we use default browsers behaviour (if plugin allows that) or overwritten action.
* Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always
* specifies to the first-level block. Other cases we just ignore.
*/
callbacks.detectWhenClickedOnFirstLevelBlockArea = function () {
var detectWhenClickedOnFirstLevelBlockArea_ = function () {
var selection = window.getSelection(),
anchorNode = selection.anchorNode,
@ -437,7 +524,7 @@ module.exports = (function (callbacks) {
}
/** If editable element founded, flag is "TRUE", Therefore we return "FALSE" */
editor.content.editorAreaHightlighted = flag ? false : true;
editor.content.editorAreaHightlighted = !flag;
}
@ -445,7 +532,11 @@ module.exports = (function (callbacks) {
/**
* Toolbar button click handler
* @param this - cursor to the button
*
* @param {Object} event - cursor to the button
* @protected
*
* @description gets current tool and calls render method
*/
callbacks.toolbarButtonClicked = function (event) {
@ -458,7 +549,9 @@ module.exports = (function (callbacks) {
};
/** Show or Hide toolbox when plus button is clicked */
/**
* Show or Hide toolbox when plus button is clicked
*/
callbacks.plusButtonClicked = function () {
if (!editor.nodes.toolbox.classList.contains('opened')) {
@ -475,25 +568,33 @@ module.exports = (function (callbacks) {
/**
* Block handlers for KeyDown events
*
* @protected
* @param {Object} event
*
* Handles keydowns on block
* @see blockRightOrDownArrowPressed_
* @see backspacePressed_
* @see blockLeftOrUpArrowPressed_
*/
callbacks.blockKeydown = function (event) {
let block = this; // event.target input
let block = event.target; // event.target is input
switch (event.keyCode) {
case editor.core.keys.DOWN:
case editor.core.keys.RIGHT:
editor.callback.blockRightOrDownArrowPressed();
blockRightOrDownArrowPressed_(event);
break;
case editor.core.keys.BACKSPACE:
editor.callback.backspacePressed(block, event);
backspacePressed_(block, event);
break;
case editor.core.keys.UP:
case editor.core.keys.LEFT:
editor.callback.blockLeftOrUpArrowPressed();
blockLeftOrUpArrowPressed_(event);
break;
}
@ -502,8 +603,15 @@ module.exports = (function (callbacks) {
/**
* RIGHT or DOWN keydowns on block
*
* @param {Object} event
* @private
*
* @description watches the selection and gets closest editable element.
* Uses method getDeepestTextNodeFromPosition to get the last node of next block
* Sets caret if it is contenteditable
*/
callbacks.blockRightOrDownArrowPressed = function () {
var blockRightOrDownArrowPressed_ = function (event) {
var selection = window.getSelection(),
inputs = editor.state.inputs,
@ -582,8 +690,16 @@ module.exports = (function (callbacks) {
/**
* LEFT or UP keydowns on block
*
* @param {Object} event
* @private
*
* watches the selection and gets closest editable element.
* Uses method getDeepestTextNodeFromPosition to get the last node of previous block
* Sets caret if it is contenteditable
*
*/
callbacks.blockLeftOrUpArrowPressed = function () {
var blockLeftOrUpArrowPressed_ = function (event) {
var selection = window.getSelection(),
inputs = editor.state.inputs,
@ -667,30 +783,39 @@ module.exports = (function (callbacks) {
};
/**
* Callback for enter key pressing in first-level block area
* @param {Event} event
* Handles backspace keydown
*
* @param {Element} block
* @param {Object} event
* @private
*
* @description if block is empty, delete the block and set caret to the previous block
* If block is not empty, try to merge two blocks - current and previous
* But it we try'n to remove first block, then we should set caret to the next block, not previous.
* If we removed the last block, create new one
*/
callbacks.enterPressedOnBlock = function () {
var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;
editor.content.insertBlock({
type : NEW_BLOCK_TYPE,
block : editor.tools[NEW_BLOCK_TYPE].render()
}, true );
editor.toolbar.move();
editor.toolbar.open();
};
callbacks.backspacePressed = function (block, event) {
var backspacePressed_ = function (block, event) {
var currentInputIndex = editor.caret.getCurrentInputIndex(),
range,
selectionLength,
firstLevelBlocksCount;
if (isNativeInput_(event.target)) {
/** If input value is empty - remove block */
if (event.target.value.trim() == '') {
block.remove();
} else {
return;
}
}
if (block.textContent.trim()) {
range = editor.content.getRange();
@ -810,19 +935,27 @@ module.exports = (function (callbacks) {
/**
* This method prevents default behaviour.
*
* We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes.
* @param {Object} event
* @protected
*
* @description We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes.
* Firstly, we need to memorize the caret position. We can do that by getting the range of selection.
* After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node
*
* @param event
*/
callbacks.blockPasteCallback = function (event) {
/** If area is input or textarea then allow default behaviour */
if ( isNativeInput_(event.target) ) {
return;
}
/** Prevent default behaviour */
event.preventDefault();
var editableParent = editor.content.getEditableParent(event.target),
firstLevelBlock = editor.content.getFirstLevelBlock(event.target);
currentNode = editor.content.currentNode;
/** Allow paste when event target placed in Editable element */
if (!editableParent) {
@ -832,7 +965,8 @@ module.exports = (function (callbacks) {
}
/** get html pasted data - dirty data */
var data = event.clipboardData.getData('text/html') || event.clipboardData.getData('text/plain');
var htmlData = event.clipboardData.getData('text/html'),
plainData = event.clipboardData.getData('text/plain');
/** Temporary DIV that is used to work with childs as arrays item */
var div = editor.draw.node('DIV', '', {}),
@ -843,9 +977,16 @@ module.exports = (function (callbacks) {
/** Create fragment, that we paste to range after proccesing */
fragment = document.createDocumentFragment();
cleanData = cleaner.clean(data);
if ( htmlData.trim() != '' ) {
div.innerHTML = cleanData;
cleanData = cleaner.clean(htmlData);
div.innerHTML = cleanData;
} else {
div.innerText = plainData.toString();
}
var node, lastNode;
@ -859,7 +1000,7 @@ module.exports = (function (callbacks) {
}
if (editor.tools[firstLevelBlock.dataset.tool].allowRenderOnPaste) {
if (editor.tools[currentNode.dataset.tool].allowRenderOnPaste) {
if (editor.paste.pasted(event)) return;
@ -891,6 +1032,7 @@ module.exports = (function (callbacks) {
};
/**
* @deprecated
* Sends all mutations to paste handler
*/
callbacks.handleMutationsOnPaste = function (mutations) {
@ -913,9 +1055,14 @@ module.exports = (function (callbacks) {
};
/**
* used by UI module
* Clicks on block settings button
*
* @param {Object} event
* @protected
* @description Opens toolbar settings
*/
callbacks.showSettingsButtonClicked = function () {
callbacks.showSettingsButtonClicked = function (event) {
/**
* Get type of current block
@ -933,6 +1080,21 @@ module.exports = (function (callbacks) {
};
/**
* Check block
* @param target
* @private
*
* @description Checks target is it native input
*/
var isNativeInput_ = function (target) {
var nativeInputAreas = ['INPUT', 'TEXTAREA'];
return (nativeInputAreas.indexOf(target.tagName) != -1);
};
return callbacks;
})({});

View file

@ -49,7 +49,7 @@ module.exports = (function (caret) {
}
/** If Element is INPUT */
if (el.tagName == 'INPUT') {
if (el.contentEditable != 'true') {
el.focus();
return;

View file

@ -2,8 +2,12 @@
* Codex Editor Content Module
* Works with DOM
*
* @module Codex Editor content module
*
* @author Codex Team
* @version 1.3.11
* @version 1.3.13
*
* @description Module works with Elements that have been appended to the main DOM
*/
module.exports = (function (content) {
@ -23,6 +27,7 @@ module.exports = (function (content) {
content.editorAreaHightlighted = null;
/**
* @deprecated
* Synchronizes redactor with original textarea
*/
content.sync = function () {
@ -36,57 +41,10 @@ module.exports = (function (content) {
};
/**
* @deprecated
*/
content.getNodeFocused = function () {
var selection = window.getSelection(),
focused;
if (selection.anchorNode === null) {
return null;
}
if ( selection.anchorNode.nodeType == editor.core.nodeTypes.TAG ) {
focused = selection.anchorNode;
} else {
focused = selection.focusNode.parentElement;
}
if ( !editor.parser.isFirstLevelBlock(focused) ) {
/** Iterate with parent nodes to find first-level*/
var parent = focused.parentNode;
while (parent && !editor.parser.isFirstLevelBlock(parent)) {
parent = parent.parentNode;
}
focused = parent;
}
if (focused != editor.nodes.redactor) {
return focused;
}
return null;
};
/**
* Appends background to the block
*
* @description add CSS class to highlight visually first-level block area
*/
content.markBlock = function () {
@ -96,6 +54,8 @@ module.exports = (function (content) {
/**
* Clear background
*
* @description clears styles that highlights block
*/
content.clearMark = function () {
@ -108,10 +68,13 @@ module.exports = (function (content) {
};
/**
* @private
*
* Finds first-level block
*
* @param {Element} node - selected or clicked in redactors area node
* @protected
*
* @description looks for first-level block.
* gets parent while node is not first-level
*/
content.getFirstLevelBlock = function (node) {
@ -142,7 +105,9 @@ module.exports = (function (content) {
/**
* Trigger this event when working node changed
* @param {Element} targetNode - first-level of this node will be current
* If targetNode is first-level then we set it as current else we look for parents to find first-level
* @protected
*
* @description If targetNode is first-level then we set it as current else we look for parents to find first-level
*/
content.workingNodeChanged = function (targetNode) {
@ -185,27 +150,6 @@ module.exports = (function (content) {
}
/**
* Check is this block was in feed
* If true, than set switched block also covered
*/
if (targetBlock.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE)) {
newBlock.classList.add(editor.ui.className.BLOCK_IN_FEED_MODE);
}
if (targetBlock.classList.contains(editor.ui.className.BLOCK_WITH_ANCHOR)) {
newBlock.classList.add(editor.ui.className.BLOCK_WITH_ANCHOR);
}
/**
* Saving anchor
*/
newBlock.dataset.anchor = targetBlock.dataset.anchor;
/** Replacing */
editor.nodes.redactor.replaceChild(newBlock, targetBlock);
@ -227,7 +171,7 @@ module.exports = (function (content) {
};
/**
* @private
* @protected
*
* Inserts new block to redactor
* Wrapps block into a DIV with BLOCK_CLASSNAME class
@ -243,23 +187,9 @@ module.exports = (function (content) {
var workingBlock = editor.content.currentNode,
newBlockContent = blockData.block,
blockType = blockData.type,
cover = blockData.cover,
anchor = blockData.anchor,
isStretched = blockData.stretched;
var newBlock = editor.content.composeNewBlock(newBlockContent, blockType, isStretched, anchor);
if (cover === true) {
newBlock.classList.add(editor.ui.className.BLOCK_IN_FEED_MODE);
}
if (anchor) {
newBlock.classList.add(editor.ui.className.BLOCK_WITH_ANCHOR);
}
var newBlock = composeNewBlock_(newBlockContent, blockType, isStretched);
if (workingBlock) {
@ -347,7 +277,8 @@ module.exports = (function (content) {
*/
content.switchBlock = function (blockToReplace, newBlock, tool) {
var newBlockComposed = editor.content.composeNewBlock(newBlock, tool);
tool = tool || editor.content.currentNode.dataset.tool;
var newBlockComposed = composeNewBlock_(newBlock, tool);
/** Replacing */
editor.content.replaceBlock(blockToReplace, newBlockComposed);
@ -359,7 +290,8 @@ module.exports = (function (content) {
/**
* Iterates between child noted and looking for #text node on deepest level
* @private
* @protected
*
* @param {Element} block - node where find
* @param {int} postiton - starting postion
* Example: childNodex.length to find from the end
@ -451,8 +383,13 @@ module.exports = (function (content) {
/**
* @private
* @param {Element} block - current plugins render
* @param {String} tool - plugins name
* @param {Boolean} isStretched - make stretched block or not
*
* @description adds necessary information to wrap new created block by first-level holder
*/
content.composeNewBlock = function (block, tool, isStretched, anchor) {
var composeNewBlock_ = function (block, tool, isStretched) {
var newBlock = editor.draw.node('DIV', editor.ui.className.BLOCK_CLASSNAME, {}),
blockContent = editor.draw.node('DIV', editor.ui.className.BLOCK_CONTENT, {});
@ -467,13 +404,13 @@ module.exports = (function (content) {
}
newBlock.dataset.tool = tool;
newBlock.dataset.anchor = anchor || '';
return newBlock;
};
/**
* Returns Range object of current selection
* @protected
*/
content.getRange = function () {
@ -485,8 +422,12 @@ module.exports = (function (content) {
/**
* Divides block in two blocks (after and before caret)
* @private
* @param {Int} inputIndex - target input index
*
* @protected
* @param {int} inputIndex - target input index
*
* @description splits current input content to the separate blocks
* When enter is pressed among the words, that text will be splited.
*/
content.splitBlock = function (inputIndex) {
@ -593,6 +534,12 @@ module.exports = (function (content) {
/**
* Merges two blocks current and target
* If target index is not exist, then previous will be as target
*
* @protected
* @param {int} currentInputIndex
* @param {int} targetInputIndex
*
* @description gets two inputs indexes and merges into one
*/
content.mergeBlocks = function (currentInputIndex, targetInputIndex) {
@ -621,7 +568,7 @@ module.exports = (function (content) {
};
/**
* @private
* @deprecated
*
* Callback for HTML Mutations
* @param {Array} mutation - Mutation Record
@ -644,7 +591,7 @@ module.exports = (function (content) {
};
/**
* @private
* @deprecated
*
* gets only text/plain content of node
* @param {Element} target - HTML node
@ -679,7 +626,7 @@ module.exports = (function (content) {
};
/**
* @private
* @deprecated
*
* Sanitizes HTML content
* @param {Element} target - inserted element

View file

@ -2,7 +2,7 @@
* Codex Editor Core
*
* @author Codex Team
* @version 1.1.2
* @version 1.1.3
*/
module.exports = (function (core) {
@ -37,12 +37,6 @@ module.exports = (function (core) {
}
if (userSettings.uploadImagesUrl) {
editor.settings.uploadImagesUrl = userSettings.uploadImagesUrl;
}
editor.hideToolbar = userSettings.hideToolbar;
editor.nodes.textarea = document.getElementById(userSettings.textareaId || editor.settings.textareaId);
@ -146,9 +140,9 @@ module.exports = (function (core) {
/**
* Native Ajax
*/
core.ajax = function (data) {
core.ajax = function (settings) {
if (!data || !data.url) {
if (!settings || !settings.url) {
return;
@ -156,44 +150,65 @@ module.exports = (function (core) {
var XMLHTTP = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),
successFunction = function () {},
params = '',
obj;
encodedString,
isFormData,
prop;
data.async = true;
data.type = data.type || 'GET';
data.data = data.data || '';
data['content-type'] = data['content-type'] || 'application/json; charset=utf-8';
successFunction = data.success || successFunction ;
settings.async = true;
settings.type = settings.type || 'GET';
settings.data = settings.data || '';
settings['content-type'] = settings['content-type'] || 'application/json; charset=utf-8';
successFunction = settings.success || successFunction ;
if (data.type == 'GET' && data.data) {
if (settings.type == 'GET' && settings.data) {
data.url = /\?/.test(data.url) ? data.url + '&' + data.data : data.url + '?' + data.data;
settings.url = /\?/.test(settings.url) ? settings.url + '&' + settings.data : settings.url + '?' + settings.data;
} else {
for(obj in data.data) {
encodedString = '';
for(prop in settings.data) {
params += (obj + '=' + encodeURIComponent(data.data[obj]) + '&');
encodedString += (prop + '=' + encodeURIComponent(settings.data[prop]) + '&');
}
}
if (data.withCredentials) {
if (settings.withCredentials) {
XMLHTTP.withCredentials = true;
}
if (data.beforeSend && typeof data.beforeSend == 'function') {
if (settings.beforeSend && typeof settings.beforeSend == 'function') {
data.beforeSend.call();
settings.beforeSend.call();
}
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.open( data.type, data.url, data.async );
XMLHTTP.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
XMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
XMLHTTP.onreadystatechange = function () {
@ -205,7 +220,18 @@ module.exports = (function (core) {
};
XMLHTTP.send(params);
if (isFormData) {
// Sending FormData
XMLHTTP.send(settings.data);
} else {
// POST requests
XMLHTTP.send(encodedString);
}
};
@ -254,6 +280,17 @@ module.exports = (function (core) {
};
/**
* Function for checking is it FormData object to send.
* @param {Object} object to check
* @return boolean
*/
var isFormData_ = function (object) {
return object instanceof FormData;
};
return core;
})({});

View file

@ -2,7 +2,7 @@
* Codex Editor Paste module
*
* @author Codex Team
* @version 1.0
* @version 1.1.1
*/
module.exports = function (paste) {
@ -25,6 +25,7 @@ module.exports = function (paste) {
tools[tool].renderOnPastePatterns.map(function (pattern) {
patterns.push(pattern);
});
@ -69,7 +70,10 @@ module.exports = function (paste) {
patterns.map( function (pattern) {
if (pattern.regex.test(string)) {
var execArray = pattern.regex.exec(string),
match = execArray && execArray[0];
if ( match && match === string.trim()) {
/** current block is not empty */
if ( content.textContent.trim() && plugin == editor.settings.initialBlockPlugin ) {

View file

@ -149,9 +149,7 @@ module.exports = (function (renderer) {
/** New parser */
var block,
tool = toolData.tool,
pluginName = tool.type,
anchor = tool.anchor,
cover = tool.cover;
pluginName = tool.type;
/** Get first key of object that stores plugin name */
// for (var pluginName in blockData) break;
@ -195,9 +193,7 @@ module.exports = (function (renderer) {
return {
type : pluginName,
block : block,
stretched : stretched,
cover : cover,
anchor : anchor
stretched : stretched
};
};

View file

@ -86,8 +86,7 @@ module.exports = (function (saver) {
saver.makeFormDataFromBlocks = function (block) {
var pluginName = block.dataset.tool,
anchor = block.dataset.anchor;
var pluginName = block.dataset.tool;
/** Check for plugin existance */
if (!editor.tools[pluginName]) {
@ -108,22 +107,17 @@ module.exports = (function (saver) {
pluginsContent = blockContent.childNodes[0],
savedData,
position,
output,
coverFlag = false;
output;
/** 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;
anchor = codex.editor.state.blocks.items[position].anchor;
} else {
savedData = editor.tools[pluginName].save(pluginsContent);
coverFlag = block.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE);
if (editor.tools[pluginName].validate) {
@ -141,13 +135,9 @@ module.exports = (function (saver) {
output = {
type : pluginName,
anchor : anchor,
data : savedData
};
/** Marks Blocks that will be in main page */
output.cover = coverFlag;
editor.state.jsonOutput.push(output);
};

View file

@ -13,8 +13,6 @@ module.exports = (function (settings) {
settings.setting = null;
settings.actions = null;
settings.cover = null;
/**
* Append and open settings
*/
@ -27,7 +25,7 @@ module.exports = (function (settings) {
if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings ) {
editor.core.log(`Plugin «${toolType}» has no settings`, 'warn');
// editor.nodes.pluginSettings.innerHTML = `Плагин «${toolType}» не имеет настроек`;
editor.nodes.pluginSettings.innerHTML = `Плагин «${toolType}» не имеет настроек`;
} else {
@ -42,7 +40,6 @@ module.exports = (function (settings) {
/** Open settings block */
editor.nodes.blockSettings.classList.add('opened');
editor.toolbar.settings.addDefaultSettings();
this.opened = true;
};
@ -67,7 +64,6 @@ module.exports = (function (settings) {
if ( !this.opened ) {
this.open(toolType);
editor.anchors.settingsOpened(editor.content.currentNode);
} else {
@ -77,125 +73,6 @@ module.exports = (function (settings) {
};
/**
* This function adds default core settings
*/
settings.addDefaultSettings = function () {
/** list of default settings */
var feedModeToggler,
anchorInput;
/** Clear block and append initialized settings */
editor.nodes.defaultSettings.innerHTML = '';
/** Init all default setting buttons */
feedModeToggler = editor.toolbar.settings.makeFeedModeToggler();
anchorInput = editor.toolbar.settings.makeAnchorInput();
/**
* Fill defaultSettings
*/
/**
* Input for anchor for block
*/
editor.nodes.defaultSettings.appendChild(anchorInput);
/**
* Button that enables/disables Feed-mode
* Feed-mode means that block will be showed in articles-feed like cover
*/
editor.nodes.defaultSettings.appendChild(feedModeToggler);
};
/**
* Cover setting.
* This tune highlights block, so that it may be used for showing target block on main page
* Draw different setting when block is marked for main page
* If TRUE, then we show button that removes this selection
* Also defined setting "Click" events will be listened and have separate callbacks
*
* @return {Element} node/button that we place in default settings block
*/
settings.makeFeedModeToggler = function () {
var isFeedModeActivated = editor.toolbar.settings.isFeedModeActivated(),
setting,
data;
if (!isFeedModeActivated) {
data = {
innerHTML : '<i class="ce-icon-newspaper"></i>Вывести в ленте'
};
} else {
data = {
innerHTML : '<i class="ce-icon-newspaper"></i>Не выводить в ленте'
};
}
setting = editor.draw.node('DIV', editor.ui.className.SETTINGS_ITEM, data);
editor.listeners.add(setting, 'click', editor.toolbar.settings.updateFeedMode, false);
return setting;
};
/**
* Updates Feed-mode
*/
settings.updateFeedMode = function () {
var currentNode = editor.content.currentNode;
currentNode.classList.toggle(editor.ui.className.BLOCK_IN_FEED_MODE);
editor.toolbar.settings.close();
};
settings.isFeedModeActivated = function () {
var currentBlock = editor.content.currentNode;
if (currentBlock) {
return currentBlock.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE);
} else {
return false;
}
};
settings.makeAnchorInput = function () {
var anchorWrapper = editor.draw.node('div', 'ce-settings__anchor-wrapper ce-settings__item', {}),
hash = editor.draw.node('i', 'ce-settings__anchor-hash', {}),
anchor = editor.draw.node('input', 'ce-settings__anchor-input', { placeholder: 'Якорь' });
editor.listeners.add(anchor, 'keydown', editor.anchors.keyDownOnAnchorInput );
editor.listeners.add(anchor, 'keyup', editor.anchors.keyUpOnAnchorInput );
editor.listeners.add(anchor, 'input', editor.anchors.anchorChanged );
editor.listeners.add(anchor, 'blur', editor.anchors.anchorChanged );
anchorWrapper.appendChild(hash);
anchorWrapper.appendChild(anchor);
editor.anchors.input = anchor;
return anchorWrapper;
};
/**
* Here we will draw buttons and add listeners to components
*/

View file

@ -46,6 +46,8 @@ module.exports = (function (toolbox) {
/** toolbox state */
editor.toolbar.toolbox.opened = false;
editor.toolbar.current = null;
};
toolbox.leaf = function () {
@ -75,21 +77,14 @@ module.exports = (function (toolbox) {
} else {
nextToolIndex = tools.indexOf(currentTool) + 1;
nextToolIndex = (tools.indexOf(currentTool) + 1) % tools.length;
visibleTool = tools[nextToolIndex];
while (!editor.tools[visibleTool].displayInToolbox) {
nextToolIndex++;
nextToolIndex = (nextToolIndex + 1) % tools.length;
visibleTool = tools[nextToolIndex];
if ( nextToolIndex == tools.length ) {
nextToolIndex = 0;
visibleTool = tools[nextToolIndex];
}
}
}

View file

@ -2,14 +2,17 @@
*
* Codex.Editor Transport Module
*
* @author Codex Team
* @version 1.0
* @copyright 2017 Codex-Team
* @version 1.2.0
*/
module.exports = (function (transport) {
let editor = codex.editor;
/**
* @type {null} | {DOMElement} input - keeps input element in memory
*/
transport.input = null;
/**
@ -17,13 +20,14 @@ module.exports = (function (transport) {
*/
transport.arguments = null;
/**
* Prepares input element where will be files
*/
transport.prepare = function () {
var input = document.createElement('INPUT');
let input = editor.draw.node( 'INPUT', '', { type : 'file' } );
input.type = 'file';
editor.listeners.add(input, 'change', editor.transport.fileSelected);
editor.transport.input = input;
};
@ -32,10 +36,10 @@ module.exports = (function (transport) {
transport.clearInput = function () {
/** Remove old input */
this.input = null;
transport.input = null;
/** Prepare new one */
this.prepare();
transport.prepare();
};
@ -46,65 +50,67 @@ module.exports = (function (transport) {
transport.fileSelected = function () {
var input = this,
i,
files = input.files,
formdData = new FormData();
formData = new FormData();
formdData.append('files', files[0], files[0].name);
if (editor.transport.arguments.multiple === false) {
editor.transport.ajax({
data : formdData,
formData.append('files', files[0], files[0].name);
} else {
for ( i = 0; i < files.length; i++) {
formData.append('files[]', files[i], files[i].name);
}
}
editor.core.ajax({
type : 'POST',
data : formData,
url : editor.transport.arguments.url,
beforeSend : editor.transport.arguments.beforeSend,
success : editor.transport.arguments.success,
error : editor.transport.arguments.error
});
/** Clear input */
transport.clearInput();
};
/**
* Use plugin callbacks
* @protected
*
* @param {Object} args - can have :
* @param {String} args.url - fetch URL
* @param {Function} args.beforeSend - function calls before sending ajax
* @param {Function} args.success - success callback
* @param {Function} args.error - on error handler
* @param {Boolean} args.multiple - allow select several files
* @param {String} args.accept - adds accept attribute
*/
transport.selectAndUpload = function (args) {
this.arguments = args;
this.input.click();
transport.arguments = args;
};
if ( args.multiple === true) {
/**
* Ajax requests module
* @todo use core.ajax
*/
transport.ajax = function (params) {
transport.input.setAttribute('multiple', 'multiple');
var xhr = new XMLHttpRequest(),
beforeSend = typeof params.beforeSend == 'function' ? params.beforeSend : function () {},
success = typeof params.success == 'function' ? params.success : function () {},
error = typeof params.error == 'function' ? params.error : function () {};
}
beforeSend();
if ( args.accept ) {
xhr.open('POST', editor.settings.uploadImagesUrl, true);
transport.input.setAttribute('accept', args.accept);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
}
xhr.onload = function () {
if (xhr.status === 200) {
success(xhr.responseText);
} else {
editor.core.log('request error: %o', xhr);
error();
}
};
xhr.send(params.data);
this.clearInput();
transport.input.click();
};

View file

@ -2,7 +2,7 @@
* Codex Editor UI module
*
* @author Codex Team
* @version 1.1
* @version 1.2.0
*/
module.exports = (function (ui) {
@ -34,16 +34,6 @@ module.exports = (function (ui) {
*/
BLOCK_HIGHLIGHTED : 'ce-block--focused',
/**
* @const {String} - highlights covered blocks
*/
BLOCK_IN_FEED_MODE : 'ce-block--feed-mode',
/**
* @const {String} - Block with anchor
*/
BLOCK_WITH_ANCHOR : 'ce-block--anchor',
/**
* @const {String} - for all default settings
*/
@ -56,92 +46,56 @@ module.exports = (function (ui) {
*
* Making main interface
*/
ui.make = function () {
ui.prepare = function () {
var wrapper,
toolbar,
toolbarContent,
redactor,
blockButtons,
blockSettings,
showSettingsButton,
showTrashButton,
toolbox,
plusButton;
return new Promise(function (resolve) {
/** Make editor wrapper */
wrapper = editor.draw.wrapper();
let wrapper = editor.draw.wrapper(),
redactor = editor.draw.redactor(),
toolbar = makeToolBar_();
/** Append editor wrapper after initial textarea */
editor.core.insertAfter(editor.nodes.textarea, wrapper);
wrapper.appendChild(toolbar);
wrapper.appendChild(redactor);
/** Append block with notifications to the document */
editor.notifications.createHolder();
/** Save created ui-elements to static nodes state */
editor.nodes.wrapper = wrapper;
editor.nodes.redactor = redactor;
/** Make toolbar and content-editable redactor */
toolbar = editor.draw.toolbar();
toolbarContent = editor.draw.toolbarContent();
plusButton = editor.draw.plusButton();
showSettingsButton = editor.draw.settingsButton();
showTrashButton = editor.toolbar.settings.makeRemoveBlockButton();
blockSettings = editor.draw.blockSettings();
blockButtons = editor.draw.blockButtons();
toolbox = editor.draw.toolbox();
redactor = editor.draw.redactor();
/** Append editor wrapper with redactor zone after initial textarea */
editor.core.insertAfter(editor.nodes.textarea, wrapper);
/** settings */
var defaultSettings = editor.draw.defaultSettings(),
pluginSettings = editor.draw.pluginsSettings();
resolve();
/** Add default and plugins settings */
blockSettings.appendChild(pluginSettings);
blockSettings.appendChild(defaultSettings);
})
/** Make blocks buttons
* This block contains settings button and remove block button
*/
blockButtons.appendChild(showSettingsButton);
blockButtons.appendChild(showTrashButton);
blockButtons.appendChild(blockSettings);
/** Append plus button */
toolbarContent.appendChild(plusButton);
/** Appending toolbar tools */
toolbarContent.appendChild(toolbox);
/** Appending first-level block buttons */
toolbar.appendChild(blockButtons);
/** Append toolbarContent to toolbar */
toolbar.appendChild(toolbarContent);
wrapper.appendChild(toolbar);
wrapper.appendChild(redactor);
/** Save created ui-elements to static nodes state */
editor.nodes.wrapper = wrapper;
editor.nodes.toolbar = toolbar;
editor.nodes.plusButton = plusButton;
editor.nodes.toolbox = toolbox;
editor.nodes.blockSettings = blockSettings;
editor.nodes.pluginSettings = pluginSettings;
editor.nodes.defaultSettings = defaultSettings;
editor.nodes.showSettingsButton = showSettingsButton;
editor.nodes.showTrashButton = showTrashButton;
editor.nodes.redactor = redactor;
/** Add toolbox tools */
.then(addTools_)
/** Make container for inline toolbar */
editor.ui.makeInlineToolbar();
.then(makeInlineToolbar_)
/** fill in default settings */
editor.toolbar.settings.addDefaultSettings();
/** Add inline toolbar tools */
.then(addInlineToolbarTools_)
/** Draw wrapper for notifications */
.then(makeNotificationHolder_)
/** Add eventlisteners to redactor elements */
.then(bindEvents_)
.catch( function () {
editor.core.log("Can't draw editor interface");
});
};
ui.makeInlineToolbar = function () {
/**
* @private
* Draws inline toolbar zone
*/
var makeInlineToolbar_ = function () {
var container = editor.draw.inlineToolbar();
@ -162,11 +116,90 @@ module.exports = (function (ui) {
};
var makeToolBar_ = function () {
let toolbar = editor.draw.toolbar(),
blockButtons = makeToolbarSettings_(),
toolbarContent = makeToolbarContent_();
/** Appending first-level block buttons */
toolbar.appendChild(blockButtons);
/** Append toolbarContent to toolbar */
toolbar.appendChild(toolbarContent);
/** Make toolbar global */
editor.nodes.toolbar = toolbar;
return toolbar;
};
var makeToolbarContent_ = function () {
let toolbarContent = editor.draw.toolbarContent(),
toolbox = editor.draw.toolbox(),
plusButton = editor.draw.plusButton();
/** Append plus button */
toolbarContent.appendChild(plusButton);
/** Appending toolbar tools */
toolbarContent.appendChild(toolbox);
/** Make Toolbox and plusButton global */
editor.nodes.toolbox = toolbox;
editor.nodes.plusButton = plusButton;
return toolbarContent;
};
var makeToolbarSettings_ = function () {
let blockSettings = editor.draw.blockSettings(),
blockButtons = editor.draw.blockButtons(),
defaultSettings = editor.draw.defaultSettings(),
showSettingsButton = editor.draw.settingsButton(),
showTrashButton = editor.toolbar.settings.makeRemoveBlockButton(),
pluginSettings = editor.draw.pluginsSettings();
/** Add default and plugins settings */
blockSettings.appendChild(pluginSettings);
blockSettings.appendChild(defaultSettings);
/**
* Make blocks buttons
* This block contains settings button and remove block button
*/
blockButtons.appendChild(showSettingsButton);
blockButtons.appendChild(showTrashButton);
blockButtons.appendChild(blockSettings);
/** Make BlockSettings, PluginSettings, DefaultSettings global */
editor.nodes.blockSettings = blockSettings;
editor.nodes.pluginSettings = pluginSettings;
editor.nodes.defaultSettings = defaultSettings;
editor.nodes.showSettingsButton = showSettingsButton;
editor.nodes.showTrashButton = showTrashButton;
return blockButtons;
};
/** Draw notifications holder */
var makeNotificationHolder_ = function () {
/** Append block with notifications to the document */
editor.nodes.notifications = editor.notifications.createHolder();
};
/**
* @private
* Append tools passed in editor.tools
*/
ui.addTools = function () {
var addTools_ = function () {
var tool,
toolName,
@ -178,7 +211,7 @@ module.exports = (function (ui) {
editor.tools[toolName] = tool;
if (!tool.iconClassname) {
if (!tool.iconClassname && tool.displayInToolbox) {
editor.core.log('Toolbar icon classname missed. Tool %o skipped', 'warn', toolName);
continue;
@ -209,15 +242,9 @@ module.exports = (function (ui) {
}
/**
* Add inline toolbar tools
*/
editor.ui.addInlineToolbarTools();
};
ui.addInlineToolbarTools = function () {
var addInlineToolbarTools_ = function () {
var tools = {
@ -265,7 +292,7 @@ module.exports = (function (ui) {
* @private
* Bind editor UI events
*/
ui.bindEvents = function () {
var bindEvents_ = function () {
editor.core.log('ui.bindEvents fired', 'info');
@ -297,12 +324,6 @@ module.exports = (function (ui) {
*/
editor.listeners.add(editor.nodes.showSettingsButton, 'click', editor.callback.showSettingsButtonClicked, false );
/**
* @deprecated ( but now in use for syncronization );
* Any redactor changes: keyboard input, mouse cut/paste, drag-n-drop text
*/
// editor.nodes.redactor.addEventListener('input', editor.callback.redactorInputEvent, false );
/** Bind click listeners on toolbar buttons */
for (var button in editor.nodes.toolbarButtons) {
@ -351,8 +372,20 @@ module.exports = (function (ui) {
var redactor = editor.nodes.redactor;
editor.state.inputs = [];
/** Save all inputs in global variable state */
editor.state.inputs = redactor.querySelectorAll('[contenteditable], input');
var inputs = redactor.querySelectorAll('[contenteditable], input, textarea');
Array.prototype.map.call(inputs, function (current) {
if (!current.type || current.type == 'text' || current.type == 'textarea') {
editor.state.inputs.push(current);
}
});
};

View file

@ -1,5 +1,7 @@
.ce-code {
display: block;
width: 100%;
min-height: 100px;
border: 1px solid #ebeef3;
border-radius: 3px;
background: #fdfdff !important;
@ -13,6 +15,9 @@
line-height: 1.5em;
color: #325179;
font-size: .8em;
resize: vertical;
outline: none;
}

View file

@ -14,17 +14,32 @@ var code = (function(code_plugin) {
*/
var make_ = function (data) {
var tag = codex.editor.draw.node('CODE', [baseClass], {});
var tag = codex.editor.draw.node('TEXTAREA', [baseClass], {});
if (data && data.text) {
tag.innerHTML = data.text;
tag.value = data.text;
}
tag.contentEditable = true;
return tag;
};
/**
* Escapes HTML chars
*
* @param {string} input
* @return {string} escaped string
*/
var escapeHTML_ = function (input) {
var div = document.createElement('DIV'),
text = document.createTextNode(input);
div.appendChild(text);
return div.innerHTML;
};
/**
* Method to render HTML block from JSON
*/
@ -38,9 +53,12 @@ var code = (function(code_plugin) {
*/
code_plugin.save = function (blockContent) {
var data = {
text : blockContent.innerHTML
};
var escaped = escapeHTML_(blockContent.value),
data = {
text : escaped
};
return data;
};

View file

@ -7,7 +7,7 @@ var embed = function(embed_plugin){
var methods = {
addInternal: function (content) {
codex.editor.content.switchBlock(codex.editor.content.currentNode, content, 'video_extended');
codex.editor.content.switchBlock(codex.editor.content.currentNode, content);
var blockContent = codex.editor.content.currentNode.childNodes[0];
blockContent.classList.add('embed__loader');
@ -48,76 +48,11 @@ var embed = function(embed_plugin){
};
var services = {
vimeo: {
regex: /(?:http[s]?:\/\/)?(?:www.)?vimeo\.co(?:.+\/([^\/]\d+)(?:#t=[\d]+)?s?$)/,
html: "<iframe src=\"https://player.vimeo.com/video/<%= remote_id %>?title=0&byline=0\" style=\"width:100%;\" height=\"320\" frameborder=\"0\"></iframe>",
height: 320,
width: 580
},
youtube: {
regex: /^.*(?:(?:youtu\.be\/)|(?:youtube\.com)\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*)(?:[\?\&]t\=(\d*)|)/,
html: "<iframe src=\"https://www.youtube.com/embed/<%= remote_id %>\" style=\"width:100%;\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>",
height: 320,
width: 580
},
vk : {
regex: /^https?.+vk?.com\/feed\?w=wall\d+_\d+/,
html: "<iframe src=\"https://tjournal.ru/proxy/video/<%= remote_id %>?rel=0&showinfo=0&enablejsapi=1&autoplay=1\" width=\"580\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>"
},
coub: {
regex: /https?:\/\/coub\.com\/view\/([^\/\?\&]+)/,
html: "<iframe src=\"//coub.com/embed/<%= remote_id %>\" style=\"width:100%;\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>",
height: 320,
width: 580
},
vine: {
regex: /https?:\/\/vine\.co\/v\/([^\/\?\&]+)/,
html: "<iframe src=\"https://vine.co/v/<%= remote_id %>/embed/simple/\" style=\"width:100%;\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>",
height: 320,
width: 580
},
imgur: {
regex: /https?:\/\/(?:i\.)?imgur\.com.*\/([a-zA-Z0-9]+)(?:\.gifv)?/,
html: "<iframe allowfullscreen=\"true\" scrolling=\"no\" src=\"http://imgur.com/<%= remote_id %>/embed\" id=\"imgur-embed-iframe-pub-<%= remote_id %>\" class=\"imgur-embed-iframe-pub\" style=\"height: 500px; width: 100%; border: 1px solid #000\"></iframe>",
height: 500,
width: 540
},
gfycat: {
regex: /https?:\/\/gfycat\.com(?:\/detail)?\/([a-zA-Z]+)/,
html: "<iframe src='https://gfycat.com/ifr/<%= remote_id %>' frameborder='0' scrolling='no' style=\"width:100%;\" height='436' allowfullscreen ></iframe>",
height: 436,
width: 580
},
'twitch-channel': {
regex: /https?:\/\/www.twitch.tv\/([^\/\?\&]*)/,
html: "<iframe src=\"https://player.twitch.tv/?channel=<%= remote_id %>\" frameborder=\"0\" allowfullscreen=\"true\" scrolling=\"no\" height=\"366\" style=\"width:100%;\"></iframe>",
height: 366,
width: 600
},
'twitch-video': {
regex: /https?:\/\/www.twitch.tv\/(?:[^\/\?\&]*\/v|videos)\/([0-9]*)/,
html: "<iframe src=\"https://player.twitch.tv/?video=v<%= remote_id %>\" frameborder=\"0\" allowfullscreen=\"true\" scrolling=\"no\" height=\"366\" style=\"width:100%;\"></iframe>",
height: 366,
width: 600
},
'yandex-music-album': {
regex: /https?:\/\/music.yandex.ru\/album\/([0-9]*)/,
html: "<iframe frameborder=\"0\" style=\"border:none;width:540px;height:400px;\" style=\"width:100%;\" height=\"400\" src=\"https://music.yandex.ru/iframe/#album/<%= remote_id %>/\"></iframe>",
height: 400,
width: 540
},
'yandex-music-track': {
regex: /https?:\/\/music.yandex.ru\/album\/([0-9]*)\/track\/([0-9]*)/,
html: "<iframe frameborder=\"0\" style=\"border:none;width:540px;height:100px;\" style=\"width:100%;\" height=\"100\" src=\"https://music.yandex.ru/iframe/#track/<%= remote_id %>/\"></iframe>",
height: 100,
width: 540
},
'yandex-music-playlist': {
regex: /https?:\/\/music.yandex.ru\/users\/([^\/\?\&]*)\/playlists\/([0-9]*)/,
html: "<iframe frameborder=\"0\" style=\"border:none;width:540px;height:400px;\" width=\"540\" height=\"400\" src=\"https://music.yandex.ru/iframe/#playlist/<%= remote_id %>/show/cover/description/\"></iframe>",
height: 400,
width: 540
}
};

View file

@ -39,7 +39,7 @@ var header = (function(header_plugin) {
new_header.setAttribute('data-placeholder', 'Заголовок');
new_header.dataset.headerData = type;
codex.editor.content.switchBlock(old_header, new_header, 'heading_styled');
codex.editor.content.switchBlock(old_header, new_header);
/** Close settings after replacing */
codex.editor.toolbar.settings.close();

View file

@ -2,7 +2,7 @@
* Image plugin for codex-editor
* @author CodeX Team <team@ifmo.su>
*
* @version 1.2.0
* @version 1.3.0
*/
var image = (function(image_plugin) {
@ -220,15 +220,19 @@ var image = (function(image_plugin) {
*/
var uploadButtonClicked_ = function(event) {
var beforeSend = uploadingCallbacks_.ByClick.beforeSend,
var url = image_plugin.config.uploadImage,
beforeSend = uploadingCallbacks_.ByClick.beforeSend,
success = uploadingCallbacks_.ByClick.success,
error = uploadingCallbacks_.ByClick.error;
/** Define callbacks */
codex.editor.transport.selectAndUpload({
beforeSend: beforeSend,
success: success,
error: error
url : url,
multiple : false,
accept : 'image/*',
beforeSend : beforeSend,
success : success,
error : error
});
};
@ -272,11 +276,7 @@ var image = (function(image_plugin) {
/**
* Save settings in dataset
*/
if (img.classList.contains(elementClasses_.imageWrapperBordered)) {
wrapper.dataset.bordered = true;
} else {
wrapper.dataset.bordered = false;
}
wrapper.dataset.bordered = img.classList.contains(elementClasses_.imageWrapperBordered);
setTimeout(function() {
codex.editor.toolbar.settings.close();
@ -302,15 +302,7 @@ var image = (function(image_plugin) {
clickedSettingsItem.classList.toggle(elementClasses_.toggled);
if (img.classList.contains(elementClasses_.uploadedImage.stretched)) {
wrapper.dataset.stretched = true;
} else {
wrapper.dataset.stretched = false;
}
wrapper.dataset.stretched = img.classList.contains(elementClasses_.uploadedImage.stretched);
setTimeout(function() {
codex.editor.toolbar.settings.close();
@ -369,7 +361,7 @@ var image = (function(image_plugin) {
var newImage = make_(data);
codex.editor.content.switchBlock(image.holder, newImage, 'image_extended');
codex.editor.content.switchBlock(image.holder, newImage);
newImage.classList.add(elementClasses_.imagePreview);
/**
@ -414,7 +406,7 @@ var image = (function(image_plugin) {
var oldHolder = image.holder;
var form = ui_.makeForm();
codex.editor.content.switchBlock(oldHolder, form, 'image_extended');
codex.editor.content.switchBlock(oldHolder, form);
}
},
@ -428,9 +420,7 @@ var image = (function(image_plugin) {
*/
uploadImageFromUrl : function(path) {
var ajaxUrl = image_plugin.config.uploadUrl,
file,
image,
var image,
current = codex.editor.content.currentNode,
beforeSend,
success_callback;
@ -479,13 +469,13 @@ var image = (function(image_plugin) {
var img = image.querySelector('img');
codex.editor.content.switchBlock(codex.editor.content.currentNode, image, 'image_extended');
codex.editor.content.switchBlock(codex.editor.content.currentNode, image);
};
/** Preparing data for XMLHTTP */
var data = {
url: image_plugin.config.uploadUrl,
url: image_plugin.config.uploadFromUrl,
type: "POST",
data : {
url: path

View file

@ -8,7 +8,7 @@ var instagram = (function(instagram_plugin) {
render : function(content) {
codex.editor.content.switchBlock(codex.editor.content.currentNode, content, 'instagram');
codex.editor.content.switchBlock(codex.editor.content.currentNode, content);
setTimeout(function() {
window.instgrm.Embeds.process();
@ -144,7 +144,7 @@ var instagram = (function(instagram_plugin) {
instagram_plugin.pastePatterns = [
{
type: 'instagram',
regex: /http?.+instagram.com\/p\/([a-zA-Z0-9]*)/,
regex: /http?.+instagram.com\/p\/([a-zA-Z0-9]*)\S*/,
callback: instagram_plugin.urlPastedCallback
}
];

View file

@ -74,7 +74,7 @@ var list = (function(list_plugin) {
newEditable.innerHTML = oldEditable.innerHTML;
newEditable.classList.add(elementClasses_.pluginWrapper);
codex.editor.content.switchBlock(currentBlock, newEditable, 'list');
codex.editor.content.switchBlock(currentBlock, newEditable);
},
keyDown: function (e) {

View file

@ -157,7 +157,7 @@ var quote = (function(quote_plugin) {
/* make Author contentEditable */
author.contentEditable = 'true';
author.textContent = data.cite || '';
author.innerHTML = data.cite || '';
/* Appending created components */
wrapper.dataset.quoteStyle = 'withCaption';
@ -200,11 +200,11 @@ var quote = (function(quote_plugin) {
/* make author block contentEditable */
author.contentEditable = 'true';
author.textContent = data.cite || '';
author.innerHTML = data.cite || '';
/* Author's position and job */
job.contentEditable = 'true';
job.textContent = data.caption || '';
job.innerHTML = data.caption || '';
var authorsWrapper = ui_.makeBlock('DIV', [elementClasses_.withPhoto.authorHolder]);
authorsWrapper.appendChild(author);
@ -234,20 +234,34 @@ var quote = (function(quote_plugin) {
quote ;
/** Simple quote text placed in Blockquote tag*/
if ( currentNode.dataset.quoteStyle == 'simple' )
if ( currentNode.dataset.quoteStyle == 'simple' ){
quote = currentNode.innerHTML || '';
else
} else {
quote = currentNode.querySelector('.' + elementClasses_.quoteText).innerHTML;
if (job)
job = job.textContent || '';
}
if (author)
author = author.textContent || '';
if (job){
job = job.innerHTML || '';
}
if (author){
author = author.innerHTML || '';
}
if (photo){
if (photo)
photo = photo.dataset.bigUrl;
}
var data = {
style : currentNode.dataset.quoteStyle,
text : quote,

View file

@ -0,0 +1,7 @@
<svg class="raw-plugin-icon-svg" width="19px" height="6px" viewBox="0 0 19 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>RAW</title>
<defs></defs>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M3.17724609,0.00146484375 C4.53955078,0.00146484375 5.31298828,0.766113281 5.31298828,1.93505859 C5.31298828,2.84033203 4.75048828,3.42480469 4.22753906,3.62255859 L5.43164062,6 L4.01660156,6 L2.97509766,3.81591797 L1.98632812,3.81591797 L1.98632812,6 L0.733886719,6 L0.733886719,0.00146484375 L3.17724609,0.00146484375 Z M1.98632812,2.84912109 L2.97509766,2.84912109 C3.59912109,2.84912109 4.00341797,2.55029297 4.00341797,1.95263672 C4.00341797,1.34179688 3.5859375,1.01660156 2.99267578,1.01660156 L1.98632812,1.01660156 L1.98632812,2.84912109 Z M6.85976562,6 L5.58095703,6 L7.56728515,0.00146484375 L9.0790039,0.00146484375 L11.056543,6 L9.70302734,6 L9.26357421,4.54541016 L7.30800781,4.54541016 L6.85976562,6 Z M8.31874999,1.22753906 L8.26162109,1.22753906 L7.55849609,3.57421875 L9.01748046,3.57421875 L8.31874999,1.22753906 Z M13.7107422,6 L12.5374023,6 L10.9641601,0.00146484375 L12.3308594,0.00146484375 L13.187793,4.20263672 L13.2493164,4.20263672 L14.2820312,0.00146484375 L15.3542969,0.00146484375 L16.3914062,4.20263672 L16.4529297,4.20263672 L17.3054687,0.00146484375 L18.6677734,0.00146484375 L17.0989258,6 L15.9255859,6 L14.8445312,2.00537109 L14.7961914,2.00537109 L13.7107422,6 Z" id="RAW" fill="#000000"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,7 @@
<svg class="raw-plugin-icon-svg" width="19px" height="6px" viewBox="0 0 19 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>RAW</title>
<defs></defs>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M3.17724609,0.00146484375 C4.53955078,0.00146484375 5.31298828,0.766113281 5.31298828,1.93505859 C5.31298828,2.84033203 4.75048828,3.42480469 4.22753906,3.62255859 L5.43164062,6 L4.01660156,6 L2.97509766,3.81591797 L1.98632812,3.81591797 L1.98632812,6 L0.733886719,6 L0.733886719,0.00146484375 L3.17724609,0.00146484375 Z M1.98632812,2.84912109 L2.97509766,2.84912109 C3.59912109,2.84912109 4.00341797,2.55029297 4.00341797,1.95263672 C4.00341797,1.34179688 3.5859375,1.01660156 2.99267578,1.01660156 L1.98632812,1.01660156 L1.98632812,2.84912109 Z M6.85976562,6 L5.58095703,6 L7.56728515,0.00146484375 L9.0790039,0.00146484375 L11.056543,6 L9.70302734,6 L9.26357421,4.54541016 L7.30800781,4.54541016 L6.85976562,6 Z M8.31874999,1.22753906 L8.26162109,1.22753906 L7.55849609,3.57421875 L9.01748046,3.57421875 L8.31874999,1.22753906 Z M13.7107422,6 L12.5374023,6 L10.9641601,0.00146484375 L12.3308594,0.00146484375 L13.187793,4.20263672 L13.2493164,4.20263672 L14.2820312,0.00146484375 L15.3542969,0.00146484375 L16.3914062,4.20263672 L16.4529297,4.20263672 L17.3054687,0.00146484375 L18.6677734,0.00146484375 L17.0989258,6 L15.9255859,6 L14.8445312,2.00537109 L14.7961914,2.00537109 L13.7107422,6 Z" id="RAW" fill="#ffffff"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

36
plugins/raw/raw.css Normal file
View file

@ -0,0 +1,36 @@
.raw-plugin__input {
display: block;
width: 100%;
min-height: 200px;
border: 0;
background: #fdfdfd;
box-shadow: inset 0 2px 8px rgba(23, 32, 74, 0.06);
border-radius: 3px;
margin: 1em auto;
padding: 1em;
box-sizing: border-box;
white-space: pre;
outline: none;
resize: vertical;
font-family: 'monospace', 'monaco', 'consolas', 'courier';
line-height: 1.7em;
font-size: 12px;
color: #152b48;
overflow: auto;
letter-spacing: 0.015em;
}
.raw-plugin-icon {
display: inline-block;
width: 16px;
height: 32px;
background: url(raw-icon-black.svg) no-repeat center center;
background-size: contain;
}
li:hover .raw-plugin-icon,
.selected .raw-plugin-icon{
background: url(raw-icon-white.svg) no-repeat center center;
background-size: contain;
}

51
plugins/raw/raw.js Normal file
View file

@ -0,0 +1,51 @@
/**
* Plugin for CodeX.Editor
* Implements RAW-data block
*/
var rawPlugin = function (plugin) {
var editor = codex.editor;
plugin.render = function (data) {
var input = editor.draw.node('TEXTAREA', 'raw-plugin__input', {});
input.placeholder = 'Вставьте HTML код';
if (data && data.raw) {
input.value = data.raw;
}
return input;
};
plugin.save = function (block) {
return {
raw: block.value
};
};
plugin.validate = function (data) {
if (data.raw.trim() === '') {
return;
}
return true;
};
plugin.destroy = function () {
rawPlugin = null;
};
return plugin;
}({});

File diff suppressed because one or more lines are too long