From cda692edec9c1719403321833572b6c628cf01fd Mon Sep 17 00:00:00 2001 From: Mark Dermanov Date: Wed, 9 Dec 2015 03:34:32 +0300 Subject: [PATCH 1/3] add new paragrath by pressing enter key add new line by pressing shift + enter key add editor settings make add buttons by one instance and move it through DOM some css changes --- ce_interface.js | 138 ++++++++++++++++++++++++++++++++++-------------- editor.css | 110 +++++++++++++++++++++----------------- example.html | 12 +++-- 3 files changed, 168 insertions(+), 92 deletions(-) diff --git a/ce_interface.js b/ce_interface.js index 19a71e182..9a979eff0 100644 --- a/ce_interface.js +++ b/ce_interface.js @@ -3,21 +3,40 @@ * @author Savchenko Peter (vk.com/specc) */ -var ce = function(textareaId) { +var ce = function(settings) { - this.resultTextarea = document.getElementById(textareaId); + this.textareaId = "codex_editor"; + this.resultTextarea = document.getElementById(this.textareaId); if (typeof this.resultTextarea == undefined || this.resultTextarea == null ){ - console.warn('Textarea not found with ID %o', textareaId); + console.warn('Textarea not found with ID %o', this.textareaId); return this; } - this.toolbarOpened = false; - this.tools = ['header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile']; + // prepare settings + this.allTools = ['header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile']; + + var defaultSettings = { + + }; + + + if ("undefined" == typeof settings || "object" != typeof settings) + settings = defaultSettings; + else { + // todo just merge settings with defaults + } + + if ("undefined" == typeof settings.tools || !Array.isArray(settings.tools)) + settings.tools = this.allTools; + + this.settings = settings; /** Some configurations */ + this.toolbarOpened = false; + this.BUTTONS_TOGGLED_CLASSNANE = 'buttons_toggled'; this.key = { TAB: 9, ENTER: 13, BACKSPACE: 8, DELETE: 46, DOWN: 40, SPACE: 32, ESC: 27, CTRL: 17, META: 91, SHIFT: 16, ALT: 18 }; @@ -27,34 +46,43 @@ var ce = function(textareaId) { /** Bind all events */ this.bindEvents(); -} +}; /** * Editor interface drawing -* @use this.tools to get necessary items -* @todo get tools from user inital-settings */ ce.prototype.makeInterface = function () { var wrapper = this.make.editorWrapper(), - firstNode = this.make.node(null, 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Porro quia nihil repellendus aut cupiditate reprehenderit sapiente magnam nobis doloremque eaque! Sint nobis assumenda nisi ducimus minima illo tenetur, cumque facilis.' ), + firstNode = this.make.textNode('Lorem ipsum dolor sit amet, consectetur adipisicing elit. Porro quia nihil repellendus aut cupiditate reprehenderit sapiente magnam nobis doloremque eaque! Sint nobis assumenda nisi ducimus minima illo tenetur, cumque facilis.' ), toolbar = this.make.toolbar(), - button; + button, + tool; - for (var i = 0; i < this.tools.length; i++) { - button = this.make.toolbarButton(this.tools[i]); - toolbar.appendChild(button); - }; + this.wrapper = wrapper; + this.toolbar = toolbar; + + this.toolbarButtons = document.createElement("span"); + this.toolbarButtons.classList.add("buttons"); + + // îáõîäèì áàçîâûé ñïèñîê, ÷òîáû ñîõðàíèòü îðèãèíàëüíóþ ïîñëåäîâàòåëüíîñòü êíîïîê + for (var i = 0; i < this.allTools.length; i++) { + tool = this.allTools[i]; + + if (this.settings.tools.indexOf(tool) < 0) + continue; + + button = this.make.toolbarButton(tool); + this.toolbarButtons.appendChild(button); + } - /** - * Add toolbar to node - * @todo make toolbar rendering once - */ - firstNode.appendChild(toolbar); /** Add first node */ wrapper.appendChild(firstNode); + /** Add toolbar to node */ + wrapper.appendChild(toolbar); + /** Insert Editor after initial textarea. Hide textarea */ this.resultTextarea.parentNode.insertBefore(wrapper, this.resultTextarea.nextSibling); this.resultTextarea.hidden = true; @@ -63,7 +91,7 @@ ce.prototype.makeInterface = function () { var contentEditable = firstNode.getElementsByClassName('ce_node_content'); contentEditable.length && contentEditable[0].focus(); -} +}; /** * All events binds in one place @@ -77,7 +105,7 @@ ce.prototype.bindEvents = function () { _this.globalKeydownCallback(event); }, false ); -} +}; /** * All window keydowns handles here @@ -89,40 +117,70 @@ ce.prototype.globalKeydownCallback = function (event) { case this.key.ENTER : this.enterKeyPressed(event); break; // Enter } -} +}; /** -* @todo: check if currently focused in contenteditable element +* */ ce.prototype.tabKeyPressed = function(event) { - var toolbar = document.getElementsByClassName('add_buttons'); + // check if currently focused in contenteditable element + if ("BODY" == event.target.tagName) + return; - if ( !toolbar[0].className.includes(this.BUTTONS_TOGGLED_CLASSNANE) ){ - toolbar[0].className += ' ' + this.BUTTONS_TOGGLED_CLASSNANE; - this.toolbarOpened = true; - } else { - toolbar[0].className = toolbar[0].className.replace(this.BUTTONS_TOGGLED_CLASSNANE, ''); - this.toolbarOpened = false - } + var toolbar = event.target.parentNode.nextSibling, + _this = this; + + toolbar.appendChild(this.toolbarButtons); + + // repair buttons animation + setTimeout(function () { + + if ( !toolbar.className.includes(_this.BUTTONS_TOGGLED_CLASSNANE) ){ + toolbar.className += ' ' + _this.BUTTONS_TOGGLED_CLASSNANE; + _this.toolbarOpened = true; + } else { + toolbar.className = toolbar.className.replace(_this.BUTTONS_TOGGLED_CLASSNANE, ''); + _this.toolbarOpened = false + } + + }, 10); event.preventDefault(); -} +}; /** * Handle Enter key. Adds new Node; */ ce.prototype.enterKeyPressed = function(event) { - console.log('ENTER'); + if (event.shiftKey){ + document.execCommand('insertHTML', false, '

'); + } else { + var + newNode = this.make.textNode(), + toolbar = this.make.toolbar(); -} + + /** Add node */ + this.wrapper.insertBefore(newNode, event.target.parentNode.nextSibling); + + /** Add toolbar to node */ + this.wrapper.insertBefore(toolbar, newNode); + + /** Set auto focus */ + var contentEditable = newNode.getElementsByClassName('ce_node_content'); + contentEditable.length && contentEditable[0].focus(); + } + + event.preventDefault(); +}; /** * Creates HTML elements */ -ce.prototype.make = function (window) { +ce.prototype.make = function () { /** Empty toolbar with toggler */ function toolbar () { @@ -133,7 +191,7 @@ ce.prototype.make = function (window) { /** Toggler button*/ bar.innerHTML = '' + - ''+ + ''+ ''; return bar; @@ -153,7 +211,7 @@ ce.prototype.make = function (window) { * Paragraph node * @todo set unique id with prefix */ - function node (id, content){ + function textNode (content){ var node = document.createElement('div'); @@ -175,13 +233,13 @@ ce.prototype.make = function (window) { var ceMake = function () { this.toolbar = toolbar; this.toolbarButton = toolbarButton; - this.node = node; + this.textNode = textNode; this.editorWrapper = editorWrapper; - } + }; return new ceMake(); -}(this) +}(); diff --git a/editor.css b/editor.css index 7357e71d0..1600b1061 100644 --- a/editor.css +++ b/editor.css @@ -1,34 +1,34 @@ @font-face { - font-family: 'codex_editor'; - src: url('fonts/codex_editor/codex_editor.eot?52438661'); - src: url('fonts/codex_editor/codex_editor.eot?52438661#iefix') format('embedded-opentype'), - url('fonts/codex_editor/codex_editor.woff?52438661') format('woff'), - url('fonts/codex_editor/codex_editor.ttf?52438661') format('truetype'), - url('fonts/codex_editor/codex_editor.svg?52438661#codex_editor') format('svg'); - font-weight: normal; - font-style: normal; + font-family: 'codex_editor'; + src: url('fonts/codex_editor/codex_editor.eot?52438661'); + src: url('fonts/codex_editor/codex_editor.eot?52438661#iefix') format('embedded-opentype'), + url('fonts/codex_editor/codex_editor.woff?52438661') format('woff'), + url('fonts/codex_editor/codex_editor.ttf?52438661') format('truetype'), + url('fonts/codex_editor/codex_editor.svg?52438661#codex_editor') format('svg'); + font-weight: normal; + font-style: normal; } [class^="ce_icon-"]:before, [class*="ce_icon-"]:before { - font-family: "codex_editor"; - font-style: normal; - font-weight: normal; - speak: none; + font-family: "codex_editor"; + font-style: normal; + font-weight: normal; + speak: none; - display: inline-block; - text-decoration: inherit; - width: 1em; - margin-right: .2em; - text-align: center; - font-variant: normal; - text-transform: none; + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + font-variant: normal; + text-transform: none; - line-height: 1em; + line-height: 1em; - /* Animation center compensation - margins should be symmetric */ - margin-left: .2em; + /* Animation center compensation - margins should be symmetric */ + margin-left: .2em; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } .ce_icon-instagram:before { content: '\e800'; } /* 'î €' */ @@ -87,41 +87,55 @@ } .codex_editor .add_buttons{ - position: relative; + /* position: relative; */ + /* left: -42px; */ + /* top: -30px; */ color: #3b4352; font-size: 16px; + margin-left: -42px; + margin-top: -20px; + margin-bottom: -25px; +} + +.add_buttons .buttons { + position: absolute; +} +.codex_editor .add_buttons button:hover, +.codex_editor .add_buttons .focused{ + color: #3770ef; +} +.codex_editor .add_buttons button{ + transition: all 150ms ease-in; + transform: translate3d(-50px, 0 , 0); + opacity: 0; + font-size: 14px; + /*display: none;*/ +} +.codex_editor .buttons_toggled{ + background: #fff; +} +.codex_editor .buttons_toggled button{ + opacity: 1; + transform: translate3d(0,0,0); } - .codex_editor .add_buttons button:hover, - .codex_editor .add_buttons .focused{ - color: #3770ef; - } - .codex_editor .add_buttons button{ - transition: all 150ms ease-in; - transform: translate3d(-50px, 0 , 0); - opacity: 0; - font-size: 14px; - /*display: none;*/ - } - .codex_editor .buttons_toggled{ - background: #fff; - } - .codex_editor .buttons_toggled button{ - opacity: 1; - transform: translate3d(0,0,0); - } .codex_editor .toggler{ - position: absolute; - left: -42px; + /* position: relative; */ + /* left: -42px; */ display: inline-block; font-size: 23px; color: #387ff5; transition: transform 100ms ease-in; } - .codex_editor .buttons_toggled .toggler{ - transform: rotate(45deg); - } + +.codex_editor .toggler .buttons { + position: relative; + top: -2px; +} +.codex_editor .buttons_toggled .plus_btn{ + transform: rotate(45deg); +} diff --git a/example.html b/example.html index 6b81a21f0..3e57bf74c 100644 --- a/example.html +++ b/example.html @@ -12,7 +12,7 @@

CodeX Editor

- +
@@ -48,14 +48,18 @@

CodeX Editor

- + From 09df6ad75bc13962617ea53031d9790d354e62cc Mon Sep 17 00:00:00 2001 From: Mark Dermanov Date: Fri, 11 Dec 2015 00:26:32 +0300 Subject: [PATCH 2/3] add gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..e2cccc2a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# --- proj files --- +.DS_Store +Thumbs.db +/.idea/ +/*.sublime-project +/*.sublime-workspace \ No newline at end of file From e0e9eb26499f9aa68bfb35c6249c77ceb6a2f522 Mon Sep 17 00:00:00 2001 From: Mark Dermanov Date: Fri, 11 Dec 2015 00:26:44 +0300 Subject: [PATCH 3/3] review refactoring --- ce_interface.js | 130 ++++++++++++++++++++++++-------------------- editor.css | 141 +++--------------------------------------------- 2 files changed, 78 insertions(+), 193 deletions(-) diff --git a/ce_interface.js b/ce_interface.js index 9a979eff0..b089129da 100644 --- a/ce_interface.js +++ b/ce_interface.js @@ -5,40 +5,22 @@ var ce = function(settings) { - this.textareaId = "codex_editor"; - this.resultTextarea = document.getElementById(this.textareaId); + this.resultTextarea = document.getElementById("codex_editor"); if (typeof this.resultTextarea == undefined || this.resultTextarea == null ){ - console.warn('Textarea not found with ID %o', this.textareaId); return this; - } - // prepare settings - this.allTools = ['header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile']; - - var defaultSettings = { - - }; - - - if ("undefined" == typeof settings || "object" != typeof settings) - settings = defaultSettings; + /* Prepare settings */ + if ("undefined" == typeof settings) settings = this.defaultSettings; else { // todo just merge settings with defaults } - if ("undefined" == typeof settings.tools || !Array.isArray(settings.tools)) - settings.tools = this.allTools; - - this.settings = settings; - - /** Some configurations */ - this.toolbarOpened = false; + settings.tools = settings.tools || this.allTools; + this.settings = settings; - this.BUTTONS_TOGGLED_CLASSNANE = 'buttons_toggled'; - this.key = { TAB: 9, ENTER: 13, BACKSPACE: 8, DELETE: 46, DOWN: 40, SPACE: 32, ESC: 27, CTRL: 17, META: 91, SHIFT: 16, ALT: 18 }; /** Making a wrapper and interface */ this.makeInterface(); @@ -48,49 +30,47 @@ var ce = function(settings) { }; +// All posible tools +ce.prototype.allTools = ['header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile']; + +// Default settings configuration +ce.prototype.defaultSettings = { + +}; + +// Add this class when open tool bar for css animation +ce.prototype.BUTTONS_TOGGLED_CLASSNANE = 'buttons_toggled'; + +// Default tool bar is closed +ce.prototype.toolbarOpened = false; + +// Key event constants +ce.prototype.key = { TAB: 9, ENTER: 13, BACKSPACE: 8, DELETE: 46, DOWN: 40, SPACE: 32, ESC: 27, CTRL: 17, META: 91, SHIFT: 16, ALT: 18 }; + /** -* Editor interface drawing + * Editor interface drawing + * calls one time in editor constructor */ ce.prototype.makeInterface = function () { var wrapper = this.make.editorWrapper(), firstNode = this.make.textNode('Lorem ipsum dolor sit amet, consectetur adipisicing elit. Porro quia nihil repellendus aut cupiditate reprehenderit sapiente magnam nobis doloremque eaque! Sint nobis assumenda nisi ducimus minima illo tenetur, cumque facilis.' ), - toolbar = this.make.toolbar(), - button, - tool; + toolbar = this.make.toolbar(); this.wrapper = wrapper; this.toolbar = toolbar; - this.toolbarButtons = document.createElement("span"); - this.toolbarButtons.classList.add("buttons"); - - // îáõîäèì áàçîâûé ñïèñîê, ÷òîáû ñîõðàíèòü îðèãèíàëüíóþ ïîñëåäîâàòåëüíîñòü êíîïîê - for (var i = 0; i < this.allTools.length; i++) { - tool = this.allTools[i]; + this.toolbarButtons = this.make.toolbarButtons(this.allTools, this.settings.tools); - if (this.settings.tools.indexOf(tool) < 0) - continue; - - button = this.make.toolbarButton(tool); - this.toolbarButtons.appendChild(button); - } - - - /** Add first node */ + /** Add first node and tool bar*/ wrapper.appendChild(firstNode); - - /** Add toolbar to node */ wrapper.appendChild(toolbar); /** Insert Editor after initial textarea. Hide textarea */ this.resultTextarea.parentNode.insertBefore(wrapper, this.resultTextarea.nextSibling); this.resultTextarea.hidden = true; - /** Set auto focus */ - var contentEditable = firstNode.getElementsByClassName('ce_node_content'); - contentEditable.length && contentEditable[0].focus(); - + this.focusNode(firstNode); }; /** @@ -107,6 +87,17 @@ ce.prototype.bindEvents = function () { }; +/** + * Sets focus to node conteneditable child + * todo depending on node type +*/ +ce.prototype.focusNode = function (node) { + + var contentEditable = node.getElementsByClassName('ce_node_content'); + contentEditable.length && contentEditable[0].focus(); + +}; + /** * All window keydowns handles here */ @@ -125,15 +116,14 @@ ce.prototype.globalKeydownCallback = function (event) { ce.prototype.tabKeyPressed = function(event) { // check if currently focused in contenteditable element - if ("BODY" == event.target.tagName) - return; + if ("BODY" == event.target.tagName) return; var toolbar = event.target.parentNode.nextSibling, _this = this; toolbar.appendChild(this.toolbarButtons); - // repair buttons animation + // repair buttons animation - just add css class async setTimeout(function () { if ( !toolbar.className.includes(_this.BUTTONS_TOGGLED_CLASSNANE) ){ @@ -144,7 +134,7 @@ ce.prototype.tabKeyPressed = function(event) { _this.toolbarOpened = false } - }, 10); + }); event.preventDefault(); @@ -158,11 +148,9 @@ ce.prototype.enterKeyPressed = function(event) { if (event.shiftKey){ document.execCommand('insertHTML', false, '

'); } else { - var - newNode = this.make.textNode(), + var newNode = this.make.textNode(), toolbar = this.make.toolbar(); - /** Add node */ this.wrapper.insertBefore(newNode, event.target.parentNode.nextSibling); @@ -182,6 +170,8 @@ ce.prototype.enterKeyPressed = function(event) { */ ce.prototype.make = function () { + var _this = this; + /** Empty toolbar with toggler */ function toolbar () { @@ -197,6 +187,7 @@ ce.prototype.make = function () { } + // Creates one button with given type function toolbarButton (type) { var button = document.createElement('button'); @@ -205,6 +196,26 @@ ce.prototype.make = function () { button.innerHTML = ''; return button; + + } + + // Creates all tool bar buttons from editor settings + // allTools, usedTools - needs becose cant get them from editor object - bad context + function toolbarButtons (allTools, usedTools) { + + var toolbarButtons = document.createElement("span"); + + toolbarButtons.classList.add("buttons"); + + // Walk base buttons list - save buttons origin sorting + allTools.forEach(function(item) { + + if (usedTools.indexOf(item) >= 0) toolbarButtons.appendChild( this.toolbarButton(item) ); + + }, this); + + return toolbarButtons; + } /** @@ -231,10 +242,11 @@ ce.prototype.make = function () { } var ceMake = function () { - this.toolbar = toolbar; - this.toolbarButton = toolbarButton; - this.textNode = textNode; - this.editorWrapper = editorWrapper; + this.toolbar = toolbar; + this.toolbarButtons = toolbarButtons; + this.toolbarButton = toolbarButton; + this.textNode = textNode; + this.editorWrapper = editorWrapper; }; return new ceMake(); diff --git a/editor.css b/editor.css index 1600b1061..b03455c9a 100644 --- a/editor.css +++ b/editor.css @@ -84,21 +84,21 @@ .codex_editor .node{ position: relative; + z-index: 5; } .codex_editor .add_buttons{ - /* position: relative; */ - /* left: -42px; */ - /* top: -30px; */ color: #3b4352; font-size: 16px; margin-left: -42px; - margin-top: -20px; + margin-top: -50px; margin-bottom: -25px; + position: relative; } .add_buttons .buttons { position: absolute; + top: 3px; } .codex_editor .add_buttons button:hover, .codex_editor .add_buttons .focused{ @@ -109,10 +109,10 @@ transform: translate3d(-50px, 0 , 0); opacity: 0; font-size: 14px; - /*display: none;*/ } .codex_editor .buttons_toggled{ background: #fff; + z-index: 10; } .codex_editor .buttons_toggled button{ opacity: 1; @@ -121,8 +121,6 @@ .codex_editor .toggler{ - /* position: relative; */ - /* left: -42px; */ display: inline-block; font-size: 23px; color: #387ff5; @@ -133,137 +131,12 @@ position: relative; top: -2px; } -.codex_editor .buttons_toggled .plus_btn{ +.codex_editor .buttons_toggled .toggler{ transform: rotate(45deg); } - - - /** Typography styles */ .codex_editor p{ margin: 30px 0; font-size: 17px; -} - - - - - -/* -.editor_content {} -.editor_content label {display:block;margin-bottom:10px;} -.editor_content input[type=file]{} -.editor_content textarea{} -*/ -/* -.editor_content button#btn_save{ - padding: 5px 15px; - float: right; -} -.editor_content .cover{ - border: 1px solid gray; - width: 96%; - display: block; - margin: 5px 0 15px 0; - padding: 2%; -} -.editor_content .cover img{max-width: 100%;margin: auto;display: block;} - -.editor_content .cover .actions { - text-align: center; -} -.editor_content .cover .actions a{ - margin: 0 10px; - border-bottom: 1px dashed; -} -.editor_content .cover .actions a:hover{ - text-decoration:none; -} - -.editor_content .actions span{ - - color: #6091c8; - - margin-right: 15px; - - border-bottom: 1px dashed; - - cursor: pointer; -} - - -.editor_content .add_buttons{ - width: 300px; - margin: 21px auto; - padding: 10px 0; - text-align: center; - background: #F2F2F6; - position: relative; -} -.editor_content .add_buttons:before{ - content: ""; - background: no-repeat url(/public/img/editor_buttons.png) -135px 0px; - position: absolute; - padding: 10px; - left: 140px; - top: 17px; -} -.editor_content .add_buttons:hover:before{background-image: none;} -.editor_content .add_buttons:hover button{ - visibility: visible; - z-index: 0; -} -.editor_content .add_buttons button { - float: none; - padding: 5px; - margin: 0px 0px; - width: 35px; - height: 35px; - border: none; - position: relative; - visibility: hidden; - z-index: -1; - background: transparent; -} -.editor_content .add_buttons button:before { - content: ""; - background: no-repeat url(/public/img/editor_buttons.png?) 0; - position: absolute; - width: 20px; - height: 20px; - left: 8px; - top: 8px; -} -.editor_content .add_buttons .paragraph:before{ - background-position: -156px -1px; -} -.editor_content .add_buttons .img:before{ - background-position: -27px 0; - width: 22px; - left: 7px; -} -.editor_content .add_buttons .list:before{ - background-position: -56px 0; - width: 21px; -} -.editor_content .node{ - border: 1px dashed; - padding: 5px; - margin: 20px 0; -} -.editor_content .node .content{} -.editor_content .node .content p{min-height: 25px;} -.editor_content .node .action_buttons{ - border-top: 1px dashed; - text-align: right; - padding: 5px 0 0; -} -.editor_content .node .action_buttons button{ - width: 35px; - height: 35px; -} - -.editor_wrapper #html_result { - height: 350px; -}*/ \ No newline at end of file +} \ No newline at end of file