diff --git a/codex-editor.js b/codex-editor.js index f588016e..4093dcdb 100644 --- a/codex-editor.js +++ b/codex-editor.js @@ -13,7 +13,7 @@ var cEditor = (function (cEditor) { // First-level tags viewing as separated blocks. Other'll be inserted as child blockTags : ['P','BLOCKQUOTE','UL','CODE','OL','H1','H2','H3','H4','H5','H6'], - uploadImagesUrl : '/upload/save.php', + uploadImagesUrl : '/editor/transport/', }; // Static nodes @@ -133,6 +133,7 @@ cEditor.core = { }, /** + * @protected * Helper for insert one element after another */ insertAfter : function (target, element) { @@ -140,6 +141,7 @@ cEditor.core = { }, /** + * @const * Readable DOM-node types map */ nodeTypes : { @@ -149,11 +151,13 @@ cEditor.core = { }, /** + * @const * Readable keys map */ 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 */ isDomNode : function (el) { @@ -407,14 +411,24 @@ cEditor.saver = { }; - +/** +* Methods: +* - make +* - addTools +* - bindEvents +* - addBlockHandlers +* - saveInputs +*/ cEditor.ui = { - /** Blocks name. */ + /** + * @const {string} BLOCK_CLASSNAME - redactor blocks name + */ BLOCK_CLASSNAME : 'ce_block', /** + * @private * Making main interface */ make : function () { @@ -459,6 +473,7 @@ cEditor.ui = { }, /** + * @private * Append tools passed in cEditor.tools */ addTools : function () { @@ -492,6 +507,7 @@ cEditor.ui = { }, /** + * @private * Bind editor UI events */ bindEvents : function () { @@ -537,11 +553,11 @@ cEditor.ui = { }, false ); /** Bind click listeners on toolbar buttons */ - for (button in cEditor.nodes.toolbarButtons){ + for (var button in cEditor.nodes.toolbarButtons){ cEditor.nodes.toolbarButtons[button].addEventListener('click', function (event) { cEditor.callback.toolbarButtonClicked(event, this); }, false); - }; + } }, @@ -797,9 +813,6 @@ cEditor.callback = { caretInLastChild = selection.anchorNode == deepestTextnode; caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset; - console.log("каретка в последнем узле: %o", caretInLastChild); - console.log("каретка в конце последнего узла: %o", caretAtTheEndOfText); - if ( !caretInLastChild || !caretAtTheEndOfText ) { cEditor.core.log('arrow [down|right] : caret does not reached the end'); return false; @@ -876,9 +889,6 @@ cEditor.callback = { caretInFirstChild = selection.anchorNode == deepestTextnode; caretAtTheBeginning = selection.anchorOffset === 0; - console.log("каретка в первом узле: %o", caretInFirstChild); - console.log("каретка в начале первого узла: %o", caretAtTheBeginning); - if ( caretInFirstChild && caretAtTheBeginning ) { cEditor.caret.setToPreviousBlock(editableElementIndex); @@ -1050,6 +1060,7 @@ cEditor.content = { /** * Replaces one redactor block with another + * @protected * @param {Element} targetBlock - block to replace. Mostly currentNode. * @param {Element} newBlock * @param {string} newBlockType - type of new block; we need to store it to data-attribute @@ -1064,12 +1075,14 @@ cEditor.content = { return; } - /** Add redactor block classname to new block */ - // newBlock.classList.add(cEditor.ui.BLOCK_CLASSNAME); - /** Store block type */ newBlock.dataset.type = newBlockType; + /** If target-block is not a frist-level block, then we iterate parents to find it */ + while(!targetBlock.classList.contains(cEditor.ui.BLOCK_CLASSNAME)) { + targetBlock = targetBlock.parentNode; + } + /** Replacing */ cEditor.nodes.redactor.replaceChild(newBlock, targetBlock); @@ -1093,6 +1106,7 @@ cEditor.content = { /** * Inserts new block to redactor * Wrapps block into a DIV with BLOCK_CLASSNAME class + * @protected */ insertBlock : function(newBlockContent, blockType) { @@ -1131,6 +1145,7 @@ cEditor.content = { /** * Replaces blocks with saving content + * @protected * @param {Element} noteToReplace * @param {Element} newNode * @param {Element} blockType @@ -1140,7 +1155,9 @@ cEditor.content = { var oldBlockEditable = blockToReplace.querySelector('[contenteditable]'); /** Saving content */ - newBlock.innerHTML = oldBlockEditable.innerHTML; + if (oldBlockEditable) { + newBlock.innerHTML = oldBlockEditable.innerHTML; + } var newBlockComposed = cEditor.content.composeNewBlock(newBlock, blockType); @@ -1150,14 +1167,12 @@ cEditor.content = { /** Save new Inputs when block is changed */ cEditor.ui.saveInputs(); - /** Add event listeners */ - //cEditor.ui.addBlockHandlers(newBlockComposed); - }, /** * Iterates between child noted and looking for #text node on deepest level + * @private * @param {Element} block - node where find * @param {int} postiton - starting postion * Example: childNodex.length to find from the end @@ -1232,6 +1247,9 @@ cEditor.content = { return block; }, + /** + * @private + */ composeNewBlock : function (block, blockType) { newBlock = cEditor.draw.block('DIV'); @@ -1265,8 +1283,10 @@ cEditor.caret = { */ focusedNodeIndex: null, + /** * Creates Document Range and sets caret to the element. + * @protected * @uses caret.save — if you need to save caret position * @param {Element} el - Changed Node. */ @@ -1308,6 +1328,7 @@ cEditor.caret = { }, /** + * @protected * @return current index of input and saves it in caret object */ getCurrentInputIndex : function () { @@ -1394,6 +1415,9 @@ cEditor.toolbar = { current : null, + /** + * @protected + */ open : function (){ if (this.opened) { @@ -1405,6 +1429,9 @@ cEditor.toolbar = { }, + /** + * @protected + */ close : function(){ cEditor.nodes.toolbar.classList.remove('opened'); @@ -1541,7 +1568,7 @@ cEditor.toolbar = { if (!cEditor.tools[toolType] || !cEditor.core.isDomNode(cEditor.tools[toolType].settings) ) { cEditor.core.log('Wrong tool type', 'warn'); - cEditor.nodes.blockSettings.innerHTML = 'Настройки для этого плагина еще не созданы'; + cEditor.nodes.blockSettings.innerHTML = `Плагин «${toolType}» не имеет настроек`; } else { @@ -1594,6 +1621,11 @@ cEditor.transport = { input : null, + /** + * @property {Object} arguments - keep plugin settings and defined callbacks + */ + arguments : null, + prepare : function(){ var input = document.createElement('INPUT'); @@ -1610,40 +1642,30 @@ cEditor.transport = { */ fileSelected : function(event){ - var input = this, - files = input.files, + var input = this, + files = input.files, filesLength = files.length, formdData = new FormData(), file, i; - for (i = 0; i < filesLength; i++) { - - file = files[i]; - - /** - * Uncomment if need file type checking - * if (!file.type.match('image.*')) { - * continue; - * } - */ - - formdData.append('files[]', file, file.name); - } + formdData.append('files', files[0], files[0].name); cEditor.transport.ajax({ - data : formdData + data : formdData, + success : cEditor.transport.arguments.success, + error : cEditor.transport.arguments.error, }); - console.log("files: %o", files); - }, /** - * @todo use callback for success and error + * Use plugin callbacks + * @protected */ - selectAndUpload : function (callback) { + selectAndUpload : function (args) { + this.arguments = args; this.input.click(); }, @@ -1663,7 +1685,6 @@ cEditor.transport = { xhr.onload = function () { if (xhr.status === 200) { - console.log("success request: %o", xhr); success(xhr.responseText); } else { console.log("request error: %o", xhr); @@ -2018,6 +2039,7 @@ cEditor.notifications = { /** * Error notificator. Shows block with message + * @protected */ errorThrown : function(errorMsg, event) { diff --git a/plugins/code/code.css b/plugins/code/code.css index 836595a2..cde82227 100644 --- a/plugins/code/code.css +++ b/plugins/code/code.css @@ -3,5 +3,5 @@ font-family: 'monospace', 'monaco', 'consolas', 'courier'; line-height: 1.5em; background: #f8f8fd !important; - color: #4a8bd1; + color: #304f77; } \ No newline at end of file diff --git a/plugins/header/header.css b/plugins/header/header.css index ffc21f06..4a32f615 100644 --- a/plugins/header/header.css +++ b/plugins/header/header.css @@ -6,18 +6,14 @@ white-space: nowrap; /*padding-right: 10px; */ } -.ce_plugin_header--caption{ - color: #b9c2c2; -} .ce_plugin_header--select_button{ display: inline-block; margin-left: 40px; - border-bottom: 1px solid #c3d5ed; - padding-bottom: 2px; - color: #5399d4; + border-bottom: 1px solid #475588; + color: #6881e6; cursor: pointer; } .ce_plugin_header--select_button:hover{ - border-bottom-color: #f6d8da; - color: #cc7d74; + border-bottom-color: #687da5; + color: #8da8dc; } diff --git a/plugins/header/header.js b/plugins/header/header.js index e2a5ca85..e1065247 100644 --- a/plugins/header/header.js +++ b/plugins/header/header.js @@ -153,7 +153,7 @@ var headerTool = { new_header.innerHTML = old_header.innerHTML; new_header.contentEditable = true; - cEditor.content.replaceBlock(old_header, new_header, 'header'); + cEditor.content.switchBlock(old_header, new_header, 'header'); /** Add listeners for Arrow keys*/ cEditor.ui.addBlockHandlers(new_header); diff --git a/plugins/images/images.css b/plugins/images/images.css new file mode 100644 index 00000000..57195664 --- /dev/null +++ b/plugins/images/images.css @@ -0,0 +1,99 @@ +/** +* Image plugin for codex-editor +* @author CodeX Team +* +* @version 0.0.1 +*/ +.ce-plugin-image__holder{ + position: relative; + background: #FEFEFE; + border: 2px dashed #E8EBF5; + border-radius: 55px; + margin: 30px 0; + padding: 30px 110px 30px 40px; +} + .ce-plugin-image__holder input{ + border: 0; + background: transparent; + outline: none; + -webkit-appearance: none; + font-size: 1.2em; + color: #A5ABBC; + } + /* Placeholder color for Chrome */ + .ce-plugin-image__holder input::-webkit-input-placeholder { + color: #A5ABBC; + } + /* Placeholder color for IE 10+ */ + .ce-plugin-image__holder input:-ms-input-placeholder { + color: #A5ABBC; + } + /* Placeholder color for Firefox 19+ */ + .ce-plugin-image__holder input::-moz-placeholder { + color: #A5ABBC; + opacity: 1; + } +.ce-plugin-image__button{ + position: absolute; + z-index: 2; + right: 40px; + cursor: pointer; + font-family: "codex_editor"; + font-size: 1.5em; + color: #8990AA; +} + .ce-plugin-image__button:hover{ + color: #393F52; + } + +.ce-plugin-image__wrapper { + margin : 3em 0; +} +.ce-plugin-image__uploaded--centered { + max-width: 700px; + display:block; + margin: 0 auto; +} + +.ce-plugin-image__uploaded--stretched { + /*width: 939px;*/ +} + .ce-plugin-image--stretch { + margin: 0 !important; + max-width: 100% !important; + } + +.ce-plugin-image__caption { + margin-top: 1em; + text-align: center; + color: #898a8c; +} + + .ce-plugin-image--caption:empty::before { + content : 'Подпись'; + font-weight: normal; + color: #a1a5b3;; + opacity: 1; + transition: opacity 200ms ease; + } + + .ce-plugin-image--caption:focus::before { + opacity: .1; + } + +/** Settings */ +.ce_plugin_image--settings{ + white-space: nowrap; + /*padding-right: 10px; */ +} +.ce_plugin_image--select_button{ + display: inline-block; + margin-left: 40px; + border-bottom: 1px solid #475588; + color: #6881e6; + cursor: pointer; +} + .ce_plugin_image--select_button:hover{ + border-bottom-color: #687da5; + color: #8da8dc; + } diff --git a/plugins/images/images.js b/plugins/images/images.js new file mode 100644 index 00000000..8243a69c --- /dev/null +++ b/plugins/images/images.js @@ -0,0 +1,353 @@ +/** +* Image plugin for codex-editor +* @author CodeX Team +* +* @version 0.0.2 +*/ +var ceImage = { + + elementClasses : { + uploadedImage : { + centered : 'ce-plugin-image__uploaded--centered', + stretched : 'ce-plugin-image__uploaded--stretched', + }, + stretch : 'ce-plugin-image--stretch', + imageCaption : 'ce-plugin-image__caption', + imageWrapper : 'ce-plugin-image__wrapper', + formHolder : 'ce-plugin-image__holder', + uploadButton : 'ce-plugin-image__button', + + }, + + /** Default path to redactors images */ + path : '/upload/redactor_images/', + + make : function ( data ) { + + /** + * If we can't find image or we've got some problems with image path, we show plugin uploader + */ + if (!data || !data.file.url) { + holder = ceImage.ui.formView(); + } else { + + if ( !data.isStretch) { + holder = ceImage.ui.centeredImage(data); + } else { + holder = ceImage.ui.stretchedImage(data); + } + } + + return holder; + }, + + /** + * Settings panel content + * @return {Element} element contains all settings + */ + makeSettings : function () { + + var holder = document.createElement('DIV'), + caption = document.createElement('SPAN'), + types = { + centered : 'По центру', + stretched : 'На всю ширину', + }, + selectTypeButton; + + /** Add holder classname */ + holder.className = 'ce_plugin_image--settings'; + + /** Add settings helper caption */ + caption.textContent = 'Настройки плагина'; + caption.className = 'ce_plugin_image--caption'; + + holder.appendChild(caption); + + /** Now add type selectors */ + for (var type in types){ + + selectTypeButton = document.createElement('SPAN'); + + selectTypeButton.textContent = types[type]; + selectTypeButton.className = 'ce_plugin_image--select_button'; + + this.addSelectTypeClickListener(selectTypeButton, type); + + holder.appendChild(selectTypeButton); + + } + + return holder; + + }, + + addSelectTypeClickListener : function(el, type) { + + el.addEventListener('click', function() { + + ceImage.selectTypeClicked(type); + + }, false); + + }, + + selectTypeClicked : function(type) { + + var current = cEditor.content.currentNode; + + if (type == 'stretched') { + + current.classList.add(ceImage.elementClasses.stretch); + + } else if (type == 'centered') { + + current.classList.remove(ceImage.elementClasses.stretch); + + } + + }, + + render : function( data ) { + + return this.make(data); + + }, + + save : function ( block ) { + + var data = block[0], + image = data.querySelector('.' + ceImage.elementClasses.uploadedImage.centered) || + data.querySelector('.' + ceImage.elementClasses.uploadedImage.stretched), + caption = data.querySelector('.' + ceImage.elementClasses.imageCaption); + + var json = { + type : 'image', + data : { + background : false, + border : false, + isStrech : data.dataset.stretched, + file : { + url : image.src, + bigUrl : null, + width : image.width, + height : image.height, + additionalData :null, + }, + caption : caption.textContent, + cover : null, + } + }; + + return json; + }, + + uploadButtonClicked : function(event) { + + var success = ceImage.photoUploadingCallbacks.success, + error = ceImage.photoUploadingCallbacks.error; + + /** Define callbacks */ + cEditor.transport.selectAndUpload({ + success, + error, + }); + } +}; + +ceImage.ui = { + + holder : function(){ + + var element = document.createElement('DIV'); + + element.classList.add(ceImage.elementClasses.formHolder); + + return element; + }, + + input : function(){ + + var input = document.createElement('INPUT'); + + return input; + + }, + + uploadButton : function(){ + + var button = document.createElement('SPAN'); + + button.classList.add(ceImage.elementClasses.uploadButton); + + button.innerHTML = ''; + + return button; + + }, + + /** + * @param {string} source - file path + * @param {string} style - css class + * @return {object} image - document IMG tag + */ + image : function(source, style) { + + var image = document.createElement('IMG'); + + image.classList.add(style); + + image.src = source; + + return image; + }, + + wrapper : function() { + + var div = document.createElement('div'); + + div.classList.add(ceImage.elementClasses.imageWrapper); + + return div; + }, + + caption : function() { + + var div = document.createElement('div'); + + div.classList.add(ceImage.elementClasses.imageCaption); + + div.contentEditable = true; + + return div; + }, + + /** + * Draws form for image upload + */ + formView : function() { + + var holder = ceImage.ui.holder(), + uploadButton = ceImage.ui.uploadButton(), + input = ceImage.ui.input(); + + input.placeholder = 'Paste image URL or file'; + + holder.appendChild(uploadButton); + holder.appendChild(input); + + uploadButton.addEventListener('click', ceImage.uploadButtonClicked, false ); + + return holder; + }, + + /** + * wraps image and caption + * @param {object} data - image information + * @return wrapped block with image and caption + */ + centeredImage : function(data) { + + var file = data.file.url, + text = data.caption, + type = data.type, + image = ceImage.ui.image(file, ceImage.elementClasses.uploadedImage.centered), + caption = ceImage.ui.caption(), + wrapper = ceImage.ui.wrapper(); + + caption.textContent = text; + + wrapper.dataset.stretched = 'false', + /** Appeding to the wrapper */ + wrapper.appendChild(image); + wrapper.appendChild(caption); + + return wrapper; + }, + + /** + * wraps image and caption + * @param {object} data - image information + * @return stretched image + */ + stretchedImage : function(data) { + + var file = data.file.url, + text = data.caption, + type = data.type, + image = ceImage.ui.image(file, ceImage.elementClasses.uploadedImage.stretched), + caption = ceImage.ui.caption(), + wrapper = ceImage.ui.wrapper(); + + caption.textContent = text; + + wrapper.dataset.stretched = 'true', + /** Appeding to the wrapper */ + wrapper.appendChild(image); + wrapper.appendChild(caption); + + return wrapper; + + } + +}; + +ceImage.photoUploadingCallbacks = { + + /** Photo was uploaded successfully */ + success : function(result) { + + var parsed = JSON.parse(result), + data, + image; + + /** + * Preparing {Object} data to draw an image + * @uses ceImage.make method + */ + data = { + background : false, + border : false, + isStrech : false, + file : { + url : ceImage.path + 'o_' + parsed.filename, + bigUrl : null, + width : null, + height : null, + additionalData : null, + }, + caption : '', + cover : null, + }; + + image = ceImage.make(data); + + /** Replace form to image */ + var form = cEditor.content.currentNode.querySelector('.' + ceImage.elementClasses.formHolder); + + cEditor.content.switchBlock(form, image, 'image'); + + }, + + /** Error callback. Sends notification to user that something happend or plugin doesn't supports method */ + error : function(result) { + console.log('Choosen file is not image or image is corrupted'); + cEditor.notifications.errorThrown(); + }, + +} + + +/** +* Add plugin it to redactor tools +*/ +cEditor.tools.image = { + + type : 'image', + iconClassname : 'ce-icon-picture', + make : ceImage.make, + settings : ceImage.makeSettings(), + render : ceImage.render, + save : ceImage.save + +}; diff --git a/plugins/images/plugin.css b/plugins/images/plugin.css deleted file mode 100644 index 795aca83..00000000 --- a/plugins/images/plugin.css +++ /dev/null @@ -1,47 +0,0 @@ -/** -* Image plugin for codex-editor -* @author CodeX Team -* -* @version 0.0.1 -*/ -.ce-plugin-image__holder{ - position: relative; - background: #FEFEFE; - border: 2px dashed #E8EBF5; - border-radius: 55px; - margin: 30px 0; - padding: 30px 110px 30px 40px; -} - .ce-plugin-image__holder input{ - border: 0; - background: transparent; - outline: none; - -webkit-appearance: none; - font-size: 1.2em; - color: #A5ABBC; - } - /* Placeholder color for Chrome */ - .ce-plugin-image__holder input::-webkit-input-placeholder { - color: #A5ABBC; - } - /* Placeholder color for IE 10+ */ - .ce-plugin-image__holder input:-ms-input-placeholder { - color: #A5ABBC; - } - /* Placeholder color for Firefox 19+ */ - .ce-plugin-image__holder input::-moz-placeholder { - color: #A5ABBC; - opacity: 1; - } -.ce-plugin-image__button{ - position: absolute; - z-index: 2; - right: 40px; - cursor: pointer; - font-family: "codex_editor"; - font-size: 1.5em; - color: #8990AA; -} - .ce-plugin-image__button:hover{ - color: #393F52; - } \ No newline at end of file diff --git a/plugins/images/plugin.js b/plugins/images/plugin.js deleted file mode 100644 index b18caf04..00000000 --- a/plugins/images/plugin.js +++ /dev/null @@ -1,91 +0,0 @@ -/** -* Image plugin for codex-editor -* @author CodeX Team -* -* @version 0.0.1 -*/ -var ceImage = { - - make : function ( data ) { - - var holder = ceImage.ui.holder(), - uploadButton = ceImage.ui.uploadButton(), - input = ceImage.ui.input(); - - input.placeholder = 'Past image URL or file'; - - holder.appendChild(uploadButton); - holder.appendChild(input); - - uploadButton.addEventListener('click', ceImage.uploadButtonClicked, false ); - - return holder; - - }, - - render : function( data ){ - - return this.make(data); - - }, - - save : function ( block ){ - - }, - - uploadButtonClicked : function(event){ - - cEditor.transport.selectAndUpload(); - - }, - - ui : { - - holder : function(){ - - var element = document.createElement('DIV'); - - element.classList.add('ce-plugin-image__holder'); - - return element; - - }, - - input : function(){ - - var input = document.createElement('INPUT'); - - return input; - - }, - - uploadButton : function(){ - - var button = document.createElement('SPAN'); - - button.classList.add('ce-plugin-image__button'); - - button.innerHTML = ''; - - return button; - - } - - } - - - -}; - -/** -* Add plugin it to redactor tools -*/ -cEditor.tools.image = { - - type : 'image', - iconClassname : 'ce-icon-picture', - make : ceImage.make, - render : ceImage.render, - save : ceImage.save - -}; diff --git a/plugins/link/link.css b/plugins/link/link.css index 6ef9e80f..a846a5a2 100644 --- a/plugins/link/link.css +++ b/plugins/link/link.css @@ -13,26 +13,26 @@ width: 100%; background: transparent; font-size: 1em; - padding: 12px; + padding: 8px 25px; transition: background 200ms; + border-left: 3px solid #65d8b3; } .tool-link-panel { - width: 50%; position: relative; - margin: 25px auto; + margin: 5px 0; background: #f8f7ef; border: 1px solid transparent; padding: 25px 30px; } -.ceditor-tool-link .tool-link-image { +.tool-link-image { float:right; width: 75px; - border-radius: 50%; + border-radius: 3px; } -.ceditor-tool-link .tool-link-title { +.tool-link-title { display: block; width: 340px; margin-bottom: 4px; @@ -42,16 +42,14 @@ color: #000; } -.ceditor-tool-link .tool-link-description { +.tool-link-description { display: block; - width: 400px; margin-top: 10px; font-size: 14px; color: #000; } -.ceditor-tool-link .tool-link-link { - display: block; +.tool-link-link { width: 360px; font-size: 10px; margin-bottom: 4px; @@ -63,21 +61,15 @@ } .tool-link-loader { - background: url("loading.gif") !important; opacity: 0.1; - } .tool-link-error { + background: rgb(255, 241, 241); + color: #bf4747; +} +.tool-link-error .ceditor-tool-link-input { + border-left-color: #d86b6b +} - /*background: repeating-linear-gradient(*/ - /*45deg,*/ - /*#bc320e,*/ - /*#606dbc 10px,*/ - /*#465298 10px,*/ - /*#465298 20px*/ - /*);*/ - background: rgba(255, 0, 0, 0.9); - -} \ No newline at end of file diff --git a/plugins/link/link.js b/plugins/link/link.js index ff7a2506..4122fd6e 100644 --- a/plugins/link/link.js +++ b/plugins/link/link.js @@ -28,18 +28,17 @@ var linkTool = { */ makeNewBlock : function (data) { - var wrapper = linkTool.ui.mainBlock(); - - var tag = linkTool.ui.input(); + var wrapper = linkTool.ui.mainBlock(), + tag = linkTool.ui.input(); linkTool.currentInput = tag; wrapper.appendChild(tag); - /* Bind callbacks **/ - + /** + * Bind callbacks + **/ tag.addEventListener('paste', linkTool.blockPasteCallback, false); - tag.addEventListener('keydown', linkTool.blockKeyDownCallback, false); return wrapper; @@ -51,9 +50,8 @@ var linkTool = { */ render : function (json) { - var block = linkTool.ui.mainBlock(); - - var tag = linkTool.ui.make(json); + var block = linkTool.ui.mainBlock(), + tag = linkTool.ui.make(json); block.appendChild(tag); @@ -84,17 +82,11 @@ var linkTool = { }, - appendCallback : function () { - - console.log('link callback is appended...'); - - }, - blockPasteCallback : function (event) { var clipboardData = event.clipboardData || window.clipboardData, - pastedData = clipboardData.getData('Text'), - block = event.target.parentNode; + pastedData = clipboardData.getData('Text'), + block = event.target.parentNode; linkTool.renderLink(pastedData, block); @@ -105,16 +97,17 @@ var linkTool = { blockKeyDownCallback : function (event) { var inputTag = event.target, - block = inputTag.parentNode; + block = inputTag.parentNode, + url; - if (block.classList.contains(linkTool.elementClasses.error)) + if ( block.classList.contains(linkTool.elementClasses.error) ) { - block.classList.remove(linkTool.elementClasses.error) + block.classList.remove(linkTool.elementClasses.error); } if (event.keyCode == linkTool.ENTER_KEY) { - var url = inputTag.value; + url = inputTag.value; linkTool.renderLink(url, block); @@ -124,12 +117,15 @@ var linkTool = { }, + /** + * @todo move request-url to accepted settings + */ renderLink : function (url, block) { Promise.resolve() .then(function () { - return linkTool.urlify(url) + return linkTool.urlify(url); }) .then(function (url) { @@ -137,7 +133,7 @@ var linkTool = { /* Show loader gif **/ block.classList.add(linkTool.elementClasses.loader); - return fetch('/server/?url=' + encodeURI(url)); + return fetch('/editor/parseLink?url=' + encodeURI(url)); }) .then(function (response) { @@ -146,8 +142,7 @@ var linkTool = { return response.json(); - } - else { + } else { return Error("Invalid response status: %o", response); @@ -156,7 +151,7 @@ var linkTool = { }) .then(function (json) { - linkTool.composeLinkPreview(json, block) + linkTool.composeLinkPreview(json, block); }) .catch(function(error) { @@ -220,6 +215,9 @@ linkTool.ui = { wrapper.appendChild(siteLink); wrapper.appendChild(siteDescription); + siteTitle.contentEditable = true; + siteDescription.contentEditable = true; + return wrapper; }, @@ -230,7 +228,7 @@ linkTool.ui = { wrapper.classList += "ceditor-tool-link"; - return wrapper + return wrapper; }, diff --git a/plugins/list/list.css b/plugins/list/list.css index 2743fae5..43e25704 100644 --- a/plugins/list/list.css +++ b/plugins/list/list.css @@ -2,20 +2,14 @@ white-space: nowrap; } -.ce_plugin_list--caption{ - color: #b9c2c2; -} - .ce_plugin_list--select_button{ display: inline-block; margin-left: 40px; - border-bottom: 1px solid #c3d5ed; - padding-bottom: 2px; - color: #5399d4; + border-bottom: 1px solid #475588; + color: #6881e6; cursor: pointer; } - -.ce_plugin_list--select_button:hover{ - border-bottom-color: #f6d8da; - color: #cc7d74; -} + .ce_plugin_list--select_button:hover{ + border-bottom-color: #687da5; + color: #8da8dc; + } diff --git a/plugins/quote/quote.css b/plugins/quote/quote.css index f1a541fa..31bb9cad 100644 --- a/plugins/quote/quote.css +++ b/plugins/quote/quote.css @@ -3,20 +3,16 @@ white-space: nowrap; /*padding-right: 10px; */ } -.ce_plugin_quote--caption{ - color: #b9c2c2; -} .ce_plugin_quote--select_button{ display: inline-block; margin-left: 40px; - border-bottom: 1px solid #c3d5ed; - padding-bottom: 2px; - color: #5399d4; + border-bottom: 1px solid #475588; + color: #6881e6; cursor: pointer; } .ce_plugin_quote--select_button:hover{ - border-bottom-color: #f6d8da; - color: #cc7d74; + border-bottom-color: #687da5; + color: #8da8dc; } /** Quote Styles */ @@ -26,10 +22,9 @@ } .quoteStyle-withCaption--author { - margin: 2.5em 100px !important; - margin-top: 25px; + margin: 0 100px 3em !important; text-align: right; - font-size: 1.38em; + font-size: 1.16em; font-weight: bold; color: #000; line-height: 1.5em; @@ -38,16 +33,19 @@ .quoteStyle-simple--text, .quoteStyle-withCaption--blockquote, .quoteStyle-withPhoto--quote { - font-size: 1.3em; + font-size: 1.04em; line-height: 1.9em; } -.quoteStyle-simple--text, +.quoteStyle-simple--text { + padding: 1.2em 2.1em; + margin: 2.5em 2em; + background: #FBFBFB; +} .quoteStyle-withCaption--blockquote { - margin: 2.5em 100px; + margin: 0 1em; padding: 1.5em 2.0em !important; - background: #FBFBFB; } .quoteStyle-withCaption--blockquote:focus, @@ -68,7 +66,8 @@ .quoteStyle-withCaption--author:empty::before { content : 'Введите имя автора'; - color: #A5ABBC; + font-weight: normal; + color: #a1a5b3;; opacity: 1; transition: opacity 200ms ease; } @@ -95,45 +94,54 @@ /** Quote with photo */ .ce_redactor .quoteStyle-withPhoto--wrapper { margin: 0; - padding: 4.5em 8.5em !important; + padding: 4.5em 3.6em !important; } .quoteStyle-withPhoto--photo { - width: 80px; - height: 80px; + width: 70px; + height: 70px; + overflow: hidden; text-align: center; - line-height: 80px; + line-height: 70px; border-radius: 50%; float: left; - margin-right: 35px; - border: 3px dashed #D7E2EE; -} -.quoteStyle-withPhoto--photo:hover { - cursor: pointer; - background: #F0F3F6; - border: 3px dashed #D7E2EE; + margin-right: 25px; + border: 2px dashed #D7E2EE; } + .quoteStyle-withPhoto--photo:hover { + cursor: pointer; + background: #F0F3F6; + } -.quoteStyle-withPhoto--photo:hover::after { - display: block; -} + .quoteStyle-withPhoto--photo:hover::after { + display: block; + } + + .authorsPhoto { + height: 110%; + vertical-align: top; + } + + .authorsPhoto-wrapper { + border: 0 !important; + } .quoteStyle-withPhoto--photo .ce-icon-picture { font-family: "codex_editor"; font-size: 1.5em; - color: #8990AA; + color: #d5dbea; } .quoteStyle-withPhoto--author { - font-size: 1.56em; + font-size: 1.36em; font-weight: bold; color: #000; outline: none; overflow: hidden; - margin-bottom: 6px; + margin-bottom: 8px; } .quoteStyle-withPhoto--job { - font-size: 1.38em; + font-size: 1.04em; color: #818BA1; outline: none; overflow: hidden; @@ -151,9 +159,8 @@ } .quoteStyle-withPhoto--quote { - background: #FBFBFB; - margin-top: 45px; - line-height: 32px; + margin-top: 33px; + line-height: 1.82em; outline: none; overflow: hidden; } diff --git a/plugins/quote/quote.js b/plugins/quote/quote.js index f92b4ca2..5dc84405 100644 --- a/plugins/quote/quote.js +++ b/plugins/quote/quote.js @@ -5,6 +5,9 @@ var quoteTools = { + /** Default path to redactors images */ + path : '/upload/redactor_images/', + /** * Make Quote from JSON datasets */ @@ -33,8 +36,8 @@ var quoteTools = { tag.dataset.quoteStyle = 'simple'; - tag.classList.add('ce_quote--text'); - tag.classList.add('quoteStyle-simple--text'); + tag.classList.add(quoteTools.styles.quoteText); + tag.classList.add(quoteTools.styles.simple.text); } @@ -51,9 +54,8 @@ var quoteTools = { * Extracts JSON quote data from HTML block * @param {Text} text, {Text} author, {Object} photo */ - parsedblock = quoteTools.parseBlockQuote(blockContent); - var block = blockContent[0], + parsedblock = quoteTools.parseBlockQuote(block); json = { type : 'quote', data : { @@ -80,11 +82,11 @@ var quoteTools = { selectTypeButton; /** Add holder classname */ - holder.className = 'ce_plugin_quote--settings' + holder.className = quoteTools.styles.settings.holder, /** Add settings helper caption */ caption.textContent = 'Настройки цитат'; - caption.className = 'ce_plugin_quote--caption'; + caption.className = quoteTools.styles.settings.caption; holder.appendChild(caption); @@ -95,7 +97,7 @@ var quoteTools = { selectTypeButton.textContent = types[type]; - selectTypeButton.className = 'ce_plugin_quote--select_button'; + selectTypeButton.className = quoteTools.styles.settings.buttons; var quoteStyle = quoteTools.selectTypeQuoteStyle(type); quoteTools.addSelectTypeClickListener(selectTypeButton, quoteStyle); @@ -153,7 +155,7 @@ var quoteTools = { makeSimpleQuote : function(data) { - var wrapper = quoteTools.ui.makeBlock('BLOCKQUOTE', ['quoteStyle-simple--text', 'ce_quote--text']); + var wrapper = quoteTools.ui.makeBlock('BLOCKQUOTE', [quoteTools.styles.simple.text, quoteTools.styles.quoteText]); wrapper.innerHTML = data.text || ''; @@ -167,8 +169,8 @@ var quoteTools = { makeQuoteWithCaption : function(data) { var wrapper = quoteTools.ui.blockquote(), - text = quoteTools.ui.makeBlock('DIV', ['quoteStyle-withCaption--blockquote', 'ce_quote--text']), - author = quoteTools.ui.makeBlock('DIV', ['quoteStyle-withCaption--author', 'ce_quote--author']); + text = quoteTools.ui.makeBlock('DIV', [quoteTools.styles.withCaption.blockquote, quoteTools.styles.quoteText]), + author = quoteTools.ui.makeBlock('DIV', [quoteTools.styles.withCaption.author, quoteTools.styles.quoteAuthor]); /* make text block ontentEditable */ text.contentEditable = 'true'; @@ -193,15 +195,17 @@ var quoteTools = { makeQuoteWithPhoto : function(data) { var wrapper = quoteTools.ui.blockquote(); - photo = quoteTools.ui.makeBlock('DIV', ['quoteStyle-withPhoto--photo']), - author = quoteTools.ui.makeBlock('DIV', ['quoteStyle-withPhoto--author', 'ce_quote--author']), - job = quoteTools.ui.makeBlock('DIV', ['quoteStyle-withPhoto--job', 'ce_quote--job']), - quote = quoteTools.ui.makeBlock('DIV', ['quoteStyle-withPhoto--quote', 'ce_quote--text']) + photo = quoteTools.ui.makeBlock('DIV', [quoteTools.styles.withPhoto.photo]), + author = quoteTools.ui.makeBlock('DIV', [quoteTools.styles.withPhoto.author, quoteTools.styles.quoteAuthor]), + job = quoteTools.ui.makeBlock('DIV', [quoteTools.styles.withPhoto.job, quoteTools.styles.authorsJob]), + quote = quoteTools.ui.makeBlock('DIV', [quoteTools.styles.withPhoto.quote, quoteTools.styles.quoteText]) /* Default Image src */ var icon = quoteTools.ui.makeBlock('SPAN', ['ce-icon-picture']); photo.appendChild(icon); + photo.addEventListener('click', quoteTools.fileUploadClicked, false); + /* make author block contentEditable */ author.contentEditable = 'true'; author.textContent = data.author; @@ -210,7 +214,7 @@ var quoteTools = { job.contentEditable = 'true'; job.textContent = data.job; - var authorsWrapper = quoteTools.ui.makeBlock('DIV', ['quoteStyle-withPhoto--authorWrapper']); + var authorsWrapper = quoteTools.ui.makeBlock('DIV', [quoteTools.styles.withPhoto.authorHolder]); authorsWrapper.appendChild(author); authorsWrapper.appendChild(job); @@ -218,7 +222,7 @@ var quoteTools = { quote.contentEditable = 'true'; quote.innerHTML = data.text; - wrapper.classList.add('quoteStyle-withPhoto--wrapper'); + wrapper.classList.add(quoteTools.styles.withPhoto.wrapper); wrapper.dataset.quoteStyle = 'withPhoto'; wrapper.appendChild(photo); @@ -232,15 +236,15 @@ var quoteTools = { var currentNode = block || cEditor.content.currentNode, photo = currentNode.getElementsByTagName('img')[0], - author = currentNode.querySelector('.ce_quote--author'), - job = currentNode.querySelector('.ce_quote--job'), + author = currentNode.querySelector('.' + quoteTools.styles.quoteAuthor), + job = currentNode.querySelector('.' + quoteTools.styles.authorsJob), quote ; /** Simple quote text placed in Blockquote tag*/ if ( currentNode.dataset.quoteStyle == 'simple' ) quote = currentNode.textContent; else - quote = currentNode.querySelector('.ce_quote--text').textContent; + quote = currentNode.querySelector('.' + quoteTools.styles.quoteText).textContent; if (job) job = job.textContent; @@ -262,8 +266,53 @@ var quoteTools = { return data; }, + fileUploadClicked : function() { + + var success = quoteTools.photoUploadingCallbacks.success, + error = quoteTools.photoUploadingCallbacks.error; + + cEditor.transport.selectAndUpload({ + success, + error, + }); + + } + }; +quoteTools.styles = { + + quoteText : 'ce_quote--text', + quoteAuthor : 'ce_quote--author', + authorsJob : 'ce_quote--job', + authorsPhoto : 'authorsPhoto', + authorsPhotoWrapper : 'authorsPhoto-wrapper', + + simple : { + text : 'quoteStyle-simple--text', + }, + + withCaption : { + blockquote : 'quoteStyle-withCaption--blockquote', + author : 'quoteStyle-withCaption--author', + }, + + withPhoto : { + photo : 'quoteStyle-withPhoto--photo', + author : 'quoteStyle-withPhoto--author', + job : 'quoteStyle-withPhoto--job', + quote : 'quoteStyle-withPhoto--quote', + wrapper : 'quoteStyle-withPhoto--wrapper', + authorHolder : 'quoteStyle-withPhoto--authorWrapper', + }, + + settings : { + holder : 'ce_plugin_quote--settings', + caption : 'ce_plugin_quote--caption', + buttons : 'ce_plugin_quote--select_button', + }, +} + quoteTools.ui = { wrapper : function($classList) { @@ -284,6 +333,13 @@ quoteTools.ui = { }, + img : function(attribute) { + var imageTag = document.createElement('IMG'); + imageTag.classList.add(attribute); + + return imageTag; + }, + makeBlock : function(tag, classList) { var el = document.createElement(tag); @@ -299,7 +355,40 @@ quoteTools.ui = { } -} +}; + +quoteTools.photoUploadingCallbacks = { + + /** + * Success callbacks for uploaded photo. + * Replace upload icon with uploaded photo + */ + success : function(result) { + + var parsed = JSON.parse(result), + filename = parsed.filename, + uploadImageWrapper = cEditor.content.currentNode.querySelector('.' + quoteTools.styles.withPhoto.photo), + authorsPhoto = quoteTools.ui.img(quoteTools.styles.authorsPhoto); + + authorsPhoto.src = quoteTools.path + 'b_' + filename; + + /** Remove icon from image wrapper */ + uploadImageWrapper.innerHTML = ''; + + /** Appending uploaded image */ + uploadImageWrapper.classList.add(quoteTools.styles.authorsPhotoWrapper); + uploadImageWrapper.appendChild(authorsPhoto); + }, + + /** Error callback. Sends notification to user that something happend or plugin doesn't supports method */ + error : function(result) { + + console.log('Can\'t upload an image'); + cEditor.notifications.errorThrown(); + + } + +}; cEditor.tools.quote = {