Plugin for Ckeditor
/**
* @file plugin.js
* Encadre plugin for Ckeditor.
* Wraps the selected words/text in div element with custom class.
*/
(function() {
CKEDITOR.plugins.add('encadre', {
init: function(editor) {
editor.ui.addButton('encadre', {
label: 'Encadre',
command: 'insertDiv',
icon: this.path + 'icons/encadre.png'
});
editor.addCommand('insertDiv', {
exec : function(editor) {
divProceed(editor, 'encadre');
}
});
editor.addCommand( 'removeDiv', {
requiredContent: 'div',
exec: function( editor ) {
var selection = editor.getSelection(),
ranges = selection && selection.getRanges(),
range,
bookmarks = selection.createBookmarks(),
walker,
toRemove = [];
function findDiv( node ) {
var div = CKEDITOR.plugins.div.getSurroundDiv( editor, node );
if ( div && !div.data( 'cke-div-added' ) ) {
toRemove.push( div );
div.data( 'cke-div-added' );
}
}
for ( var i = 0; i < ranges.length; i++ ) {
range = ranges[ i ];
if ( range.collapsed )
findDiv( selection.getStartElement() );
else {
walker = new CKEDITOR.dom.walker( range );
walker.evaluator = findDiv;
walker.lastForward();
}
}
for ( i = 0; i < toRemove.length; i++ )
toRemove[ i ].remove( true );
selection.selectBookmarks( bookmarks );
}
});
if ( editor.addMenuItems && editor.contextMenu ) {
editor.addMenuItems({
removediv: {
label: 'Remove Encadre block',
command: 'removeDiv',
group: 'div',
order: 5
}
});
editor.contextMenu.addListener( function( element ) {
if ( !element || element.isReadOnly() )
return null;
if ( CKEDITOR.plugins.div.getSurroundDiv( editor ) ) {
return {
removediv: CKEDITOR.TRISTATE_OFF
};
}
return null;
});
}
}
});
CKEDITOR.plugins.div = {
getSurroundDiv: function( editor, start ) {
var path = editor.elementPath( start );
return editor.elementPath( path.blockLimit ).contains( 'div', 1 );
}
};
function divProceed(editor, className) {
function createDiv(editor) {
// new adding containers OR detected pre-existed containers.
var containers = [];
// node markers store.
var database = {};
// All block level elements which contained by the ranges.
var containedBlocks = [],
block;
var divLimitDefinition = (function () {
// Customzie from specialize blockLimit elements
var definition = CKEDITOR.tools.extend({}, CKEDITOR.dtd.$blockLimit);
if (editor.config.div_wrapTable) {
delete definition.td;
delete definition.th;
}
return definition;
})();
// DTD of 'div' element
var dtd = CKEDITOR.dtd.div;
// Get all ranges from the selection.
var selection = editor.getSelection(),
ranges = selection.getRanges();
var bookmarks = selection.createBookmarks();
var i, iterator;
// Calcualte a default block tag if we need to create blocks.
var blockTag = editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p';
// collect all included elements from dom-iterator
for (i = 0; i < ranges.length; i++) {
iterator = ranges[i].createIterator();
while (( block = iterator.getNextParagraph() )) {
// include contents of blockLimit elements.
if (block.getName() in divLimitDefinition) {
var j,
childNodes = block.getChildren();
for (j = 0; j < childNodes.count(); j++)
addSafely(containedBlocks, childNodes.getItem(j), database);
} else {
while (!dtd[block.getName()] && !block.equals(ranges[i].root))
block = block.getParent();
if (block.getName() == 'p' && block.getParent().getName() == 'figure') {
var imgHtml = block.getHtml();
var figureElement = block.getParent();
figureElement.setHtml(imgHtml);
block = figureElement.getParent();
}
addSafely(containedBlocks, block, database);
}
}
}
CKEDITOR.dom.element.clearAllMarkers(database);
var blockGroups = groupByDivLimit(containedBlocks);
var ancestor, blockEl, divElement;
divElement = new CKEDITOR.dom.element('div', editor.document);
divElement.setAttributes({class: className});
//console.log(blockGroups);
for (i = 0; i < blockGroups.length; i++) {
var currentNode = blockGroups[i][0];
// Calculate the common parent node of all contained elements.
ancestor = currentNode.getParent();
for (j = 1; j < blockGroups[i].length; j++)
ancestor = ancestor.getCommonAncestor(blockGroups[i][j]);
// Normalize the blocks in each group to a common parent.
for (j = 0; j < blockGroups[i].length; j++) {
currentNode = blockGroups[i][j];
while (!currentNode.getParent().equals(ancestor))
currentNode = currentNode.getParent();
// This could introduce some duplicated elements in array.
blockGroups[i][j] = currentNode;
}
// Wrapped blocks counting
var fixedBlock = null;
for (j = 0; j < blockGroups[i].length; j++) {
currentNode = blockGroups[i][j];
// Avoid DUP elements introduced by grouping.
if (!( currentNode.getCustomData && currentNode.getCustomData('block_processed') )) {
currentNode.is && CKEDITOR.dom.element.setMarker(database, currentNode, 'block_processed', true);
// Establish new container, wrapping all elements in this group.
if (!j)
divElement.insertBefore(currentNode);
divElement.append(currentNode);
}
}
CKEDITOR.dom.element.clearAllMarkers(database);
containers.push(divElement);
}
selection.selectBookmarks(bookmarks);
return containers;
}
function groupByDivLimit(nodes) {
var groups = [],
lastDivLimit = null,
path, block;
for (var i = 0; i < nodes.length; i++) {
block = nodes[i];
var limit = getDivContainer(block);
if (!limit.equals(lastDivLimit)) {
lastDivLimit = limit;
groups.push([]);
}
groups[groups.length - 1].push(block);
}
return groups;
}
function getDivContainer(element) {
var container = editor.elementPath(element).blockLimit;
// Dont stop at 'td' and 'th' when div should wrap entire table.
if (editor.config.div_wrapTable && container.is(['td', 'th'])) {
var parentPath = editor.elementPath(container.getParent());
container = parentPath.blockLimit;
}
return container;
}
function addSafely(collection, element, database) {
// 1. IE doesn't support customData on text nodes;
// 2. Text nodes never get chance to appear twice;
if (!element.is || !element.getCustomData('block_processed')) {
element.is && CKEDITOR.dom.element.setMarker(database, element, 'block_processed', true);
collection.push(element);
}
}
return createDiv(editor);
}
})();
<?php
/**
* @file
* M60 ckeditor.
*/
/**
* Implements hook_ckeditor_plugin().
*/
function m60_ckeditor_ckeditor_plugin() {
return array('encadre' => array(
'name' => 'encadre',
'desc' => t('Plugin adds simple html block with class encadre'),
'path' => drupal_get_path('module', 'm60_ckeditor') . '/plugins/encadre/',
'buttons' => array(
'encadre' => array(
'icon' => 'icons/encadre.png',
'label' => 'Encadre',
)
)
));
}