Fixed the Richeditor's UI blocks toolbar.

This commit is contained in:
alekseybobkov 2015-04-25 14:32:32 -07:00
parent 0e4db692af
commit e3a4dd64c5
7 changed files with 748 additions and 4 deletions

File diff suppressed because one or more lines are too long

View File

@ -1816,7 +1816,65 @@ 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 Base=$.oc.foundation.base,BaseProto=Base.prototype
{$current.before(td);}},this));this.code.sync();}};};})(jQuery);(function($){'use strict';window.RedactorPlugins=window.RedactorPlugins||{}
var Figure=function(redactor){this.redactor=redactor
this.toolbar={}
this.init()}
Figure.prototype={control:{up:{classSuffix:'arrow-up'},down:{classSuffix:'arrow-down'},'|':{classSuffix:'divider'},remove:{classSuffix:'delete'}},controlGroup:['up','down','remove'],init:function(){this.observeCaptions()
this.observeToolbars()
this.observeKeyboard()},observeCaptions:function(){this.redactor.$editor.on('click','figcaption:empty, cite:empty',$.proxy(function(event){$(event.target).prepend('<br />')
this.redactor.caret.setEnd(event.target)
event.stopPropagation()},this))
$(window).on('click',$.proxy(this.cleanCaptions,this))
this.redactor.$editor.on('blur',$.proxy(this.cleanCaptions,this))
this.redactor.$editor.closest('form').one('submit',$.proxy(this.clearCaptions,this))
this.redactor.$editor.on('keydown',$.proxy(function(event){var current=this.redactor.selection.getCurrent(),isEmpty=!current.length,isCaptionNode=!!$(current).closest('figcaption, cite').length,isDeleteKey=$.inArray(event.keyCode,[this.redactor.keyCode.BACKSPACE,this.redactor.keyCode.DELETE])>=0
if(isEmpty&&isDeleteKey&&isCaptionNode){event.preventDefault()}},this))},cleanCaptions:function(){this.redactor.$editor.find('figcaption, cite').filter(function(){return $(this).text()==''}).empty()},clearCaptions:function(){this.redactor.$editor.find('figcaption, cite').filter(function(){return $(this).text()==''}).remove()
if(this.redactor.opts.visual){this.redactor.code.sync()}},showToolbar:function(event){var $figure=$(event.currentTarget),type=$figure.data('type')||'default',$toolbar=this.getToolbar(type).data('figure',$figure).prependTo($figure).show()
if(this.redactor[type]&&this.redactor[type].onShow){this.redactor[type].onShow($figure,$toolbar)}},hideToolbar:function(event){$(event.currentTarget).find('.oc-figure-controls').appendTo(this.redactor.$box).hide()},observeToolbars:function(){this.redactor.$editor.on('mousedown','.oc-figure-controls',$.proxy(function(event){event.preventDefault()
this.current=this.redactor.selection.getCurrent()},this))
this.redactor.$editor.on('click','.oc-figure-controls span, .oc-figure-controls a',$.proxy(function(event){event.stopPropagation()
var $target=$(event.currentTarget),command=$target.data('command'),$figure=$target.closest('figure'),plugin=this.redactor[$figure.data('type')]
this.command(command,$figure,plugin)},this))
this.redactor.$editor.on('keydown',function(){$(this).find('figure').triggerHandler('mouseleave')})
if(this.redactor.utils.isMobile()){this.redactor.$editor.on('touchstart','figure',function(event){if(event.target.nodeName!=='FIGCAPTION'&&$(event.target).parents('.oc-figure-controls').length){$(this).trigger('click',event)}})
this.redactor.$editor.on('click','figure',$.proxy(function(event){if(event.target.nodeName!=='FIGCAPTION'){this.redactor.$editor.trigger('blur')}
this.showToolbar(event)},this))}
else{this.redactor.$editor.on('mouseenter','figure',$.proxy(this.showToolbar,this))
this.redactor.$editor.on('mouseleave','figure',$.proxy(this.hideToolbar,this))}},getToolbar:function(type){if(this.toolbar[type])
return this.toolbar[type]
var controlGroup=(this.redactor[type]&&this.redactor[type].controlGroup)||this.controlGroup,controls=$.extend({},this.control,(this.redactor[type]&&this.redactor[type].control)||{}),$controls=this.buildControls(controlGroup,controls),$toolbar=$('<div class="oc-figure-controls">').append($controls)
this.toolbar[type]=$toolbar
return $toolbar},buildControls:function(controlGroup,controls){var $controls=$()
$.each(controlGroup,$.proxy(function(index,command){var control
if(typeof command==='string'){control=controls[command]
$controls=$controls.add($('<span>',{'class':'oc-figure-controls-'+control.classSuffix,'text':control.text}).data({command:command,control:control}))}
else if(typeof command==='object'){$.each(command,$.proxy(function(text,commands){var $button=$('<span>').text(' '+text).addClass('oc-figure-controls-table dropdown'),$dropdown=$('<ul class="dropdown-menu open oc-dropdown-menu" />'),container=$('<li class="dropdown-container" />'),list=$('<ul />'),listItem
$dropdown.append(container.append(list))
$button.append($dropdown)
$button.on('mouseover',function(){$dropdown.show()})
$button.on('mouseout',function(){$dropdown.hide()})
$.each(commands,$.proxy(function(index,command){control=controls[command]
if(command==='|'){$('<li class="divider" />').appendTo(list)}
else{listItem=$('<li />')
$('<a />',{text:control.text}).data({command:command,control:control}).appendTo(listItem)
if(index==0)listItem.addClass('first-item')
listItem.appendTo(list)}},this))
$controls=$controls.add($button)},this))}},this))
return $controls},command:function(command,$figure,plugin){$figure.find('.oc-figure-controls').appendTo(this.redactor.$box)
this.redactor.buffer.set(this.redactor.$editor.html())
switch(command){case'up':$figure.prev().before($figure)
break
case'down':$figure.next().after($figure)
break
case'remove':$figure.remove()
break
default:if(plugin&&plugin.command){plugin.command(command,$figure,$(this.current))}
break}
this.redactor.code.sync()},observeKeyboard:function(){var redactor=this.redactor
redactor.$editor.on('keydown',function(event){var currentNode=redactor.selection.getBlock()
if(event.keyCode===8&&!redactor.caret.getOffset(currentNode)&&currentNode.previousSibling&&currentNode.previousSibling.nodeName==='FIGURE'){event.preventDefault()}})}}
window.RedactorPlugins.figure=function(){return{init:function(){this.figure=new Figure(this)}}}}(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')
@ -1835,7 +1893,7 @@ 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: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.plugins=['fullscreen','figure','table','mediamanager']
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}

View File

@ -11,6 +11,7 @@
=require plugin.cleanup.js
=require plugin.fullscreen.js
=require plugin.table.js
=require plugin.figure.js
=require richeditor.js
*/

View File

@ -0,0 +1,307 @@
(function ($) {
'use strict';
window.RedactorPlugins = window.RedactorPlugins || {}
var Figure = function (redactor) {
this.redactor = redactor
this.toolbar = {}
this.init()
}
Figure.prototype = {
control: {
up : { classSuffix: 'arrow-up' },
down : { classSuffix: 'arrow-down' },
'|' : { classSuffix: 'divider' },
remove: { classSuffix: 'delete' }
},
controlGroup: ['up', 'down', 'remove'],
init: function () {
this.observeCaptions()
this.observeToolbars()
this.observeKeyboard()
},
observeCaptions: function () {
/*
* Adding a line-break to empty captions and citations on click will place the cursor in the expected place
*/
this.redactor.$editor.on('click', 'figcaption:empty, cite:empty', $.proxy(function (event) {
$(event.target).prepend('<br />')
this.redactor.caret.setEnd(event.target)
event.stopPropagation()
}, this))
/*
* Remove generated line-breaks empty figcaptions
*/
$(window).on('click', $.proxy(this.cleanCaptions, this))
this.redactor.$editor.on('blur', $.proxy(this.cleanCaptions, this))
this.redactor.$editor.closest('form').one('submit', $.proxy(this.clearCaptions, this))
/*
* Prevent user from removing captions or citations with delete/backspace keys
*/
this.redactor.$editor.on('keydown', $.proxy(function (event) {
var current = this.redactor.selection.getCurrent(),
isEmpty = !current.length,
isCaptionNode = !!$(current).closest('figcaption, cite').length,
isDeleteKey = $.inArray(event.keyCode, [this.redactor.keyCode.BACKSPACE, this.redactor.keyCode.DELETE]) >= 0
if (isEmpty && isDeleteKey && isCaptionNode) {
event.preventDefault()
}
}, this))
},
cleanCaptions: function () {
this.redactor.$editor.find('figcaption, cite').filter(function () {
return $(this).text() == ''
}).empty()
},
clearCaptions: function () {
this.redactor.$editor.find('figcaption, cite').filter(function () {
return $(this).text() == ''
}).remove()
if (this.redactor.opts.visual) {
this.redactor.code.sync()
}
},
showToolbar: function (event) {
var $figure = $(event.currentTarget),
type = $figure.data('type') || 'default',
$toolbar = this.getToolbar(type).data('figure', $figure).prependTo($figure).show()
if (this.redactor[type] && this.redactor[type].onShow) {
this.redactor[type].onShow($figure, $toolbar)
}
},
hideToolbar: function (event) {
$(event.currentTarget).find('.oc-figure-controls').appendTo(this.redactor.$box).hide()
},
observeToolbars: function () {
/*
* Before clicking a command, make sure we save the current node within the editor
*/
this.redactor.$editor.on('mousedown', '.oc-figure-controls', $.proxy(function (event) {
event.preventDefault()
this.current = this.redactor.selection.getCurrent()
}, this))
this.redactor.$editor.on('click', '.oc-figure-controls span, .oc-figure-controls a', $.proxy(function (event) {
event.stopPropagation()
var $target = $(event.currentTarget),
command = $target.data('command'),
$figure = $target.closest('figure'),
plugin = this.redactor[$figure.data('type')]
this.command(command, $figure, plugin)
}, this))
this.redactor.$editor.on('keydown', function () {
$(this).find('figure').triggerHandler('mouseleave')
})
/*
* Mobile
*/
if (this.redactor.utils.isMobile()) {
/*
* If $editor is focused, click doesn't seem to fire
*/
this.redactor.$editor.on('touchstart', 'figure', function (event) {
if (event.target.nodeName !== 'FIGCAPTION' && $(event.target).parents('.oc-figure-controls').length) {
$(this).trigger('click', event)
}
})
this.redactor.$editor.on('click', 'figure', $.proxy(function (event) {
if (event.target.nodeName !== 'FIGCAPTION') {
this.redactor.$editor.trigger('blur')
}
this.showToolbar(event)
}, this))
}
/*
* Desktop
*/
else {
/*
* Move toolbar into figure on mouseenter
*/
this.redactor.$editor.on('mouseenter', 'figure', $.proxy(this.showToolbar, this))
/*
* Remove toolbar from figure on mouseleave
*/
this.redactor.$editor.on('mouseleave', 'figure', $.proxy(this.hideToolbar, this))
}
},
getToolbar: function (type) {
if (this.toolbar[type])
return this.toolbar[type]
var controlGroup = (this.redactor[type] && this.redactor[type].controlGroup) || this.controlGroup,
controls = $.extend({}, this.control, (this.redactor[type] && this.redactor[type].control) || {}),
$controls = this.buildControls(controlGroup, controls),
$toolbar = $('<div class="oc-figure-controls">').append($controls)
this.toolbar[type] = $toolbar
return $toolbar
},
buildControls: function (controlGroup, controls) {
var $controls = $()
$.each(controlGroup, $.proxy(function (index, command) {
var control
/*
* Basic command
*/
if (typeof command === 'string') {
control = controls[command]
$controls = $controls.add($('<span>', {
'class': 'oc-figure-controls-' + control.classSuffix,
'text': control.text
}).data({
command: command,
control: control
}))
}
/*
* Dropdown
*/
else if (typeof command === 'object') {
$.each(command, $.proxy(function (text, commands) {
var $button = $('<span>').text(' ' + text).addClass('oc-figure-controls-table dropdown'),
$dropdown = $('<ul class="dropdown-menu open oc-dropdown-menu" />'),
container = $('<li class="dropdown-container" />'),
list = $('<ul />'),
listItem
$dropdown.append(container.append(list))
$button.append($dropdown)
$button.on('mouseover', function () { $dropdown.show() })
$button.on('mouseout', function () { $dropdown.hide() })
$.each(commands, $.proxy(function (index, command) {
control = controls[command]
if (command === '|') {
$('<li class="divider" />').appendTo(list)
}
else {
listItem = $('<li />')
$('<a />', {
text: control.text
}).data({
command: command,
control: control
}).appendTo(listItem)
if (index == 0) listItem.addClass('first-item')
listItem.appendTo(list)
}
}, this))
$controls = $controls.add($button)
}, this))
}
}, this))
return $controls
},
command: function (command, $figure, plugin) {
/*
* Move the toolbar before carrying out the command so it doesn't break when undoing/redoing
*/
$figure.find('.oc-figure-controls').appendTo(this.redactor.$box)
/*
* Maintain undo history
*/
this.redactor.buffer.set(this.redactor.$editor.html())
/*
* Shared functions
*/
switch (command) {
case 'up':
$figure.prev().before($figure)
break
case 'down':
$figure.next().after($figure)
break
case 'remove':
$figure.remove()
break
default:
if (plugin && plugin.command) {
plugin.command(command, $figure, $(this.current))
}
break
}
this.redactor.code.sync()
},
observeKeyboard: function () {
var redactor = this.redactor
redactor.$editor.on('keydown', function (event) {
/*
* Node at cursor
*/
var currentNode = redactor.selection.getBlock()
/*
* Delete key
*/
if (
event.keyCode === 8
&& !redactor.caret.getOffset(currentNode)
&& currentNode.previousSibling
&& currentNode.previousSibling.nodeName === 'FIGURE'
) {
event.preventDefault()
}
})
}
}
window.RedactorPlugins.figure = function() {
return {
init: function () {
this.figure = new Figure(this)
}
}
}
}(jQuery));

View File

@ -82,7 +82,7 @@
redactorOptions.fullpage = true
}
redactorOptions.plugins = ['fullscreen', 'table', 'mediamanager']
redactorOptions.plugins = ['fullscreen', 'figure', 'table', 'mediamanager']
redactorOptions.buttons = ['formatting', 'bold', 'italic', 'unorderedlist', 'orderedlist', 'link', 'horizontalrule', 'html'],
this.$textarea.redactor(redactorOptions)

View File

@ -0,0 +1,315 @@
//
// Figures
//
.redactor-editor {
figure {
position: relative;
}
figcaption {
text-align: center;
line-height: @line-height-computed;
font-size: @font-size-base;
}
figure[data-type=table] {
clear: both;
}
figure[data-type=video] {
position: relative;
margin-bottom: @line-height-computed;
text-align: center;
clear: both;
p {
margin: 0;
}
&.oc-figure-full {
&:before {
position: relative;
padding-bottom: 51%;
width: 100%;
height: 0;
content: "";
display: block;
}
iframe {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
}
}
figure[data-type=image] {
position: relative;
margin-bottom: @line-height-computed;
.oc-figure-controls {
top: 0px;
}
img {
width: 100%;
}
&.oc-figure-large {
width: 100%;
clear: both;
}
&.oc-figure-medium {
width: 50%;
}
&.oc-figure-small {
width: 33%;
}
}
figure[data-type=quote] {
font-family: Georgia, serif;
margin-bottom: @line-height-computed;
margin-left: @line-height-computed;
font-style: italic;
position: relative;
border-left: solid 4px @color-border;
padding-left: @line-height-computed;
figcaption {
font-weight: bold;
text-align: left;
}
.oc-figure-controls {
margin-left: -5px;
}
&.oc-figure-medium {
&, blockquote { font-size: 20px; }
}
&.oc-figure-large {
&, blockquote { font-size: 24px; }
}
&.oc-figure-right {
width: 33%;
}
&.oc-figure-left {
width: 33%;
border-left: none;
border-right: solid 4px @color-border;
padding-left: 0;
padding-right: @line-height-computed;
margin-left: 0;
margin-right: @line-height-computed;
.oc-figure-controls {
margin-left: 0;
margin-right: -5px;
}
}
cite {
display: block;
text-align: left;
font-weight: normal;
&:before {
content: "\2014\00a0";
}
&:empty:before {
opacity: 0.4;
// @todo Convert to language value
content: "\2014 Type to add citation (optional)";
}
}
}
}
.redactor-box {
figcaption {
&:empty:before {
opacity: .4;
// @todo Convert to language value
content: "Type to add caption (optional)";
}
}
.oc-figure-controls {
background: #2b3e50 !important;
padding: 0;
.border-radius(4px);
position: absolute;
display: none;
// min-width: 100%;
white-space: nowrap;
left: 10px;
top: -50px;
margin: 0 auto;
font-family: @font-family-base;
line-height: @line-height-computed;
font-style: normal;
z-index: @richeditor-zindex + 500;
text-align: center;
&:after {
content: ' ';
left: 0;
position: absolute;
display: block;
height: 10px;
background: rgba(0,0,0,0.1);
width: 100%;
bottom: -10px;
.opacity(0.1);
}
&:before {
.triangle(down, 17px, 9px, #2b3e50);
position: absolute;
left: 14px;
bottom: -8px;
}
}
figure:hover .oc-figure-controls {
display: block;
}
.oc-figure-controls.bottom {
bottom: -50px;
top: auto;
&:after {
bottom: auto;
top: -10px;
}
&:before {
.triangle(up, 17px, 9px, #2b3e50)!important;
border-top: none;
top: -8px;
}
}
.oc-figure-controls span {
display: inline-block;
border: none;
background: none;
color: #ffffff;
vertical-align: top;
font-size: 14px;
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
cursor: pointer;
&:before {
line-height: 24px;
}
&:hover {
color: #fff;
background: rgba(255, 255, 255, 0.1);
}
&.on {
background: #fff;
color: @gray;
background-color: #1f9e84;
color: white;
}
&:last-child {
.border-right-radius(4px);
}
&:first-child {
.border-right-radius(4px);
}
}
.oc-figure-controls span {
&.oc-figure-controls-divider {
width: 1px;
background: @color-border;
padding: 0;
margin: 0 4px;
cursor: normal;
}
&.oc-figure-controls-small {
font-size: 7px;
}
&.oc-figure-controls-medium {
font-size: 10px;
}
&.oc-figure-controls-arrow-left:before {
.icon(@arrow-left);
}
&.oc-figure-controls-arrow-right:before {
.icon(@arrow-right);
}
&.oc-figure-controls-arrow-up:before {
.icon(@arrow-up);
}
&.oc-figure-controls-arrow-down:before {
.icon(@arrow-down);
}
&.oc-figure-controls-resize-full:before {
.icon(@expand);
}
&.oc-figure-controls-resize-small:before {
.icon(@compress);
}
&.oc-figure-controls-delete {
margin-left: @line-height-computed;
&:before { .icon(@trash-o); }
&:hover { background: @color-btn-danger; }
}
&.oc-figure-controls-table {
width: auto;
padding-left: @line-height-computed / 2;
padding-right: @line-height-computed / 2;
text-align: left;
&:before { .icon(@table); }
}
}
.oc-figure-right {
float: right;
margin-left: @line-height-computed;
.oc-figure-controls {
right: 0;
}
}
.oc-figure-left {
float: left;
margin-right: @line-height-computed;
}
.oc-dropdown-menu {
&, ul { padding: 0 !important; }
ul {
background-color: @dropdown-bg !important;
}
a {
text-decoration: none;
padding: 0 15px !important;
color: @dropdown-link-color !important;
text-decoration: none !important;
line-height: 25px;
&:hover,
&:focus {
color: @color-dropdown-hover-text !important;
}
}
}
@media (max-width: @menu-breakpoint-max) {
figure[data-type=image] {
width: 100% !important;
float: none !important;
margin-left: 0;
margin-right: 0;
}
figure[data-type=video] iframe {
width: 100% !important;
height: auto !important;
}
}
}

View File

@ -4,6 +4,7 @@
@richeditor-zindex: 600;
@richeditor-gutter: 20px;
@import "_figures.less";
@import "../vendor/redactor/redactor.less";
.field-flush .field-richeditor {