2015-11-28 23:10:25 +01:00
/ * *
* Some UI experiments for CodeX Editor
* @ author Savchenko Peter ( vk . com / specc )
* /
2015-12-09 01:34:32 +01:00
var ce = function ( settings ) {
2015-11-28 23:10:25 +01:00
2015-12-09 01:34:32 +01:00
this . textareaId = "codex_editor" ;
this . resultTextarea = document . getElementById ( this . textareaId ) ;
2015-11-28 23:10:25 +01:00
if ( typeof this . resultTextarea == undefined || this . resultTextarea == null ) {
2015-12-09 01:34:32 +01:00
console . warn ( 'Textarea not found with ID %o' , this . textareaId ) ;
2015-11-28 23:10:25 +01:00
return this ;
}
2015-12-09 01:34:32 +01:00
// 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 ;
2015-11-28 23:10:25 +01:00
/** Some configurations */
2015-12-09 01:34:32 +01:00
this . toolbarOpened = false ;
2015-11-28 23:10:25 +01:00
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 ( ) ;
/** Bind all events */
this . bindEvents ( ) ;
2015-12-09 01:34:32 +01:00
} ;
2015-11-28 23:10:25 +01:00
/ * *
* Editor interface drawing
* /
ce . prototype . makeInterface = function ( ) {
var wrapper = this . make . editorWrapper ( ) ,
2015-12-09 01:34:32 +01:00
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.' ) ,
2015-11-28 23:10:25 +01:00
toolbar = this . make . toolbar ( ) ,
2015-12-09 01:34:32 +01:00
button ,
tool ;
2015-11-28 23:10:25 +01:00
2015-12-09 01:34:32 +01:00
this . wrapper = wrapper ;
this . toolbar = toolbar ;
this . toolbarButtons = document . createElement ( "span" ) ;
this . toolbarButtons . classList . add ( "buttons" ) ;
// <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> , <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
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 ) ;
}
2015-11-28 23:10:25 +01:00
/** Add first node */
wrapper . appendChild ( firstNode ) ;
2015-12-09 01:34:32 +01:00
/** Add toolbar to node */
wrapper . appendChild ( toolbar ) ;
2015-11-28 23:10:25 +01:00
/** 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 ( ) ;
2015-12-09 01:34:32 +01:00
} ;
2015-11-28 23:10:25 +01:00
/ * *
* All events binds in one place
* /
ce . prototype . bindEvents = function ( ) {
var _this = this ;
/** All keydowns on Window */
window . addEventListener ( 'keydown' , function ( event ) {
_this . globalKeydownCallback ( event ) ;
} , false ) ;
2015-12-09 01:34:32 +01:00
} ;
2015-11-28 23:10:25 +01:00
/ * *
* All window keydowns handles here
* /
ce . prototype . globalKeydownCallback = function ( event ) {
switch ( event . keyCode ) {
case this . key . TAB : this . tabKeyPressed ( event ) ; break ; // TAB
case this . key . ENTER : this . enterKeyPressed ( event ) ; break ; // Enter
}
2015-12-09 01:34:32 +01:00
} ;
2015-11-28 23:10:25 +01:00
/ * *
2015-12-09 01:34:32 +01:00
*
2015-11-28 23:10:25 +01:00
* /
ce . prototype . tabKeyPressed = function ( event ) {
2015-12-09 01:34:32 +01:00
// check if currently focused in contenteditable element
if ( "BODY" == event . target . tagName )
return ;
2015-11-28 23:10:25 +01:00
2015-12-09 01:34:32 +01:00
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 ) ;
2015-11-28 23:10:25 +01:00
event . preventDefault ( ) ;
2015-12-09 01:34:32 +01:00
} ;
2015-11-28 23:10:25 +01:00
/ * *
* Handle Enter key . Adds new Node ;
* /
ce . prototype . enterKeyPressed = function ( event ) {
2015-12-09 01:34:32 +01:00
if ( event . shiftKey ) {
document . execCommand ( 'insertHTML' , false , '<br><br>' ) ;
} else {
var
newNode = this . make . textNode ( ) ,
toolbar = this . make . toolbar ( ) ;
2015-11-28 23:10:25 +01:00
2015-12-09 01:34:32 +01:00
/** 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 ( ) ;
} ;
2015-11-28 23:10:25 +01:00
/ * *
* Creates HTML elements
* /
2015-12-09 01:34:32 +01:00
ce . prototype . make = function ( ) {
2015-11-28 23:10:25 +01:00
/** Empty toolbar with toggler */
function toolbar ( ) {
var bar = document . createElement ( 'div' ) ;
bar . className += 'add_buttons' ;
/** Toggler button*/
bar . innerHTML = '<span class="toggler">' +
2015-12-09 01:34:32 +01:00
'<i class="plus_btn ce_icon-plus-circled-1"></i>' +
2015-11-28 23:10:25 +01:00
'</span>' ;
return bar ;
}
function toolbarButton ( type ) {
var button = document . createElement ( 'button' ) ;
button . dataset . type = type ;
button . innerHTML = '<i class="ce_icon-' + type + '"></i>' ;
return button ;
}
/ * *
* Paragraph node
* @ todo set unique id with prefix
* /
2015-12-09 01:34:32 +01:00
function textNode ( content ) {
2015-11-28 23:10:25 +01:00
var node = document . createElement ( 'div' ) ;
node . className += 'node' ;
node . innerHTML = '<p class="ce_node_content" contenteditable="true">' + ( content || '' ) + '</p>' ;
return node ;
}
function editorWrapper ( ) {
var wrapper = document . createElement ( 'div' ) ;
wrapper . className += 'codex_editor' ;
return wrapper ;
}
var ceMake = function ( ) {
this . toolbar = toolbar ;
this . toolbarButton = toolbarButton ;
2015-12-09 01:34:32 +01:00
this . textNode = textNode ;
2015-11-28 23:10:25 +01:00
this . editorWrapper = editorWrapper ;
2015-12-09 01:34:32 +01:00
} ;
2015-11-28 23:10:25 +01:00
return new ceMake ( ) ;
2015-12-09 01:34:32 +01:00
} ( ) ;
2015-11-28 23:10:25 +01:00
/ * *
* Polyfilling ECMAScript 6 method String . includes
* https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Browser_compatibility
* /
if ( ! String . prototype . includes ) {
String . prototype . includes = function ( ) {
'use strict' ;
return String . prototype . indexOf . apply ( this , arguments ) !== - 1 ;
} ;
}