diff --git a/dialogs/contents.js b/dialogs/contents.js new file mode 100644 index 0000000..3e89e71 --- /dev/null +++ b/dialogs/contents.js @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2014-2016, CKSource - Frederico Knabben. All rights reserved. + * Licensed under the terms of the MIT License (see LICENSE.md). + */ + +// Note: This automatic widget to dialog window binding (the fact that every field is set up from the widget +// and is committed to the widget) is only possible when the dialog is opened by the Widgets System +// (i.e. the widgetDef.dialog property is set). +// When you are opening the dialog window by yourself, you need to take care of this by yourself too. + +CKEDITOR.dialog.add('contents', function (editor) { + return { + title: 'Edit', + minWidth: 200, + minHeight: 100, + contents: [ + { + id: 'info', + elements: [ + { + id: 'align', + type: 'select', + label: 'Align', + items: [ + [editor.lang.common.notSet, ''], + [editor.lang.common.alignLeft, 'float-left'], + [editor.lang.common.alignRight, 'float-right'], + ], + 'default': editor.lang.common.notSet, + // When setting up this field, set its value to the "align" value from widget data. + // Note: Align values used in the widget need to be the same as those defined in the "items" array above. + setup: function (widget) { + widget.data.align === undefined ? this.setValue('') : this.setValue(widget.data.align); + }, + // When committing (saving) this field, set its value to the widget data. + commit: function (widget) { + widget.setData('align', this.getValue()); + } + }, + + { + id: 'chkInsertOpt', + type: 'checkbox', + label: 'Ignore nested headers', + 'default': true, + setup: function (widget) { + widget.data.chkInsertOpt === undefined ? this.setValue(false) : this.setValue(widget.data.chkInsertOpt); + }, + + commit: function (widget) { + widget.setData('chkInsertOpt', this.getValue()); + } + } + ] + }, + ] + }; +}); \ No newline at end of file diff --git a/icons/contents.png b/icons/contents.png new file mode 100644 index 0000000..c7a061a Binary files /dev/null and b/icons/contents.png differ diff --git a/plugin.js b/plugin.js new file mode 100644 index 0000000..eea27c5 --- /dev/null +++ b/plugin.js @@ -0,0 +1,132 @@ +CKEDITOR.plugins.add('contents', { + requires: 'widget', + + icons: 'contents', + + init: function (editor) { + editor.addContentsCss(this.path + 'styles/styles.css'); + CKEDITOR.dialog.add('contents', this.path + 'dialogs/contents.js'); + + // Default Config + var defaultConfig = { + header: '

Contents

', + //ol or ul + listType: 'ol', + headersSelector: '> h1,> h2,> h3,> h4,> h5,> h6,', + nestedHeadersSelector: 'h1,h2,h3,h4,h5,h6,' + }; + + // Get Config + var config = CKEDITOR.tools.extend(defaultConfig, editor.config.contents || {}, true); + + editor.widgets.add('contents', { + button: 'Insert Table of Contents', + + template: + '
', + + allowedContent: + 'div(!widget-toc,float-left,float-right,align-center);' + + 'p(!toc-title);', + + dialog: 'contents', + + upcast: function (element) { + return element.name == 'div' + && element.hasClass('widget-toc'); + }, + + init: function () { + editor.on('saveSnapshot', function (evt) { + buildToc(this.element) + }.bind(this)); + + this.on('focus', function (evt) { + buildToc(this.element) + }.bind(this)); + + buildToc(this.element); + + if (this.element.hasClass('float-left')) + this.setData('align', 'float-left'); + if (this.element.hasClass('float-right')) + this.setData('align', 'float-right'); + if (this.element.hasClass('toc_root')) + this.setData('chkInsertOpt', true); + }, + + data: function () { + this.element.removeClass('float-left'); + this.element.removeClass('float-right'); + this.element.removeClass('toc_root'); + if (this.data.align) + this.element.addClass(this.data.align); + + if (this.data.chkInsertOpt) + this.element.addClass('toc_root'); + }, + }); + + function buildToc(element) { + + element.setHtml(config.header); + + Container = new CKEDITOR.dom.element(config.listType); + Container.appendTo(element); + + if (element.hasClass('toc_root')) { + findRoot = config.headersSelector; + } else { + findRoot = config.nestedHeadersSelector; + } + + var headings = editor.editable().find(findRoot), + parentLevel = 1, + length = headings.count(); + + //get each heading + for (var i = 0; i < length; ++i) { + + var currentHeading = headings.getItem(i), + text = currentHeading.getText(), + newLevel = parseInt(currentHeading.getName().substr(1, 1)); + var diff = (newLevel - parentLevel); + + //set the start level in case it is not h1 + if (i === 0) { + diff = 0; + parentLevel = newLevel; + } + + //we need a new ul if the new level has a higher number than its parents number + if (diff > 0) { + var containerLiNode = Container.getLast(); + var ulNode = new CKEDITOR.dom.element(config.listType); + ulNode.appendTo(containerLiNode); + Container = ulNode; + parentLevel = newLevel; + } + + //we need to get a previous ul if the new level has a lower number than its parents number + if (diff < 0) { + while (0 !== diff++) { + parent = Container.getParent().getParent(); + Container = (parent.getName() === config.listType ? parent : Container); + } + parentLevel = newLevel; + } + + if (text == null || text.trim() === '') { + text = ' ' + } + + var id = text.replace(/[^A-Za-z0-9_\-]+/g, '+'); + currentHeading.setAttribute('id', id); + + var liNode = CKEDITOR.dom.element.createFromHtml('
  • ' + text + '
  • '); + + liNode.appendTo(Container); + } + } + } +}); \ No newline at end of file diff --git a/styles/styles.css b/styles/styles.css new file mode 100644 index 0000000..8a4eee7 --- /dev/null +++ b/styles/styles.css @@ -0,0 +1,34 @@ +.widget-toc{ + + display: table; + border: 1px solid #a2a9b1; + background-color: #f8f9fa; + padding-right: 1rem; + font-size: 95%; +} + +.widget-toc ol { + padding-right: 0px; + counter-reset: item; +} +.widget-toc ol li { + display: block; + position: relative; +} +.widget-toc ol li:before { + content: counters(item, "."); + counter-increment: item; + position: absolute; + margin-right: 100%; + right: 0.5rem; +} + + +.toc-title{ + + text-align: center; + font-weight: 700; + margin: 0; + padding: 0; + +} \ No newline at end of file