Refactored october.hotkey.js, codeeditor.js, richeditor.js. Minor fix in the rich editor styling.

This commit is contained in:
alekseybobkov 2015-04-25 14:07:22 -07:00
parent 87b34295eb
commit 0e4db692af
7 changed files with 297 additions and 154 deletions

View File

@ -1150,7 +1150,9 @@ Base.call(this)
this.init()}
HotKey.prototype=Object.create(BaseProto)
HotKey.prototype.constructor=HotKey
HotKey.prototype.dispose=function(){this.unregisterHandlers()
HotKey.prototype.dispose=function(){if(this.$el===null)
return
this.unregisterHandlers()
this.$el.removeData('oc.hotkey')
this.$target=null
this.$el=null
@ -1165,7 +1167,7 @@ var keys=this.options.hotkey.toLowerCase().split(',')
for(var i=0,len=keys.length;i<len;i++){var keysTrimmed=this.trim(keys[i])
this.keyConditions.push(this.makeCondition(keysTrimmed))}
this.$target.on('keydown',this.proxy(this.onKeyDown))
this.$el.on('dispose-control',this.proxy(this.dispose))}
this.$el.one('dispose-control',this.proxy(this.dispose))}
HotKey.prototype.unregisterHandlers=function(){this.$target.off('keydown',this.proxy(this.onKeyDown))
this.$el.off('dispose-control',this.proxy(this.dispose))}
HotKey.prototype.makeCondition=function(keyBind){var condition={shift:false,ctrl:false,cmd:false,alt:false,specific:-1},keys=keyBind.split('+')

View File

@ -32,6 +32,9 @@
HotKey.prototype.constructor = HotKey
HotKey.prototype.dispose = function() {
if (this.$el === null)
return
this.unregisterHandlers()
this.$el.removeData('oc.hotkey')
@ -58,7 +61,7 @@
}
this.$target.on('keydown', this.proxy(this.onKeyDown))
this.$el.on('dispose-control', this.proxy(this.dispose))
this.$el.one('dispose-control', this.proxy(this.dispose))
}
HotKey.prototype.unregisterHandlers = function() {

View File

@ -31,6 +31,7 @@
this.$toolbar = this.$el.find('>.editor-toolbar:first')
this.$code = null
this.editor = null
this.$form = null
// Toolbar links
this.isFullscreen = false
@ -97,21 +98,16 @@
options = this.options,
$form = this.$el.closest('form');
this.$form = $form
this.$textarea.hide();
editor.getSession().setValue(this.$textarea.val())
$form.on('oc.beforeRequest', function(){
self.$textarea.val(editor.getSession().getValue())
})
editor.on('change', function() {
$form.trigger('change')
self.$textarea.trigger('oc.codeEditorChange')
})
$(window).on('resize, oc.updateUi', function() {
editor.resize()
})
editor.on('change', this.proxy(this.onChange))
$form.on('oc.beforeRequest', this.proxy(this.onBeforeRequest))
$(window).on('resize', this.proxy(this.onResize))
$(window).on('oc.updateUi', this.proxy(this.onResize))
this.$el.on('dispose-control', this.proxy(this.dispose))
/*
* Set language and theme
@ -144,8 +140,8 @@
editor.setReadOnly(options.readOnly)
editor.getSession().setFoldStyle(options.codeFolding)
editor.setFontSize(options.fontSize)
editor.on('blur.codeeditor', function(){ self.$el.removeClass('editor-focus') })
editor.on('focus.codeeditor', function(){ self.$el.addClass('editor-focus') })
editor.on('blur', this.proxy(this.onBlur))
editor.on('focus', this.proxy(this.onFocus))
this.setWordWrap(options.wordWrap)
editor.renderer.setScrollMargin(options.margin, options.margin, 0, 0)
@ -178,9 +174,11 @@
/*
* Hotkeys
*/
this.$el.hotKey({ hotkey: 'esc', hotkeyMac: 'esc', callback: function() {
self.isFullscreen && self.toggleFullscreen.apply(self)
}})
this.$el.hotKey({
hotkey: 'esc',
hotkeyMac: 'esc',
callback: this.proxy(this.onEscape)
})
editor.commands.addCommand({
name: 'toggleFullscreen',
@ -191,23 +189,90 @@
}
CodeEditor.prototype.dispose = function() {
this.editor.off('.codeeditor')
this.editor.destroy()
// Currently it's not possible to dispose Ace Editor
// completely because of the internal organization of
// because of the internal organization of the class.
// The class instance contains multiple references to
// DOM elements in closures and event handlers.
// See https://github.com/ajaxorg/ace/issues/2469
// --ab 2015-04-23
this.$fullscreenEnable.off('.codeeditor')
this.$fullscreenDisable.off('.codeeditor')
if (this.$el === null)
return
this.unregisterHandlers()
this.disposeAttachedControls()
this.$el = null
this.$textarea = null
this.$toolbar = null
this.$code = null
this.editor = null
this.$fullscreenEnable = null
this.$fullscreenDisable = null
this.$form = null
this.options = null
BaseProto.dispose.call(this)
}
CodeEditor.prototype.disposeAttachedControls = function() {
this.editor.destroy()
var keys = Object.keys(this.editor.renderer)
for (var i=0, len=keys.length; i<len; i++)
this.editor.renderer[keys[i]] = null
keys = Object.keys(this.editor)
for (var i=0, len=keys.length; i<len; i++)
this.editor[keys[i]] = null
this.editor = null
this.$toolbar.find('>ul>li>a').tooltip('destroy')
this.$el.removeData('oc.codeEditor')
this.$el.hotKey('dispose')
}
CodeEditor.prototype.unregisterHandlers = function() {
this.editor.off('change', this.proxy(this.onChange))
this.editor.off('blur', this.proxy(this.onBlur))
this.editor.off('focus', this.proxy(this.onFocus))
this.$fullscreenEnable.off('.codeeditor')
this.$fullscreenDisable.off('.codeeditor')
this.$form.off('oc.beforeRequest', this.proxy(this.onBeforeRequest))
this.$el.off('dispose-control', this.proxy(this.dispose))
$(window).off('resize', this.proxy(this.onResize))
$(window).off('oc.updateUi', this.proxy(this.onResize))
}
CodeEditor.prototype.onBeforeRequest = function() {
this.$textarea.val(this.editor.getSession().getValue())
}
CodeEditor.prototype.onChange = function() {
this.$form.trigger('change')
this.$textarea.trigger('oc.codeEditorChange')
}
CodeEditor.prototype.onResize = function() {
this.editor.resize()
}
CodeEditor.prototype.onBlur = function() {
this.$el.removeClass('editor-focus')
}
CodeEditor.prototype.onFocus = function() {
this.$el.addClass('editor-focus')
}
CodeEditor.prototype.onEscape = function() {
this.isFullscreen && this.toggleFullscreen()
}
CodeEditor.prototype.setWordWrap = function(mode) {
var session = this.editor.getSession(),
renderer = this.editor.renderer

View File

@ -170,7 +170,7 @@ to{background-position:0 0}
.field-richeditor.size-large .redactor-editor{height:170px !important}
.field-richeditor.size-huge .redactor-editor{height:220px !important}
.field-richeditor.size-giant .redactor-editor{height:320px !important}
.redactor-box{margin-bottom:0}
.redactor-box{margin-bottom:0;overflow:hidden}
.redactor-box iframe{border:none}
.redactor-box-fullscreen{z-index:715 !important}
.redactor-dropdown{z-index:725 !important}

View File

@ -1816,27 +1816,45 @@ this.code.sync();},addColumn:function(type)
{var $current=$(elem).find('td, th').eq(index);var td=$current.clone();td.html(this.opts.invisibleSpace);if(type=='after')
{$current.after(td);}
else
{$current.before(td);}},this));this.code.sync();}};};})(jQuery);+function($){"use strict";var RichEditor=function(element,options){this.options=options
{$current.before(td);}},this));this.code.sync();}};};})(jQuery);+function($){"use strict";var Base=$.oc.foundation.base,BaseProto=Base.prototype
var RichEditor=function(element,options){this.options=options
this.$el=$(element)
this.$textarea=this.$el.find('>textarea:first')
this.$form=this.$el.closest('form')
this.$dataLocker=null
this.$editor=null
this.redactor=null
Base.call(this)
this.init();}
RichEditor.prototype=Object.create(BaseProto)
RichEditor.prototype.constructor=RichEditor
RichEditor.DEFAULTS={stylesheet:null,fullpage:false}
RichEditor.prototype.init=function(){var self=this;if(this.options.dataLocker){this.$dataLocker=$(this.options.dataLocker)
RichEditor.prototype.init=function(){var self=this;this.$el.one('dispose-control',this.proxy(this.dispose))
if(this.options.dataLocker){this.$dataLocker=$(this.options.dataLocker)
this.$textarea.val(this.$dataLocker.val())}
if(!this.$textarea.attr('id')){this.$textarea.attr('id','element-'+Math.random().toString(36).substring(7))}
var redactorOptions={imageEditable:true,imageResizable:true,buttonSource:true,removeDataAttr:false,syncBeforeCallback:function(html){return self.syncBefore(html)},focusCallback:function(){self.$el.addClass('editor-focus')},blurCallback:function(){self.$el.removeClass('editor-focus')},keydownCallback:function(e){return self.keydown(e,this.$editor)},enterCallback:function(e){return self.enter(e,this.$editor)},initCallback:function(){self.build(this)},changeCallback:function(){self.sanityCheckContent(this.$editor)
this.$editor.trigger('mutate')
self.$form.trigger('change')
if(self.$dataLocker)
self.$dataLocker.val(self.syncBefore(this.$editor.html()))}}
var redactorOptions={imageEditable:true,imageResizable:true,buttonSource:true,removeDataAttr:false,syncBeforeCallback:this.proxy(this.onSyncBefore),focusCallback:this.proxy(this.onFocus),blurCallback:this.proxy(this.onBlur),keydownCallback:this.proxy(this.onKeydown),enterCallback:this.proxy(this.onEnter),changeCallback:this.proxy(this.onChange),initCallback:function(){self.build(this)}}
if(this.options.fullpage){redactorOptions.fullpage=true}
redactorOptions.plugins=['fullscreen','table','mediamanager']
redactorOptions.buttons=['formatting','bold','italic','unorderedlist','orderedlist','link','horizontalrule','html'],this.$textarea.redactor(redactorOptions)}
redactorOptions.buttons=['formatting','bold','italic','unorderedlist','orderedlist','link','horizontalrule','html'],this.$textarea.redactor(redactorOptions)
this.redactor=this.$textarea.redactor('core.getObject')
this.$editor=this.redactor.$editor}
RichEditor.prototype.dispose=function(){this.unregisterHandlers()
this.$textarea.redactor('core.destroy');this.$el.removeData('oc.richEditor')
this.options=null
this.$el=null
this.$textarea=null
this.$form=null
this.$dataLocker=null
this.$editor=null
this.redactor=null
BaseProto.dispose.call(this)}
RichEditor.prototype.unregisterHandlers=function(){$(window).off('resize',this.proxy(this.updateLayout))
$(window).off('oc.updateUi',this.proxy(this.updateLayout))
this.$el.off('dispose-control',this.proxy(this.dispose))}
RichEditor.prototype.build=function(redactor){this.updateLayout()
$(window).resize($.proxy(this.updateLayout,this))
$(window).on('oc.updateUi',$.proxy(this.updateLayout,this))
$(window).on('resize',this.proxy(this.updateLayout))
$(window).on('oc.updateUi',this.proxy(this.updateLayout))
this.$textarea.trigger('init.oc.richeditor',[this.$el])
this.initUiBlocks()
var self=this
@ -1847,9 +1865,9 @@ return
if(this.$el.hasClass('stretch')){var height=$toolbar.outerHeight(true)
$editor.css('top',height+1)
$codeEditor.css('top',height)}}
RichEditor.prototype.sanityCheckContent=function($editor){var safeElements='p, h1, h2, h3, h4, h5, pre, figure';if(!$editor.children(':last-child').is(safeElements)){$editor.append('<p><br></p>')}
if(!$editor.children(':first-child').is(safeElements)){$editor.prepend('<p><br></p>')}
this.$textarea.trigger('sanitize.oc.richeditor',[$editor])}
RichEditor.prototype.sanityCheckContent=function(){var safeElements='p, h1, h2, h3, h4, h5, pre, figure';if(!this.$editor.children(':last-child').is(safeElements)){this.$editor.append('<p><br></p>')}
if(!this.$editor.children(':first-child').is(safeElements)){this.$editor.prepend('<p><br></p>')}
this.$textarea.trigger('sanitize.oc.richeditor',[this.$editor])}
RichEditor.prototype.syncBefore=function(html){var container={html:html}
this.$textarea.trigger('syncBefore.oc.richeditor',[container])
var $domTree=$('<div>'+container.html+'</div>')
@ -1861,73 +1879,79 @@ $(this).remove()})
$domTree.find('[data-video], [data-audio]').each(function(){$(this).removeAttr('contenteditable data-ui-block tabindex')})
$domTree.find('div.oc-figure-controls').remove()
return $domTree.html()}
RichEditor.prototype.keydown=function(e,$editor){this.$textarea.trigger('keydown.oc.richeditor',[e,$editor,this.$textarea])
if(e.isDefaultPrevented())
return false
this.handleUiBlocksKeydown(e,$editor,this.$textarea)
if(e.isDefaultPrevented())
return false}
RichEditor.prototype.enter=function(e,$editor){this.$textarea.trigger('enter.oc.richeditor',[e,$editor,this.$textarea])
if(e.isDefaultPrevented())
return false
this.handleUiBlocksKeydown(e,$editor,this.$textarea)
if(e.isDefaultPrevented())
return false}
RichEditor.prototype.onShowFigureToolbar=function($figure,$toolbar){var toolbarTop=$figure.position().top-$toolbar.height()-10
$toolbar.toggleClass('bottom',toolbarTop<0)}
RichEditor.prototype.insertUiBlock=function($node){var redactor=this.$textarea.redactor('core.getObject'),current=redactor.selection.getCurrent(),inserted=false
RichEditor.prototype.insertUiBlock=function($node){var current=this.redactor.selection.getCurrent(),inserted=false
if(current===false)
redactor.focus.setStart()
current=redactor.selection.getCurrent()
this.redactor.focus.setStart()
current=this.redactor.selection.getCurrent()
if(current!==false){var $paragraph=$(current).closest('p')
if($paragraph.length>0){redactor.caret.setAfter($paragraph.get(0))
if($paragraph.length>0){this.redactor.caret.setAfter($paragraph.get(0))
if($.trim($paragraph.text()).length==0)
$paragraph.remove()}else{var $closestBlock=$(current).closest('[data-ui-block]')
if($closestBlock.length>0){$node.insertBefore($closestBlock.get(0))
inserted=true}}}
if(!inserted)
redactor.insert.node($node)
redactor.code.sync()
this.redactor.insert.node($node)
this.redactor.code.sync()
$node.focus()}
RichEditor.prototype.initUiBlocks=function(){$('.redactor-editor [data-video], .redactor-editor [data-audio]',this.$el).each(function(){$(this).attr({'data-ui-block':true,'tabindex':'0'})
this.contentEditable=false})}
RichEditor.prototype.handleUiBlocksKeydown=function(originalEv,$editor,$textarea){if($textarea===undefined)
RichEditor.prototype.handleUiBlocksKeydown=function(ev){if(this.$textarea===undefined)
return
var redactor=$textarea.redactor('core.getObject')
if(originalEv.target&&$(originalEv.target).attr('data-ui-block')!==undefined){this.uiBlockKeyDown(originalEv,originalEv.target)
originalEv.preventDefault()
if(ev.target&&$(ev.target).attr('data-ui-block')!==undefined){this.uiBlockKeyDown(ev,ev.target)
ev.preventDefault()
return}
switch(originalEv.which){case 38:var block=redactor.selection.getBlock()
switch(ev.which){case 38:var block=this.redactor.selection.getBlock()
if(block)
this.handleUiBlockCaretIn($(block).prev(),redactor)
this.handleUiBlockCaretIn($(block).prev())
break
case 40:var block=redactor.selection.getBlock()
case 40:var block=this.redactor.selection.getBlock()
if(block)
this.handleUiBlockCaretIn($(block).next(),redactor)
this.handleUiBlockCaretIn($(block).next())
break}}
RichEditor.prototype.handleUiBlockCaretIn=function($block,redactor){if($block.attr('data-ui-block')!==undefined){$block.focus()
redactor.selection.remove()
RichEditor.prototype.handleUiBlockCaretIn=function($block){if($block.attr('data-ui-block')!==undefined){$block.focus()
this.redactor.selection.remove()
return true}
return false}
RichEditor.prototype.uiBlockKeyDown=function(ev,block){if(ev.which==40||ev.which==38||ev.which==13||ev.which==8){var $textarea=$(block).closest('.redactor-box').find('textarea'),redactor=$textarea.redactor('core.getObject')
switch(ev.which){case 40:this.focusUiBlockOrText(redactor,$(block).next(),true)
RichEditor.prototype.uiBlockKeyDown=function(ev,block){if(ev.which==40||ev.which==38||ev.which==13||ev.which==8){switch(ev.which){case 40:this.focusUiBlockOrText($(block).next(),true)
break
case 38:this.focusUiBlockOrText(redactor,$(block).prev(),false)
case 38:this.focusUiBlockOrText($(block).prev(),false)
break
case 13:var $paragraph=$('<p><br/></p>')
$paragraph.insertAfter(block)
redactor.caret.setStart($paragraph.get(0))
this.redactor.caret.setStart($paragraph.get(0))
break
case 8:var $nextFocus=$(block).next(),gotoStart=true
if($nextFocus.length==0){$nextFocus=$(block).prev()
gotoStart=false}
this.focusUiBlockOrText(redactor,$nextFocus,gotoStart)
this.focusUiBlockOrText($nextFocus,gotoStart)
$(block).remove()
break}}}
RichEditor.prototype.focusUiBlockOrText=function(redactor,$block,gotoStart){if($block.length>0){if(!this.handleUiBlockCaretIn($block,redactor)){if(gotoStart)
redactor.caret.setStart($block.get(0))
RichEditor.prototype.focusUiBlockOrText=function($block,gotoStart){if($block.length>0){if(!this.handleUiBlockCaretIn($block,this.redactor)){if(gotoStart)
this.redactor.caret.setStart($block.get(0))
else
redactor.caret.setEnd($block.get(0))}}}
this.redactor.caret.setEnd($block.get(0))}}}
RichEditor.prototype.onSyncBefore=function(html){return this.syncBefore(html)}
RichEditor.prototype.onFocus=function(){this.$el.addClass('editor-focus')}
RichEditor.prototype.onBlur=function(){this.$el.removeClass('editor-focus')}
RichEditor.prototype.onKeydown=function(ev){this.$textarea.trigger('keydown.oc.richeditor',[ev,this.$editor,this.$textarea])
if(ev.isDefaultPrevented())
return false
this.handleUiBlocksKeydown(ev)
if(ev.isDefaultPrevented())
return false}
RichEditor.prototype.onEnter=function(ev){this.$textarea.trigger('enter.oc.richeditor',[ev,this.$editor,this.$textarea])
if(ev.isDefaultPrevented())
return false
this.handleUiBlocksKeydown(ev)
if(ev.isDefaultPrevented())
return false}
RichEditor.prototype.onChange=function(ev){this.sanityCheckContent()
this.$editor.trigger('mutate')
this.$form.trigger('change')
if(this.$dataLocker)
this.$dataLocker.val(this.syncBefore(this.$editor.html()))}
var old=$.fn.richEditor
$.fn.richEditor=function(option){var args=arguments;return this.each(function(){var $this=$(this)
var data=$this.data('oc.richEditor')

View File

@ -11,6 +11,9 @@
* - Redactor Editor (redactor.js)
*/
+function ($) { "use strict";
var Base = $.oc.foundation.base,
BaseProto = Base.prototype
// RICHEDITOR CLASS DEFINITION
// ============================
@ -21,19 +24,27 @@
this.$textarea = this.$el.find('>textarea:first')
this.$form = this.$el.closest('form')
this.$dataLocker = null
this.$editor = null
this.redactor = null
Base.call(this)
this.init();
}
RichEditor.prototype = Object.create(BaseProto)
RichEditor.prototype.constructor = RichEditor
RichEditor.DEFAULTS = {
stylesheet: null,
fullpage: false
}
RichEditor.prototype.init = function (){
var self = this;
this.$el.one('dispose-control', this.proxy(this.dispose))
/*
* Sync all changes to a data locker, since fullscreen mode
* will pull the textarea outside of the form element.
@ -58,20 +69,13 @@
imageResizable: true,
buttonSource: true,
removeDataAttr: false,
syncBeforeCallback: function(html) { return self.syncBefore(html) },
focusCallback: function() { self.$el.addClass('editor-focus') },
blurCallback: function() { self.$el.removeClass('editor-focus') },
keydownCallback: function(e) { return self.keydown(e, this.$editor) },
enterCallback: function(e) { return self.enter(e, this.$editor) },
initCallback: function() { self.build(this) },
changeCallback: function() {
self.sanityCheckContent(this.$editor)
this.$editor.trigger('mutate')
self.$form.trigger('change')
if (self.$dataLocker)
self.$dataLocker.val(self.syncBefore(this.$editor.html()))
}
syncBeforeCallback: this.proxy(this.onSyncBefore),
focusCallback: this.proxy(this.onFocus),
blurCallback: this.proxy(this.onBlur),
keydownCallback: this.proxy(this.onKeydown),
enterCallback: this.proxy(this.onEnter),
changeCallback: this.proxy(this.onChange),
initCallback: function() { self.build(this) }
}
if (this.options.fullpage) {
@ -82,13 +86,39 @@
redactorOptions.buttons = ['formatting', 'bold', 'italic', 'unorderedlist', 'orderedlist', 'link', 'horizontalrule', 'html'],
this.$textarea.redactor(redactorOptions)
this.redactor = this.$textarea.redactor('core.getObject')
this.$editor = this.redactor.$editor
}
RichEditor.prototype.dispose = function() {
this.unregisterHandlers()
this.$textarea.redactor('core.destroy');
this.$el.removeData('oc.richEditor')
this.options = null
this.$el = null
this.$textarea = null
this.$form = null
this.$dataLocker = null
this.$editor = null
this.redactor = null
BaseProto.dispose.call(this)
}
RichEditor.prototype.unregisterHandlers = function() {
$(window).off('resize', this.proxy(this.updateLayout))
$(window).off('oc.updateUi', this.proxy(this.updateLayout))
this.$el.off('dispose-control', this.proxy(this.dispose))
}
RichEditor.prototype.build = function(redactor) {
this.updateLayout()
$(window).resize($.proxy(this.updateLayout, this))
$(window).on('oc.updateUi', $.proxy(this.updateLayout, this))
$(window).on('resize', this.proxy(this.updateLayout))
$(window).on('oc.updateUi', this.proxy(this.updateLayout))
this.$textarea.trigger('init.oc.richeditor', [this.$el])
@ -117,19 +147,19 @@
}
}
RichEditor.prototype.sanityCheckContent = function($editor) {
RichEditor.prototype.sanityCheckContent = function() {
// First and last elements should always be paragraphs or pre
var safeElements = 'p, h1, h2, h3, h4, h5, pre, figure';
if (!$editor.children(':last-child').is(safeElements)) {
$editor.append('<p><br></p>')
if (!this.$editor.children(':last-child').is(safeElements)) {
this.$editor.append('<p><br></p>')
}
if (!$editor.children(':first-child').is(safeElements)) {
$editor.prepend('<p><br></p>')
if (!this.$editor.children(':first-child').is(safeElements)) {
this.$editor.prepend('<p><br></p>')
}
this.$textarea.trigger('sanitize.oc.richeditor', [$editor])
this.$textarea.trigger('sanitize.oc.richeditor', [this.$editor])
}
RichEditor.prototype.syncBefore = function(html) {
@ -165,30 +195,6 @@
return $domTree.html()
}
RichEditor.prototype.keydown = function(e, $editor) {
this.$textarea.trigger('keydown.oc.richeditor', [e, $editor, this.$textarea])
if (e.isDefaultPrevented())
return false
this.handleUiBlocksKeydown(e, $editor, this.$textarea)
if (e.isDefaultPrevented())
return false
}
RichEditor.prototype.enter = function(e, $editor) {
this.$textarea.trigger('enter.oc.richeditor', [e, $editor, this.$textarea])
if (e.isDefaultPrevented())
return false
this.handleUiBlocksKeydown(e, $editor, this.$textarea)
if (e.isDefaultPrevented())
return false
}
RichEditor.prototype.onShowFigureToolbar = function($figure, $toolbar) {
// Deal with the case when the toolbar top has negative
// value
@ -201,20 +207,19 @@
* Inserts non-editable block (used for snippets, audio and video)
*/
RichEditor.prototype.insertUiBlock = function($node) {
var redactor = this.$textarea.redactor('core.getObject'),
current = redactor.selection.getCurrent(),
var current = this.redactor.selection.getCurrent(),
inserted = false
if (current === false)
redactor.focus.setStart()
this.redactor.focus.setStart()
current = redactor.selection.getCurrent()
current = this.redactor.selection.getCurrent()
if (current !== false) {
// If the block is inserted into a paragraph, insert it after the paragraph.
var $paragraph = $(current).closest('p')
if ($paragraph.length > 0) {
redactor.caret.setAfter($paragraph.get(0))
this.redactor.caret.setAfter($paragraph.get(0))
// If the paragraph is empty, remove it.
if ($.trim($paragraph.text()).length == 0)
@ -230,9 +235,9 @@
}
if (!inserted)
redactor.insert.node($node)
this.redactor.insert.node($node)
redactor.code.sync()
this.redactor.code.sync()
$node.focus()
}
@ -247,39 +252,37 @@
})
}
RichEditor.prototype.handleUiBlocksKeydown = function(originalEv, $editor, $textarea) {
if ($textarea === undefined)
RichEditor.prototype.handleUiBlocksKeydown = function(ev) {
if (this.$textarea === undefined)
return
var redactor = $textarea.redactor('core.getObject')
if (ev.target && $(ev.target).attr('data-ui-block') !== undefined) {
this.uiBlockKeyDown(ev, ev.target)
if (originalEv.target && $(originalEv.target).attr('data-ui-block') !== undefined) {
this.uiBlockKeyDown(originalEv, originalEv.target)
originalEv.preventDefault()
ev.preventDefault()
return
}
switch (originalEv.which) {
switch (ev.which) {
case 38:
// Up arrow
var block = redactor.selection.getBlock()
var block = this.redactor.selection.getBlock()
if (block)
this.handleUiBlockCaretIn($(block).prev(), redactor)
this.handleUiBlockCaretIn($(block).prev())
break
case 40:
// Down arrow
var block = redactor.selection.getBlock()
var block = this.redactor.selection.getBlock()
if (block)
this.handleUiBlockCaretIn($(block).next(), redactor)
this.handleUiBlockCaretIn($(block).next())
break
}
}
RichEditor.prototype.handleUiBlockCaretIn = function($block, redactor) {
RichEditor.prototype.handleUiBlockCaretIn = function($block) {
if ($block.attr('data-ui-block') !== undefined) {
$block.focus()
redactor.selection.remove()
this.redactor.selection.remove()
return true
}
@ -289,23 +292,20 @@
RichEditor.prototype.uiBlockKeyDown = function(ev, block) {
if (ev.which == 40 || ev.which == 38 || ev.which == 13 || ev.which == 8) {
var $textarea = $(block).closest('.redactor-box').find('textarea'),
redactor = $textarea.redactor('core.getObject')
switch (ev.which) {
case 40:
// Down arrow
this.focusUiBlockOrText(redactor, $(block).next(), true)
this.focusUiBlockOrText($(block).next(), true)
break
case 38:
// Up arrow
this.focusUiBlockOrText(redactor, $(block).prev(), false)
this.focusUiBlockOrText($(block).prev(), false)
break
case 13:
// Enter key
var $paragraph = $('<p><br/></p>')
$paragraph.insertAfter(block)
redactor.caret.setStart($paragraph.get(0))
this.redactor.caret.setStart($paragraph.get(0))
break
case 8:
// Backspace key
@ -317,7 +317,7 @@
gotoStart = false
}
this.focusUiBlockOrText(redactor, $nextFocus, gotoStart)
this.focusUiBlockOrText($nextFocus, gotoStart)
$(block).remove()
break
@ -325,17 +325,65 @@
}
}
RichEditor.prototype.focusUiBlockOrText = function(redactor, $block, gotoStart) {
RichEditor.prototype.focusUiBlockOrText = function($block, gotoStart) {
if ($block.length > 0) {
if (!this.handleUiBlockCaretIn($block, redactor)) {
if (!this.handleUiBlockCaretIn($block, this.redactor)) {
if (gotoStart)
redactor.caret.setStart($block.get(0))
this.redactor.caret.setStart($block.get(0))
else
redactor.caret.setEnd($block.get(0))
this.redactor.caret.setEnd($block.get(0))
}
}
}
// EVENT HANDLERS
// ============================
RichEditor.prototype.onSyncBefore = function(html) {
return this.syncBefore(html)
}
RichEditor.prototype.onFocus = function() {
this.$el.addClass('editor-focus')
}
RichEditor.prototype.onBlur = function() {
this.$el.removeClass('editor-focus')
}
RichEditor.prototype.onKeydown = function(ev) {
this.$textarea.trigger('keydown.oc.richeditor', [ev, this.$editor, this.$textarea])
if (ev.isDefaultPrevented())
return false
this.handleUiBlocksKeydown(ev)
if (ev.isDefaultPrevented())
return false
}
RichEditor.prototype.onEnter = function(ev) {
this.$textarea.trigger('enter.oc.richeditor', [ev, this.$editor, this.$textarea])
if (ev.isDefaultPrevented())
return false
this.handleUiBlocksKeydown(ev)
if (ev.isDefaultPrevented())
return false
}
RichEditor.prototype.onChange = function(ev) {
this.sanityCheckContent()
this.$editor.trigger('mutate')
this.$form.trigger('change')
if (this.$dataLocker)
this.$dataLocker.val(this.syncBefore(this.$editor.html()))
}
// RICHEDITOR PLUGIN DEFINITION
// ============================

View File

@ -38,6 +38,7 @@
.redactor-box {
margin-bottom: 0;
overflow: hidden;
& iframe {
border: none; // Oc