Initial switch from Redactor to Froala
This commit is contained in:
parent
3eb47e679b
commit
4789616190
|
|
@ -119,10 +119,13 @@ class RichEditor extends FormWidgetBase
|
|||
{
|
||||
$this->addCss('css/richeditor.css', 'core');
|
||||
$this->addJs('js/build-min.js', 'core');
|
||||
$this->addJs('/modules/backend/formwidgets/codeeditor/assets/js/build-min.js', 'core');
|
||||
|
||||
if ($lang = $this->getValidEditorLang()) {
|
||||
$this->addJs('vendor/redactor/lang/'.$lang.'.js', 'core');
|
||||
}
|
||||
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// if ($lang = $this->getValidEditorLang()) {
|
||||
// $this->addJs('vendor/redactor/lang/'.$lang.'.js', 'core');
|
||||
// }
|
||||
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
There's a hack in plugin.figure.js script. Added method destory(). The figure plugin keeps a reference to the editor and toolba which can't be cleaned up after the Editor is destroyed. Also, the figure plugin binds event handlers that are never unbound. --ab Apr 08 2015
|
||||
|
|
@ -7,11 +7,10 @@
|
|||
* @see build-min.js
|
||||
*
|
||||
|
||||
=require ../vendor/redactor/redactor.js
|
||||
=require plugin.fullscreen.js
|
||||
=require plugin.figure.js
|
||||
=require plugin.table.js
|
||||
=require plugin.pagelinks.js
|
||||
=require ../vendor/froala/js/froala_editor.min.js
|
||||
=require ../vendor/froala/js/plugins/fullscreen.min.js
|
||||
=require ../vendor/froala/js/plugins/code_beautifier.min.js
|
||||
=require ../vendor/froala_drm/js/plugins/code_view.js
|
||||
=require richeditor.js
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
if (!RedactorPlugins) var RedactorPlugins = {};
|
||||
|
||||
(function($) {
|
||||
|
||||
RedactorPlugins.definedlinks = function() {
|
||||
return {
|
||||
init: function() {
|
||||
if (!this.opts.definedLinks) return;
|
||||
|
||||
this.modal.addCallback('link', $.proxy(this.definedlinks.load, this));
|
||||
|
||||
},
|
||||
load: function() {
|
||||
var $select = $('<select id="redactor-defined-links" />');
|
||||
$('#redactor-modal-link-insert').prepend($select);
|
||||
|
||||
this.definedlinks.storage = {};
|
||||
|
||||
$.getJSON(this.opts.definedLinks, $.proxy(function(data) {
|
||||
$.each(data, $.proxy(function(key, val) {
|
||||
this.definedlinks.storage[key] = val;
|
||||
$select.append($('<option>').val(key).html(val.name));
|
||||
|
||||
}, this));
|
||||
|
||||
$select.on('change', $.proxy(this.definedlinks.select, this));
|
||||
|
||||
}, this));
|
||||
|
||||
},
|
||||
select: function(e) {
|
||||
var key = $(e.target).val();
|
||||
var name = '', url = '';
|
||||
if (key !== 0)
|
||||
{
|
||||
name = this.definedlinks.storage[key].name;
|
||||
url = this.definedlinks.storage[key].url;
|
||||
}
|
||||
|
||||
$('#redactor-link-url').val(url);
|
||||
|
||||
var $el = $('#redactor-link-url-text');
|
||||
if ($el.val() === '') $el.val(name);
|
||||
}
|
||||
};
|
||||
};
|
||||
})(jQuery);
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
(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.observeToolbars()
|
||||
this.observeKeyboard()
|
||||
},
|
||||
|
||||
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.figure', '.oc-figure-controls', $.proxy(function (event) {
|
||||
event.preventDefault()
|
||||
this.current = this.redactor.selection.getCurrent()
|
||||
}, this))
|
||||
|
||||
this.redactor.$editor.on('click.figure', '.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.figure', 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', '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', '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', 'figure', $.proxy(this.showToolbar, this))
|
||||
|
||||
/*
|
||||
* Remove toolbar from figure on mouseleave
|
||||
*/
|
||||
this.redactor.$editor.on('mouseleave.figure', '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.figure', function () { $dropdown.show() })
|
||||
$button.on('mouseout.figure', 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.figure', 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()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.redactor.$editor.off('.figure')
|
||||
|
||||
for (var type in this.toolbar) {
|
||||
this.toolbar[type].find('span').off('.figure')
|
||||
}
|
||||
|
||||
this.redactor = null
|
||||
this.toolbar = null
|
||||
}
|
||||
}
|
||||
|
||||
window.RedactorPlugins.figure = function() {
|
||||
return {
|
||||
init: function () {
|
||||
this.figure = new Figure(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}(jQuery));
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
window.RedactorPlugins = window.RedactorPlugins || {};
|
||||
|
||||
window.RedactorPlugins.fullscreen = function() {
|
||||
return {
|
||||
|
||||
init: function() {
|
||||
this.fullscreen.isOpen = false
|
||||
|
||||
var button = this.button.add('fullscreen', 'FullScreen')
|
||||
this.button.addCallback(button, $.proxy(this.fullscreen.toggle, this))
|
||||
button.addClass('redactor_btn_fullscreen').removeClass('redactor-btn-image')
|
||||
button.parent().addClass('redactor-btn-right')
|
||||
|
||||
if (this.opts.fullscreen)
|
||||
this.fullscreen.toggle()
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (!this.fullscreen.isOpen)
|
||||
this.fullscreen.enable()
|
||||
else
|
||||
this.fullscreen.disable()
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this.button.changeIcon('fullscreen', 'normalscreen')
|
||||
this.button.setActive('fullscreen')
|
||||
this.fullscreen.isOpen = true
|
||||
|
||||
if (this.opts.toolbarExternal) {
|
||||
this.fullscreen.toolcss = {}
|
||||
this.fullscreen.boxcss = {}
|
||||
this.fullscreen.toolcss.width = this.$toolbar.css('width')
|
||||
this.fullscreen.toolcss.top = this.$toolbar.css('top')
|
||||
this.fullscreen.toolcss.position = this.$toolbar.css('position')
|
||||
this.fullscreen.boxcss.top = this.$box.css('top')
|
||||
}
|
||||
|
||||
this.fullscreen.height = this.$editor.height()
|
||||
|
||||
if (this.opts.maxHeight) this.$editor.css('max-height', '')
|
||||
if (this.opts.minHeight) this.$editor.css('min-height', '')
|
||||
|
||||
if (!this.$fullscreenPlaceholder) this.$fullscreenPlaceholder = $('<div/>')
|
||||
this.$fullscreenPlaceholder.insertAfter(this.$box)
|
||||
|
||||
this.$box.appendTo(document.body)
|
||||
|
||||
this.$box.addClass('redactor-box-fullscreen')
|
||||
$('body, html').css('overflow', 'hidden')
|
||||
|
||||
this.fullscreen.resize()
|
||||
$(window).on('resize.redactor.fullscreen', $.proxy(this.fullscreen.resize, this))
|
||||
$(document).scrollTop(0, 0)
|
||||
|
||||
this.$editor.focus()
|
||||
this.observe.load()
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this.button.removeIcon('fullscreen', 'normalscreen')
|
||||
this.button.setInactive('fullscreen')
|
||||
this.fullscreen.isOpen = false
|
||||
|
||||
$(window).off('resize.redactor.fullscreen')
|
||||
$('body, html').css('overflow', '')
|
||||
|
||||
this.$box.insertBefore(this.$fullscreenPlaceholder)
|
||||
this.$fullscreenPlaceholder.remove()
|
||||
|
||||
this.$box.removeClass('redactor-box-fullscreen').css({ width: 'auto', height: 'auto' })
|
||||
|
||||
this.code.sync()
|
||||
|
||||
if (this.opts.toolbarExternal) {
|
||||
this.$box.css('top', this.fullscreen.boxcss.top)
|
||||
this.$toolbar.css({
|
||||
'width': this.fullscreen.toolcss.width,
|
||||
'top': this.fullscreen.toolcss.top,
|
||||
'position': this.fullscreen.toolcss.position
|
||||
})
|
||||
}
|
||||
|
||||
if (this.opts.minHeight) this.$editor.css('minHeight', this.opts.minHeight)
|
||||
if (this.opts.maxHeight) this.$editor.css('maxHeight', this.opts.maxHeight)
|
||||
|
||||
this.$editor.css('height', 'auto')
|
||||
this.$editor.focus()
|
||||
this.observe.load()
|
||||
},
|
||||
|
||||
resize: function() {
|
||||
if (!this.fullscreen.isOpen)
|
||||
return false
|
||||
|
||||
var pad = this.$editor.css('padding-top').replace('px', '')
|
||||
|
||||
var toolbarHeight = this.$toolbar.height(),
|
||||
height = $(window).height() - toolbarHeight
|
||||
|
||||
this.$box.width($(window).width() - 2).height(height + toolbarHeight)
|
||||
|
||||
if (this.opts.toolbarExternal) {
|
||||
this.$toolbar.css({
|
||||
top: '0px',
|
||||
position: 'absolute',
|
||||
width: '100%'
|
||||
})
|
||||
|
||||
this.$box.css('top', toolbarHeight + 'px')
|
||||
}
|
||||
|
||||
// if (!this.opts.iframe) {
|
||||
// this.$editor.height(height - (pad * 2))
|
||||
// }
|
||||
// else {
|
||||
// setTimeout($.proxy(function() {
|
||||
// this.$frame.height(height)
|
||||
// }, this), 1)
|
||||
// }
|
||||
|
||||
// this.$editor.height(height)
|
||||
}
|
||||
}
|
||||
}
|
||||
}(jQuery));
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
if (!RedactorPlugins) var RedactorPlugins = {};
|
||||
|
||||
(function($)
|
||||
{
|
||||
RedactorPlugins.pagelinks = function()
|
||||
{
|
||||
return {
|
||||
init: function()
|
||||
{
|
||||
if (!this.opts.pageLinksHandler) return
|
||||
|
||||
this.modal.addCallback('link', $.proxy(this.pagelinks.load, this))
|
||||
},
|
||||
load: function()
|
||||
{
|
||||
var $select = $('<select id="redactor-page-links" />')
|
||||
$('#redactor-modal-link-insert').prepend($select)
|
||||
|
||||
this.pagelinks.storage = {};
|
||||
|
||||
this.$editor.request(this.opts.pageLinksHandler, {
|
||||
success: $.proxy(function(data) {
|
||||
|
||||
$.each(data.links, $.proxy(function(key, val) {
|
||||
this.pagelinks.storage[key] = val
|
||||
$select.append($('<option>').val(key).html(val.name))
|
||||
}, this))
|
||||
|
||||
$select.on('change', $.proxy(this.pagelinks.select, this))
|
||||
|
||||
}, this)
|
||||
})
|
||||
},
|
||||
select: function(e)
|
||||
{
|
||||
var key = $(e.target).val()
|
||||
var name = '', url = ''
|
||||
if (key !== 0) {
|
||||
name = this.pagelinks.storage[key].name
|
||||
url = this.pagelinks.storage[key].url
|
||||
}
|
||||
|
||||
$('#redactor-link-url').val(url)
|
||||
|
||||
var $el = $('#redactor-link-url-text')
|
||||
if ($el.val() === '') {
|
||||
$el.val($.trim($('<span />').html(name).text()))
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})(jQuery);
|
||||
|
|
@ -1,340 +0,0 @@
|
|||
if (!RedactorPlugins) var RedactorPlugins = {};
|
||||
|
||||
(function($)
|
||||
{
|
||||
RedactorPlugins.table = function()
|
||||
{
|
||||
return {
|
||||
getTemplate: function()
|
||||
{
|
||||
return String()
|
||||
+ '<section id="redactor-modal-table-insert">'
|
||||
+ '<label>' + this.lang.get('rows') + '</label>'
|
||||
+ '<input type="text" size="5" value="2" id="redactor-table-rows" />'
|
||||
+ '<label>' + this.lang.get('columns') + '</label>'
|
||||
+ '<input type="text" size="5" value="3" id="redactor-table-columns" />'
|
||||
+ '</section>';
|
||||
},
|
||||
init: function()
|
||||
{
|
||||
|
||||
var dropdown = {};
|
||||
|
||||
dropdown.insert_table = { title: this.lang.get('insert_table'), func: this.table.show };
|
||||
dropdown.insert_row_above = { title: this.lang.get('insert_row_above'), func: this.table.addRowAbove };
|
||||
dropdown.insert_row_below = { title: this.lang.get('insert_row_below'), func: this.table.addRowBelow };
|
||||
dropdown.insert_column_left = { title: this.lang.get('insert_column_left'), func: this.table.addColumnLeft };
|
||||
dropdown.insert_column_right = { title: this.lang.get('insert_column_right'), func: this.table.addColumnRight };
|
||||
dropdown.add_head = { title: this.lang.get('add_head'), func: this.table.addHead };
|
||||
dropdown.delete_head = { title: this.lang.get('delete_head'), func: this.table.deleteHead };
|
||||
dropdown.delete_column = { title: this.lang.get('delete_column'), func: this.table.deleteColumn };
|
||||
dropdown.delete_row = { title: this.lang.get('delete_row'), func: this.table.deleteRow };
|
||||
dropdown.delete_table = { title: this.lang.get('delete_table'), func: this.table.deleteTable };
|
||||
|
||||
this.observe.addButton('td', 'table');
|
||||
this.observe.addButton('th', 'table');
|
||||
|
||||
var button = this.button.addBefore('link', 'table', this.lang.get('table'));
|
||||
this.button.addDropdown(button, dropdown);
|
||||
|
||||
button.addClass('redactor_btn_table').removeClass('redactor-btn-image')
|
||||
},
|
||||
show: function()
|
||||
{
|
||||
this.modal.addTemplate('table', this.table.getTemplate());
|
||||
|
||||
this.modal.load('table', this.lang.get('insert_table'), 300);
|
||||
this.modal.createCancelButton();
|
||||
|
||||
var button = this.modal.createActionButton(this.lang.get('insert'));
|
||||
button.on('click', this.table.insert);
|
||||
|
||||
this.selection.save();
|
||||
this.modal.show();
|
||||
|
||||
$('#redactor-table-rows').focus();
|
||||
|
||||
},
|
||||
insert: function()
|
||||
{
|
||||
this.placeholder.remove();
|
||||
this.clean.cleanEmptyParagraph();
|
||||
|
||||
var rows = $('#redactor-table-rows').val(),
|
||||
columns = $('#redactor-table-columns').val(),
|
||||
$tableBox = $('<div>'),
|
||||
tableId = Math.floor(Math.random() * 99999),
|
||||
$table = $('<table id="table' + tableId + '"><tbody></tbody></table>'),
|
||||
i, $row, z, $column;
|
||||
|
||||
for (i = 0; i < rows; i++)
|
||||
{
|
||||
$row = $('<tr>');
|
||||
|
||||
for (z = 0; z < columns; z++)
|
||||
{
|
||||
$column = $('<td>' + this.opts.invisibleSpace + '</td>');
|
||||
|
||||
// set the focus to the first td
|
||||
if (i === 0 && z === 0)
|
||||
{
|
||||
$column.append(this.selection.getMarker());
|
||||
}
|
||||
|
||||
$($row).append($column);
|
||||
}
|
||||
|
||||
$table.append($row);
|
||||
}
|
||||
|
||||
$tableBox.append($table);
|
||||
var html = $tableBox.html();
|
||||
|
||||
this.modal.close();
|
||||
this.selection.restore();
|
||||
|
||||
if (this.table.getTable()) return;
|
||||
|
||||
this.buffer.set();
|
||||
|
||||
var current = this.selection.getBlock() || this.selection.getCurrent();
|
||||
if (current && current.tagName != 'BODY')
|
||||
{
|
||||
if (current.tagName == 'LI') current = $(current).closest('ul, ol');
|
||||
$(current).after(html);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.insert.html(html, false);
|
||||
}
|
||||
|
||||
this.selection.restore();
|
||||
|
||||
var table = this.$editor.find('#table' + tableId);
|
||||
|
||||
if (!this.opts.linebreaks && (this.utils.browser('mozilla') || this.utils.browser('msie')))
|
||||
{
|
||||
var $next = table.next();
|
||||
if ($next.length === 0)
|
||||
{
|
||||
table.after(this.opts.emptyHtml);
|
||||
}
|
||||
}
|
||||
|
||||
this.observe.buttons();
|
||||
|
||||
table.find('span.redactor-selection-marker').remove();
|
||||
table.removeAttr('id');
|
||||
|
||||
this.code.sync();
|
||||
this.core.setCallback('insertedTable', table);
|
||||
},
|
||||
getTable: function()
|
||||
{
|
||||
var $table = $(this.selection.getParent()).closest('table');
|
||||
|
||||
if (!this.utils.isRedactorParent($table)) return false;
|
||||
if ($table.size() === 0) return false;
|
||||
|
||||
return $table;
|
||||
},
|
||||
restoreAfterDelete: function($table)
|
||||
{
|
||||
this.selection.restore();
|
||||
$table.find('span.redactor-selection-marker').remove();
|
||||
this.code.sync();
|
||||
},
|
||||
deleteTable: function()
|
||||
{
|
||||
var $table = this.table.getTable();
|
||||
if (!$table) return;
|
||||
|
||||
this.buffer.set();
|
||||
|
||||
|
||||
var $next = $table.next();
|
||||
if (!this.opts.linebreaks && $next.length !== 0)
|
||||
{
|
||||
this.caret.setStart($next);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.caret.setAfter($table);
|
||||
}
|
||||
|
||||
|
||||
$table.remove();
|
||||
|
||||
this.code.sync();
|
||||
},
|
||||
deleteRow: function()
|
||||
{
|
||||
var $table = this.table.getTable();
|
||||
if (!$table) return;
|
||||
|
||||
var $current = $(this.selection.getCurrent());
|
||||
|
||||
this.buffer.set();
|
||||
|
||||
var $current_tr = $current.closest('tr');
|
||||
var $focus_tr = $current_tr.prev().length ? $current_tr.prev() : $current_tr.next();
|
||||
if ($focus_tr.length)
|
||||
{
|
||||
var $focus_td = $focus_tr.children('td, th').first();
|
||||
if ($focus_td.length) $focus_td.prepend(this.selection.getMarker());
|
||||
}
|
||||
|
||||
$current_tr.remove();
|
||||
this.table.restoreAfterDelete($table);
|
||||
},
|
||||
deleteColumn: function()
|
||||
{
|
||||
var $table = this.table.getTable();
|
||||
if (!$table) return;
|
||||
|
||||
this.buffer.set();
|
||||
|
||||
var $current = $(this.selection.getCurrent());
|
||||
var $current_td = $current.closest('td, th');
|
||||
var index = $current_td[0].cellIndex;
|
||||
|
||||
$table.find('tr').each($.proxy(function(i, elem)
|
||||
{
|
||||
var $elem = $(elem);
|
||||
var focusIndex = index - 1 < 0 ? index + 1 : index - 1;
|
||||
if (i === 0) $elem.find('td, th').eq(focusIndex).prepend(this.selection.getMarker());
|
||||
|
||||
$elem.find('td, th').eq(index).remove();
|
||||
|
||||
}, this));
|
||||
|
||||
this.table.restoreAfterDelete($table);
|
||||
},
|
||||
addHead: function()
|
||||
{
|
||||
var $table = this.table.getTable();
|
||||
if (!$table) return;
|
||||
|
||||
this.buffer.set();
|
||||
|
||||
if ($table.find('thead').size() !== 0)
|
||||
{
|
||||
this.table.deleteHead();
|
||||
return;
|
||||
}
|
||||
|
||||
var tr = $table.find('tr').first().clone();
|
||||
tr.find('td').replaceWith($.proxy(function()
|
||||
{
|
||||
return $('<th>').html(this.opts.invisibleSpace);
|
||||
}, this));
|
||||
|
||||
$thead = $('<thead></thead>').append(tr);
|
||||
$table.prepend($thead);
|
||||
|
||||
this.code.sync();
|
||||
|
||||
},
|
||||
deleteHead: function()
|
||||
{
|
||||
var $table = this.table.getTable();
|
||||
if (!$table) return;
|
||||
|
||||
var $thead = $table.find('thead');
|
||||
if ($thead.size() === 0) return;
|
||||
|
||||
this.buffer.set();
|
||||
|
||||
$thead.remove();
|
||||
this.code.sync();
|
||||
},
|
||||
addRowAbove: function()
|
||||
{
|
||||
this.table.addRow('before');
|
||||
},
|
||||
addRowBelow: function()
|
||||
{
|
||||
this.table.addRow('after');
|
||||
},
|
||||
addColumnLeft: function()
|
||||
{
|
||||
this.table.addColumn('before');
|
||||
},
|
||||
addColumnRight: function()
|
||||
{
|
||||
this.table.addColumn('after');
|
||||
},
|
||||
addRow: function(type)
|
||||
{
|
||||
var $table = this.table.getTable();
|
||||
if (!$table) return;
|
||||
|
||||
this.buffer.set();
|
||||
|
||||
var $current = $(this.selection.getCurrent());
|
||||
var $current_tr = $current.closest('tr');
|
||||
var new_tr = $current_tr.clone();
|
||||
|
||||
new_tr.find('th').replaceWith(function()
|
||||
{
|
||||
var $td = $('<td>');
|
||||
$td[0].attributes = this.attributes;
|
||||
|
||||
return $td.append($(this).contents());
|
||||
});
|
||||
|
||||
new_tr.find('td').html(this.opts.invisibleSpace);
|
||||
|
||||
if (type == 'after')
|
||||
{
|
||||
$current_tr.after(new_tr);
|
||||
}
|
||||
else
|
||||
{
|
||||
$current_tr.before(new_tr);
|
||||
}
|
||||
|
||||
this.code.sync();
|
||||
},
|
||||
addColumn: function (type)
|
||||
{
|
||||
var $table = this.table.getTable();
|
||||
if (!$table) return;
|
||||
|
||||
var index = 0;
|
||||
var current = $(this.selection.getCurrent());
|
||||
|
||||
this.buffer.set();
|
||||
|
||||
var $current_tr = current.closest('tr');
|
||||
var $current_td = current.closest('td, th');
|
||||
|
||||
$current_tr.find('td, th').each($.proxy(function(i, elem)
|
||||
{
|
||||
if ($(elem)[0] === $current_td[0]) index = i;
|
||||
|
||||
}, this));
|
||||
|
||||
$table.find('tr').each($.proxy(function(i, elem)
|
||||
{
|
||||
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);
|
||||
|
|
@ -22,7 +22,6 @@
|
|||
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
|
||||
|
||||
|
|
@ -37,7 +36,6 @@
|
|||
RichEditor.prototype.constructor = RichEditor
|
||||
|
||||
RichEditor.DEFAULTS = {
|
||||
dataLocker: null,
|
||||
linksHandler: null,
|
||||
stylesheet: null,
|
||||
fullpage: false,
|
||||
|
|
@ -49,15 +47,6 @@
|
|||
|
||||
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.
|
||||
*/
|
||||
if (this.options.dataLocker) {
|
||||
this.$dataLocker = $(this.options.dataLocker)
|
||||
this.$textarea.val(this.$dataLocker.val())
|
||||
}
|
||||
|
||||
/*
|
||||
* Textarea must have an identifier
|
||||
*/
|
||||
|
|
@ -68,52 +57,66 @@
|
|||
/*
|
||||
* Initialize Redactor editor
|
||||
*/
|
||||
var redactorOptions = {
|
||||
lang: this.options.editorLang,
|
||||
imageEditable: true,
|
||||
imageResizable: true,
|
||||
buttonSource: true,
|
||||
removeDataAttr: false,
|
||||
toolbarFixed: false,
|
||||
visualCallback: this.proxy(this.onVisualMode),
|
||||
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),
|
||||
pageLinksHandler: this.options.linksHandler,
|
||||
initCallback: function() { self.build(this) }
|
||||
var froalaOptions = {
|
||||
editorClass: 'control-richeditor',
|
||||
height: Infinity // Height set via CSS, enable the scrollbars
|
||||
}
|
||||
|
||||
if (this.options.fullpage) {
|
||||
redactorOptions.fullpage = true
|
||||
froalaOptions.toolbarButtons = [
|
||||
'fullscreen',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'subscript',
|
||||
'superscript',
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'color',
|
||||
'emoticons',
|
||||
'inlineStyle',
|
||||
'paragraphStyle',
|
||||
'paragraphFormat',
|
||||
'align',
|
||||
'formatOL',
|
||||
'formatUL',
|
||||
'outdent',
|
||||
'indent',
|
||||
'quote',
|
||||
'insertHR',
|
||||
'insertLink',
|
||||
'insertImage',
|
||||
'insertVideo',
|
||||
'insertFile',
|
||||
'insertTable',
|
||||
'undo',
|
||||
'redo',
|
||||
'clearFormatting',
|
||||
'selectAll',
|
||||
'html'
|
||||
]
|
||||
|
||||
froalaOptions.toolbarButtonsMD = froalaOptions.toolbarButtons
|
||||
froalaOptions.toolbarButtonsSM = froalaOptions.toolbarButtons
|
||||
froalaOptions.toolbarButtonsXS = froalaOptions.toolbarButtons
|
||||
froalaOptions.htmlAllowedEmptyTags = ['figure', 'textarea', 'a', 'iframe', 'object', 'video', 'style', 'script']
|
||||
froalaOptions.htmlDoNotWrapTags = ['figure', 'script', 'style']
|
||||
|
||||
$.FroalaEditor.ICON_TEMPLATES = {
|
||||
font_awesome: '<i class="icon-[NAME]"></i>',
|
||||
text: '<span style="text-align: center;">[NAME]</span>',
|
||||
image: '<img src=[SRC] alt=[ALT] />'
|
||||
}
|
||||
|
||||
redactorOptions.plugins = ['fullscreen', 'figure', 'table', 'pagelinks', 'mediamanager']
|
||||
redactorOptions.buttons = ['html', 'formatting', 'bold', 'italic', 'alignment', 'unorderedlist', 'orderedlist', 'link', 'horizontalrule'],
|
||||
this.$textarea.on('froalaEditor.initialized', this.proxy(this.build))
|
||||
|
||||
this.$textarea.redactor(redactorOptions)
|
||||
|
||||
this.redactor = this.$textarea.redactor('core.getObject')
|
||||
this.$editor = this.redactor.$editor
|
||||
this.$textarea.froalaEditor(froalaOptions)
|
||||
}
|
||||
|
||||
RichEditor.prototype.dispose = function() {
|
||||
this.unregisterHandlers()
|
||||
|
||||
// Release clickedElement reference inside redactor.js
|
||||
$(document).trigger('mousedown')
|
||||
|
||||
this.redactor.core.destroy()
|
||||
|
||||
// The figure plugin keeps references to the editor,
|
||||
// DOM elements and event handlers. It was hacked and
|
||||
// extended with the destroy() method.
|
||||
if (this.redactor.figure) {
|
||||
this.redactor.figure.destroy()
|
||||
this.redactor.figure = null
|
||||
}
|
||||
this.$textarea.froalaEditor('destroy')
|
||||
|
||||
this.$el.removeData('oc.richEditor')
|
||||
|
||||
|
|
@ -121,14 +124,8 @@
|
|||
this.$el = null
|
||||
this.$textarea = null
|
||||
this.$form = null
|
||||
this.$dataLocker = null
|
||||
this.$editor = null
|
||||
|
||||
this.redactor.$textarea = null
|
||||
this.redactor.$element = null
|
||||
|
||||
this.redactor = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +135,7 @@
|
|||
this.$el.off('dispose-control', this.proxy(this.dispose))
|
||||
}
|
||||
|
||||
RichEditor.prototype.build = function(redactor) {
|
||||
RichEditor.prototype.build = function(event, editor) {
|
||||
this.updateLayout()
|
||||
|
||||
$(window).on('resize', this.proxy(this.updateLayout))
|
||||
|
|
@ -148,12 +145,12 @@
|
|||
|
||||
this.initUiBlocks()
|
||||
|
||||
var self = this
|
||||
redactor.default = {
|
||||
onShow: function($figure, $toolbar) {
|
||||
self.onShowFigureToolbar($figure, $toolbar)
|
||||
}
|
||||
}
|
||||
// var self = this
|
||||
// redactor.default = {
|
||||
// onShow: function($figure, $toolbar) {
|
||||
// self.onShowFigureToolbar($figure, $toolbar)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
RichEditor.prototype.getElement = function() {
|
||||
|
|
@ -175,19 +172,24 @@
|
|||
}
|
||||
|
||||
RichEditor.prototype.updateLayout = function() {
|
||||
var $editor = $('.redactor-editor', this.$el),
|
||||
$codeEditor = $('textarea', this.$el),
|
||||
$toolbar = $('.redactor-toolbar', this.$el)
|
||||
var $editor = $('.fr-wrapper', this.$el),
|
||||
$codeEditor = $('.fr-code', this.$el),
|
||||
$toolbar = $('.fr-toolbar', this.$el),
|
||||
$box = $('.fr-box', this.$el)
|
||||
|
||||
if (!$editor.length) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.$el.hasClass('stretch')) {
|
||||
if (this.$el.hasClass('stretch') && !$box.hasClass('fr-fullscreen')) {
|
||||
var height = $toolbar.outerHeight(true)
|
||||
$editor.css('top', height+1)
|
||||
$codeEditor.css('top', height)
|
||||
}
|
||||
else {
|
||||
$editor.css('top', '')
|
||||
$codeEditor.css('top', '')
|
||||
}
|
||||
}
|
||||
|
||||
RichEditor.prototype.sanityCheckContent = function() {
|
||||
|
|
@ -289,7 +291,7 @@
|
|||
}
|
||||
|
||||
RichEditor.prototype.initUiBlocks = function() {
|
||||
$('.redactor-editor [data-video], .redactor-editor [data-audio]', this.$el).each(function() {
|
||||
$('.fr-wrapper [data-video], .fr-wrapper [data-audio]', this.$el).each(function() {
|
||||
$(this).attr({
|
||||
'data-ui-block': true,
|
||||
'tabindex': '0'
|
||||
|
|
@ -436,10 +438,6 @@
|
|||
this.sanityCheckContent()
|
||||
this.$editor.trigger('mutate')
|
||||
this.$form.trigger('change')
|
||||
|
||||
if (this.$dataLocker) {
|
||||
this.$dataLocker.val(this.syncBefore(this.$editor.html()))
|
||||
}
|
||||
}
|
||||
|
||||
// RICHEDITOR PLUGIN DEFINITION
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@
|
|||
@color-richeditor-toolbar-btn-bg-active: #404040;
|
||||
@color-richeditor-toolbar-btn-color-hover: #ffffff;
|
||||
|
||||
@import "_figures.less";
|
||||
@import "../vendor/redactor/redactor.less";
|
||||
@import "../vendor/froala_drm/less/plugins/code_view.less";
|
||||
@import "../vendor/froala/css/froala_editor.min.css";
|
||||
@import "../vendor/froala/css/froala_style.min.css";
|
||||
@import "../vendor/froala/css/plugins/fullscreen.css";
|
||||
|
||||
.field-flush .field-richeditor {
|
||||
&, &.editor-focus {
|
||||
|
|
@ -19,107 +21,36 @@
|
|||
}
|
||||
}
|
||||
|
||||
.richeditor-set-height(@size) {
|
||||
.fr-wrapper {
|
||||
height: @size;
|
||||
.fr-view {
|
||||
min-height: @size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field-richeditor {
|
||||
border: 1px solid @color-form-field-border;
|
||||
.transition(@input-transition);
|
||||
|
||||
&, .redactor-box {
|
||||
.border-radius(5px);
|
||||
}
|
||||
.redactor-toolbar {
|
||||
.border-top-radius(5px);
|
||||
}
|
||||
|
||||
&.editor-focus {
|
||||
border-color: @color-form-field-border-focus;
|
||||
}
|
||||
|
||||
&.size-tiny .redactor-editor { height: (@size-tiny - @richeditor-toolbar-size) !important; }
|
||||
&.size-small .redactor-editor { height: (@size-small - @richeditor-toolbar-size) !important; }
|
||||
&.size-large .redactor-editor { height: (@size-large - @richeditor-toolbar-size) !important; }
|
||||
&.size-huge .redactor-editor { height: (@size-huge - @richeditor-toolbar-size) !important; }
|
||||
&.size-giant .redactor-editor { height: (@size-giant - @richeditor-toolbar-size) !important; }
|
||||
&.size-tiny { .richeditor-set-height(@size-tiny); }
|
||||
&.size-small { .richeditor-set-height(@size-small); }
|
||||
&.size-large { .richeditor-set-height(@size-large); }
|
||||
&.size-huge { .richeditor-set-height(@size-huge); }
|
||||
&.size-giant { .richeditor-set-height(@size-giant); }
|
||||
}
|
||||
|
||||
//
|
||||
// Override redactor defaults
|
||||
// Overrides
|
||||
//
|
||||
|
||||
.redactor-box {
|
||||
margin-bottom: 0;
|
||||
overflow: hidden;
|
||||
|
||||
& iframe {
|
||||
border: none; // Oc
|
||||
}
|
||||
}
|
||||
|
||||
.redactor-box-fullscreen {
|
||||
z-index: @richeditor-zindex + 115 !important;
|
||||
}
|
||||
|
||||
.redactor-dropdown {
|
||||
z-index: @richeditor-zindex + 125 !important;
|
||||
}
|
||||
#redactor-modal-overlay,
|
||||
#redactor-modal-box,
|
||||
#redactor-modal {
|
||||
z-index: @richeditor-zindex + 120 !important;
|
||||
}
|
||||
|
||||
.redactor-toolbar {
|
||||
background: @color-richeditor-toolbar;
|
||||
.box-shadow(none);
|
||||
z-index: 410 !important;
|
||||
|
||||
li.redactor-btn-right {
|
||||
float: right;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
li a {
|
||||
color: @color-richeditor-toolbar-btn-color;
|
||||
font-size: 14px;
|
||||
width: 20px;
|
||||
line-height: 20px;
|
||||
|
||||
.user-select(none);
|
||||
|
||||
&:hover {
|
||||
background-color: @color-richeditor-toolbar-btn-bg-hover;
|
||||
color: @color-richeditor-toolbar-btn-color-hover;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.redactor-act {
|
||||
background-color: @color-richeditor-toolbar-btn-bg-active;
|
||||
color: @color-richeditor-toolbar-btn-color-hover;
|
||||
}
|
||||
|
||||
&.fa-redactor-btn {
|
||||
padding: 9px 10px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&.oc-redactor-button i:before {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
&.oc-autumn-button {
|
||||
color: #c03f31;
|
||||
|
||||
&:hover {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.redactor-editor {
|
||||
border: none;
|
||||
font-size: 13px;
|
||||
color: @input-color;
|
||||
padding: 15px;
|
||||
.fr-toolbar {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -127,7 +58,7 @@
|
|||
//
|
||||
|
||||
.field-richeditor.stretch {
|
||||
.redactor-box {
|
||||
.fr-box:not(.fr-fullscreen) {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 100% !important;
|
||||
|
|
@ -135,17 +66,11 @@
|
|||
.border-radius(0) !important;
|
||||
overflow: hidden;
|
||||
|
||||
.redactor-toolbar {
|
||||
.fr-toolbar {
|
||||
.border-radius(0) !important;
|
||||
|
||||
display: block;
|
||||
border-bottom: none;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.redactor-editor, textarea {
|
||||
.fr-wrapper {
|
||||
width: 100% !important;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
|
@ -155,39 +80,23 @@
|
|||
padding: 20px;
|
||||
}
|
||||
|
||||
.redactor-editor {
|
||||
height: auto !important;
|
||||
.fr-view, textarea {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
padding: 10px;
|
||||
.fr-placeholder {
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Full screen
|
||||
//
|
||||
|
||||
body .redactor-box-fullscreen {
|
||||
background: @body-bg;
|
||||
overflow-y: scroll !important;
|
||||
width: 100% !important;
|
||||
.redactor-editor {
|
||||
background: #fff;
|
||||
max-width: 960px;
|
||||
padding: 50px 30px !important;
|
||||
margin: @richeditor-gutter auto !important;
|
||||
padding: @richeditor-gutter;
|
||||
top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Placeholders and snippets
|
||||
//
|
||||
|
||||
.redactor-editor {
|
||||
.control-richeditor {
|
||||
.richeditor-snippet() {
|
||||
display: block;
|
||||
margin: 0 0 15px 0;
|
||||
|
|
|
|||
|
|
@ -7,14 +7,8 @@
|
|||
class="field-richeditor size-<?= $size ?> <?= $stretch?'layout-relative stretch':'' ?>"
|
||||
<?php if ($editorLang): ?>data-editor-lang="<?= $editorLang ?>"<?php endif ?>
|
||||
<?php if ($fullPage): ?>data-fullpage="true"<?php endif ?>
|
||||
data-data-locker="#<?= $this->getId('dataLocker') ?>"
|
||||
data-links-handler="<?= $this->getEventHandler('onGetPageLinks') ?>"
|
||||
data-control="richeditor">
|
||||
<textarea id="<?= $this->getId('textarea') ?>"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Data locker -->
|
||||
<div style="display: none">
|
||||
<textarea name="<?= $name ?>" id="<?= $this->getId('dataLocker') ?>"><?= e($value) ?></textarea>
|
||||
<textarea name="<?= $name ?>" id="<?= $this->getId('textarea') ?>"><?= e($value) ?></textarea>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
|
|
|||
Loading…
Reference in New Issue