diff --git a/src/plugins/list-enhanced.js b/src/plugins/list-enhanced.js new file mode 100644 index 000000000..ec1e848d0 --- /dev/null +++ b/src/plugins/list-enhanced.js @@ -0,0 +1,468 @@ +/** + * SCEditor Inline-Code Plugin for BBCode format + * http://www.sceditor.com/ + * + * Copyright (C) 2011-2013, Sam Clarke (samclarke.com) + * + * SCEditor is licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * @fileoverview SCEditor list enhancement plugin + * This plugin expands the implementation of the list commands, adding dropdown + * options to select between different list types + * @author Alex Betis + */ + +(function (sceditor) { + 'use strict'; + + var dom = sceditor.dom; + + function isFunction(fn) { + return typeof fn === 'function'; + } + + /** + * Fixes a bug in FF where it sometimes wraps + * new lines in their own list item. + * See issue #359 + */ + function fixFirefoxListBug(editor) { + // Only apply to Firefox as will break other browsers. + if ('mozHidden' in document) { + var node = editor.getBody(); + var next; + + while (node) { + next = node; + + if (next.firstChild) { + next = next.firstChild; + } else { + + while (next && !next.nextSibling) { + next = next.parentNode; + } + + if (next) { + next = next.nextSibling; + } + } + + if (node.nodeType === 3 && /[\n\r\t]+/.test(node.nodeValue)) { + // Only remove if newlines are collapsed + if (!/^pre/.test(dom.css(node.parentNode, 'whiteSpace'))) { + dom.remove(node); + } + } + + node = next; + } + } + } + + sceditor.plugins['list-enhanced'] = function () { + var base = this; + + /** + * Default configuration of the ordered list options. + * + * The list should include BBCode tagName, valid css style + * list-style-type and the description of the option that + * will be shown in the dropdown list + * + * The format of the list is: + * bbCode-type: { "type": style-type, "description": Description } + * + * @type {Object} + */ + var orderedList = { + '1': { + type: 'decimal', + description: 'Decimal numbers (1, 2, 3, 4)' + }, + 'a': { + type: 'lower-alpha', + description: 'Alphabetic lowercase (a, b, c, d)' + }, + 'A': { + type: 'upper-alpha', + description: 'Alphabetic uppercase (A, B, C, D)' + }, + 'i': { + type: 'lower-roman', + description: 'Roman lowercase (i, ii, iii, iv)' + }, + 'I': { + type: 'upper-roman', + description: 'Roman uppercase (I, II, III, IV)' + } + }; + + /** + * Default configuration of the bullet list options. + * + * The list should include BBCode tagName, which is valid css style name + * and the description of the option that will be shown in the + * dropdown list + * + * The format of the list is: + * bbCode-type: Description + * + * @type {Object} + */ + var bulletList = { + 'disc': 'Bullet', + 'circle': 'Circle', + 'square': 'Square', + 'none': 'None' + }; + + /** + * Use aleternative list format configration option. + * Default: True + * + * Alternative list format craetes lists in phpBB format: + * [list=type] + * [*]text + * [*]text + * [/list] + * @type {Boolean} + */ + var alternativeLists = true; + + /** + * Private functions + * @private + */ + var bulletHandler; + var orderedHandler; + + base.init = function () { + var opts = this.opts; + var pOpts = opts.listEnhanced; + + // Enable for BBCode only + if (opts.format && opts.format !== 'bbcode') { + return; + } + + if (pOpts) { + if (pOpts.orderedList) { + orderedList = pOpts.orderedList; + } + + if (pOpts.bulletList) { + bulletList = pOpts.bulletList; + } + + if (pOpts.alternativeLists) { + alternativeLists = pOpts.alternativeLists; + } + } + + // The plugin will override current implementation + sceditor.command.set('orderedlist', { + exec: orderedHandler, + txtExec: orderedHandler, + tooltip: 'Ordered list' + }); + + sceditor.command.set('bulletlist', { + exec: bulletHandler, + txtExec: bulletHandler, + tooltip: 'Bullet list' + }); + + sceditor.formats.bbcode.set('list', { + breakStart: true, + isInline: false, + skipLastLineBreak: true, + html: function (token, attrs, content) { + var listType = 'disc'; + var toHtml = null; + + if (attrs.defaultattr) { + listType = attrs.defaultattr; + } + + if (bulletList[listType]) { + // This listType belongs to bulletList (UL) + toHtml = sceditor.formats.bbcode.get('ul').html; + } else if (orderedList[listType]) { + // This listType belongs to orderedList (OL) + toHtml = sceditor.formats.bbcode.get('ol').html; + } else { + // unknown listType, use default bullet list behavior + toHtml = sceditor.formats.bbcode.get('ul').html; + } + + if (isFunction(toHtml)) { + return toHtml.call(this, token, attrs, content); + } else { + token.attrs['0'] = content; + return sceditor.formats.bbcode.formatBBCodeString( + toHtml, token.attrs); + } + } + }); + + sceditor.formats.bbcode.set('li', { + tags: { + li: null + }, + isInline: false, + closedBy: ['/ul', '/ol', '/list', '*', 'li'], + format: function (element, content) { + if (alternativeLists) { + return '[*]' + content; + } else { + return '[li]' + content + '[/li]'; + } + }, + html: '
  • {0}
  • ' + }); + + if (alternativeLists) { + sceditor.formats.bbcode.set('*', { + isInline: false, + excludeClosing: true, + closedBy: ['/ul', '/ol', '/list', '*', 'li'], + html: '
  • {0}
  • ' + }); + } + + sceditor.formats.bbcode.set('ol', { + tags: { + ol: null + }, + breakStart: true, + isInline: false, + skipLastLineBreak: true, + format: function (element, content) { + var tagType = dom.attr(element, 'data-tagtype'); + var list = orderedList; + var listTag = 'ol'; + + if (alternativeLists) { + listTag = 'list'; + } + + if ((tagType && tagType !== '1' || + alternativeLists) && list[tagType]) { + return '[' + listTag + '=' + tagType + ']' + + content + + '[/' + listTag + ']'; + } else { + return '[' + listTag + ']' + + content + + '[/' + listTag + ']'; + } + }, + html: function (token, attrs, content) { + var tagType = '1'; + var styleType; + var list = orderedList; + var attr = attrs.defaultattr; + + if (attr) { + tagType = attr; + } + + // Specified list type is not valid, backup to default + if (!list[tagType]) { + tagType = '1'; + } + + styleType = list[tagType].type; + + return '
      ' + content + '
    '; + } + }); + + sceditor.formats.bbcode.set('ul', { + tags: { + ul: null + }, + breakStart: true, + isInline: false, + skipLastLineBreak: true, + format: function (element, content) { + var listType = element.style['list-style-type']; + var list = bulletList; + var listTag = 'ul'; + + if (alternativeLists) { + listTag = 'list'; + } + + if (listType && listType !== 'disc' && list[listType]) { + return '[' + listTag + '=' + listType + ']' + + content + + '[/' + listTag + ']'; + } else { + return '[' + listTag + ']' + + content + + '[/' + listTag + ']'; + } + }, + html: function (token, attrs, content) { + var listType = 'disc'; + var attr = attrs.defaultattr; + var list = bulletList; + + if (attr) { + listType = attr; + } + + // Specified list type is not valid, backup to default + if (!list[listType]) { + listType = 'disc'; + } + + return ''; + } + }); + }; + + /** + * Function for the txtExec and exec properties + * + * @param {node} caller + * @private + */ + orderedHandler = function (caller, selected) { + var editor = this; + var content = document.createElement('div'); + + sceditor.utils.each(orderedList, function (tag, item) { + var link = document.createElement('a'); + link.className = 'sceditor-listtype-option'; + link.setAttribute('data-tagtype', tag); + link.textContent = item.description;; + link.addEventListener('click', function (e) { + var tagType = dom.attr(this, 'data-tagtype'); + var listTag = 'ol'; + var itemStart = '[li]'; + var itemEnd = '[/li]'; + + if (alternativeLists) { + listTag = 'list'; + itemStart = '[*]'; + itemEnd = ''; + } + + editor.closeDropDown(true); + fixFirefoxListBug(this); + + if (editor.sourceMode()) { + var content = ''; + + selected.split(/\r?\n/).forEach(function (item) { + content += (content ? '\n' : '') + + itemStart + item + itemEnd; + }); + + if (tagType === '1' && !alternativeLists) { + editor.insertText( + '[' + listTag + ']\n' + + content + + '\n[/' + listTag + ']' + ); + } else { + editor.insertText( + '[' + listTag + '=' + tagType + ']\n' + + content + + '\n[/' + listTag + ']' + ); + } + } else { + var styleType = orderedList[tagType].type; + + editor.wysiwygEditorInsertHtml( + '

    ' + ); + } + + e.preventDefault(); + }); + + content.appendChild(link); + }); + + editor.createDropDown(caller, 'listtype-picker', content); + }; + + bulletHandler = function (caller, selected) { + var editor = this; + var content = document.createElement('div'); + + sceditor.utils.each(bulletList, function (styleType, description) { + var ul = document.createElement('ul'); + var link = document.createElement('a'); + var li = document.createElement('li'); + ul.appendChild(li); + li.appendChild(link); + ul.style.listStyleType = styleType; + ul.className = 'sceditor-listtype'; + link.className = 'sceditor-listtype-option'; + link.setAttribute('data-tagtype', styleType); + link.textContent = description;; + link.addEventListener('click', function (e) { + var tagType = dom.attr(this, 'data-tagtype'); + var listTag = 'ul'; + var itemStart = '[li]'; + var itemEnd = '[/li]'; + + if (alternativeLists) { + listTag = 'list'; + itemStart = '[*]'; + itemEnd = ''; + } + + editor.closeDropDown(true); + fixFirefoxListBug(this); + + if (editor.sourceMode()) { + var content = ''; + + selected.split(/\r?\n/).forEach(function (item) { + content += (content ? '\n' : '') + + itemStart + item + itemEnd; + }); + + if (tagType === 'disc') { + editor.insertText( + '[' + listTag + ']\n' + + content + + '\n[/' + listTag + ']' + ); + } else { + editor.insertText( + '[' + listTag + '=' + tagType + ']\n' + + content + + '\n[/' + listTag + ']' + ); + } + } else { + editor.wysiwygEditorInsertHtml( + '' + ); + } + + e.preventDefault(); + }); + + content.appendChild(ul); + }); + + editor.createDropDown(caller, 'listtype-picker', content); + }; + + }; +})(sceditor); diff --git a/src/themes/inc/defaultbase.less b/src/themes/inc/defaultbase.less index f3f51097a..4bd31ff7d 100644 --- a/src/themes/inc/defaultbase.less +++ b/src/themes/inc/defaultbase.less @@ -226,6 +226,7 @@ div.sceditor-dropdown div { div.sceditor-font-picker, div.sceditor-fontsize-picker, + div.sceditor-listtype-picker, div.sceditor-format { padding: 6px 0; } @@ -266,6 +267,7 @@ div.sceditor-dropdown div { .sceditor-fontsize-option, .sceditor-font-option, + .sceditor-listtype-option, .sceditor-format a { display: block; padding: 7px 10px; @@ -274,7 +276,13 @@ div.sceditor-dropdown div { color: #222; } - .sceditor-fontsize-option { + ul.sceditor-listtype { + padding: 0; + margin: 0 30px; + } + + .sceditor-fontsize-option, + .sceditor-listtype-option { padding: 7px 13px; } @@ -282,17 +290,17 @@ div.sceditor-dropdown div { float: left; } - .sceditor-color-option { - display: block; - border: 2px solid #fff; - height: 18px; - width: 18px; - overflow: hidden; - } + .sceditor-color-option { + display: block; + border: 2px solid #fff; + height: 18px; + width: 18px; + overflow: hidden; + } - .sceditor-color-option:hover { - border: 1px solid #aaa; - } + .sceditor-color-option:hover { + border: 1px solid #aaa; + } /** diff --git a/tests/manual/debug/index.html b/tests/manual/debug/index.html index 23e309d92..6a82e7c4c 100644 --- a/tests/manual/debug/index.html +++ b/tests/manual/debug/index.html @@ -20,6 +20,7 @@ +