diff --git a/src/formats/bbcode.js b/src/formats/bbcode.js
index 277e13121..83f78677f 100644
--- a/src/formats/bbcode.js
+++ b/src/formats/bbcode.js
@@ -137,26 +137,56 @@
},
bulletlist: {
txtExec: function (caller, selected) {
+ var editor = this;
var content = '';
- each(selected.split(/\r?\n/), function () {
- content += (content ? '\n' : '') +
- '[li]' + this + '[/li]';
- });
+ getEditorCommand('bulletlist')._dropDown(
+ editor,
+ caller,
+ function (listType) {
+ selected.split(/\r?\n/).forEach(function (item) {
+ content += (content ? '\n' : '') +
+ '[li]' + item + '[/li]';
+ });
- this.insertText('[ul]\n' + content + '\n[/ul]');
+ if (listType === 'disc') {
+ editor.insertText(
+ '[ul]\n' + content + '\n[/ul]'
+ );
+ } else {
+ editor.insertText(
+ '[ul=' + listType + ']\n' + content + '\n[/ul]'
+ );
+ }
+ }
+ );
}
},
orderedlist: {
txtExec: function (caller, selected) {
+ var editor = this;
var content = '';
- each(selected.split(/\r?\n/), function () {
- content += (content ? '\n' : '') +
- '[li]' + this + '[/li]';
- });
+ getEditorCommand('orderedlist')._dropDown(
+ editor,
+ caller,
+ function (tagType) {
+ selected.split(/\r?\n/).forEach(function (item) {
+ content += (content ? '\n' : '') +
+ '[li]' + item + '[/li]';
+ });
- this.insertText('[ol]\n' + content + '\n[/ol]');
+ if (tagType === '1') {
+ editor.insertText(
+ '[ol]\n' + content + '\n[/ol]'
+ );
+ } else {
+ editor.insertText(
+ '[ol=' + tagType + ']\n' + content + '\n[/ol]'
+ );
+ }
+ }
+ );
}
},
table: {
@@ -436,14 +466,41 @@
// START_COMMAND: Lists
ul: {
- tags: {
- ul: null
+ styles: {
+ 'list-style-type': null
},
breakStart: true,
isInline: false,
skipLastLineBreak: true,
- format: '[ul]{0}[/ul]',
- html: '
'
+ format: function (element, content) {
+ var tag = element.nodeName.toLowerCase();
+ var listType = element.style['list-style-type'];
+ var validTypes = ['disc', 'circle', 'square', 'none'];
+
+ // That call is not for this tag, skip it
+ if (tag !== 'ul') {
+ return content;
+ }
+
+ if (listType && listType !== 'disc' &&
+ validTypes.indexOf(listType) > -1) {
+ return '[ul=' + listType + ']' + content + '[/ul]';
+ } else {
+ return '[ul]' + content + '[/ul]';
+ }
+ },
+ html: function (token, attrs, content) {
+ var listType = 'disc';
+ var validTypes = ['disc', 'circle', 'square', 'none'];
+ var attr = attrs.defaultattr;
+
+ if (attr && validTypes.indexOf(attr) > -1) {
+ listType = attr;
+ }
+
+ return '';
+ }
},
list: {
breakStart: true,
@@ -452,14 +509,51 @@
html: ''
},
ol: {
- tags: {
- ol: null
+ styles: {
+ 'list-style-type': null
},
breakStart: true,
isInline: false,
skipLastLineBreak: true,
- format: '[ol]{0}[/ol]',
- html: '{0} '
+ format: function (element, content) {
+ var tag = element.nodeName.toLowerCase();
+ var tagType = attr(element, 'data-tagtype');
+ var validTypes = ['1', 'A', 'a', 'I', 'i'];
+
+ // That call is not for this tag, skip it
+ if (tag !== 'ol') {
+ return content;
+ }
+
+ if (tagType && tagType !== '1' &&
+ validTypes.indexOf(tagType) > -1) {
+ return '[ol=' + tagType + ']' + content + '[/ol]';
+ } else {
+ return '[ol]' + content + '[/ol]';
+ }
+ },
+ html: function (token, attrs, content) {
+ var tagType = '1';
+ var styleType = 'decimal';
+ var validTypes = ['1', 'A', 'a', 'I', 'i'];
+ var attr = attrs.defaultattr;
+
+ if (attr && validTypes.indexOf(attr) > -1) {
+ tagType = attr;
+ }
+
+ switch (tagType) {
+ case '1': styleType = 'decimal'; break;
+ case 'A': styleType = 'upper-alpha'; break;
+ case 'a': styleType = 'lower-alpha'; break;
+ case 'I': styleType = 'upper-roman'; break;
+ case 'i': styleType = 'lower-roman'; break;
+ default: styleType = 'decimal'; break;
+ }
+
+ return '' + content + ' ';
+ }
},
li: {
tags: {
diff --git a/src/formats/xhtml.js b/src/formats/xhtml.js
index dcd1d1acc..7e90521a2 100644
--- a/src/formats/xhtml.js
+++ b/src/formats/xhtml.js
@@ -100,10 +100,54 @@
}
},
bulletlist: {
- txtExec: ['']
+ txtExec: function (caller, selected) {
+ var editor = this;
+ var content = '';
+
+ getEditorCommand('bulletlist')._dropDown(
+ editor,
+ caller,
+ function (listType) {
+ selected.split(/\r?\n/).forEach(function (item) {
+ content += (content ? '\n' : '') +
+ '' + item + ' ';
+ });
+
+ editor.insertText(
+ ''
+ );
+ }
+ );
+ }
},
orderedlist: {
- txtExec: ['', ' ']
+ txtExec: function (caller, selected) {
+ var editor = this;
+ var content = '';
+
+ getEditorCommand('orderedlist')._dropDown(
+ editor,
+ caller,
+ function (tagType, styleType) {
+ selected.split(/\r?\n/).forEach(function (item) {
+ content += (content ? '\n' : '') +
+ '' + item + ' ';
+ });
+
+ if (styleType === 'decimal') {
+ editor.insertText(
+ '' + content + ' '
+ );
+ } else {
+ editor.insertText(
+ '' + content + ' '
+ );
+ }
+ }
+ );
+ }
},
table: {
txtExec: ['']
@@ -1156,6 +1200,16 @@
conv: function (node) {
node.parentNode.removeChild(node);
}
+ },
+ {
+ tags: {
+ ol: {
+ 'data-tagtype': null
+ }
+ },
+ conv: function (node) {
+ removeAttr(node, 'data-tagtype');
+ }
}
];
diff --git a/src/lib/defaultCommands.js b/src/lib/defaultCommands.js
index 9f8fe3281..ae1f62e29 100644
--- a/src/lib/defaultCommands.js
+++ b/src/lib/defaultCommands.js
@@ -284,18 +284,97 @@ var defaultCmds = {
// END_COMMAND
// START_COMMAND: Bullet List
bulletlist: {
- exec: function () {
- fixFirefoxListBug(this);
- this.execCommand('insertunorderedlist');
+ _dropDown: function (editor, caller, callback) {
+ var content = dom.createElement('div');
+
+ dom.on(content, 'click', 'a', function (e) {
+ callback(dom.data(this, 'type'));
+ editor.closeDropDown(true);
+ e.preventDefault();
+ });
+
+ dom.appendChild(content, _tmpl('ulistTypeOpt',
+ { type: 'disc', text: 'Bullet' }, true));
+ dom.appendChild(content, _tmpl('ulistTypeOpt',
+ { type: 'circle', text: 'Circle' }, true));
+ dom.appendChild(content, _tmpl('ulistTypeOpt',
+ { type: 'square', text: 'Square' }, true));
+ dom.appendChild(content, _tmpl('ulistTypeOpt',
+ { type: 'none', text: 'None' }, true));
+
+ editor.createDropDown(caller, 'listtype-picker', content);
+ },
+ exec: function (caller) {
+ var editor = this;
+
+ defaultCmds.bulletlist._dropDown(editor, caller, function (type) {
+ fixFirefoxListBug(this);
+ editor.wysiwygEditorInsertHtml(
+ ''
+ );
+ });
},
tooltip: 'Bullet list'
},
// END_COMMAND
// START_COMMAND: Ordered List
orderedlist: {
- exec: function () {
- fixFirefoxListBug(this);
- this.execCommand('insertorderedlist');
+ _dropDown: function (editor, caller, callback) {
+ var content = dom.createElement('div');
+
+ dom.on(content, 'click', 'a', function (e) {
+ callback(dom.data(this, 'tagtype'),
+ dom.data(this, 'styletype'));
+ editor.closeDropDown(true);
+ e.preventDefault();
+ });
+
+ dom.appendChild(content, _tmpl('olistTypeOpt',
+ {
+ tagType: '1',
+ styleType: 'decimal',
+ text: 'Decimal numbers (1, 2, 3, 4)'
+ }, true));
+ dom.appendChild(content, _tmpl('olistTypeOpt',
+ {
+ tagType: 'a',
+ styleType: 'lower-alpha',
+ text: 'Alphabetic lowercase (a, b, c, d)'
+ }, true));
+ dom.appendChild(content, _tmpl('olistTypeOpt',
+ {
+ tagType: 'A',
+ styleType: 'upper-alpha',
+ text: 'Alphabetic uppercase (A, B, C, D)'
+ }, true));
+ dom.appendChild(content, _tmpl('olistTypeOpt',
+ {
+ tagType: 'i',
+ styleType: 'lower-roman',
+ text: 'Roman lowercase (i, ii, iii, iv)'
+ }, true));
+ dom.appendChild(content, _tmpl('olistTypeOpt',
+ {
+ tagType: 'I',
+ styleType: 'upper-roman',
+ text: 'Roman uppercase (I, II, III, IV)'
+ }, true));
+
+ editor.createDropDown(caller, 'listtype-picker', content);
+ },
+ exec: function (caller) {
+ var editor = this;
+
+ defaultCmds.orderedlist._dropDown(editor, caller,
+ function (tagType, styleType) {
+ fixFirefoxListBug(this);
+ editor.wysiwygEditorInsertHtml(
+ ' '
+ );
+ }
+ );
},
tooltip: 'Numbered list'
},
diff --git a/src/lib/templates.js b/src/lib/templates.js
index 98543f09a..2926aa88e 100644
--- a/src/lib/templates.js
+++ b/src/lib/templates.js
@@ -39,6 +39,16 @@ var _templates = {
sizeOpt: '{size} ',
+ ulistTypeOpt: '' +
+ ' ',
+
+ olistTypeOpt: '{text} ',
+
pastetext:
'{label} ' +
'
' +
diff --git a/src/themes/inc/defaultbase.less b/src/themes/inc/defaultbase.less
index 9fe3dddb6..7fcc64a0b 100644
--- a/src/themes/inc/defaultbase.less
+++ b/src/themes/inc/defaultbase.less
@@ -225,6 +225,7 @@ div.sceditor-dropdown div {
div.sceditor-font-picker,
div.sceditor-fontsize-picker,
+ div.sceditor-listtype-picker,
div.sceditor-format {
padding: 6px 0;
}
@@ -264,6 +265,7 @@ div.sceditor-dropdown div {
}
.sceditor-fontsize-option,
+ .sceditor-listtype-option,
.sceditor-font-option,
.sceditor-format a {
display: block;
@@ -273,7 +275,13 @@ div.sceditor-dropdown div {
color: #222;
}
- .sceditor-fontsize-option {
+ ul.sceditor-listtype {
+ padding: 0;
+ margin: 0 20px;
+ }
+
+ .sceditor-fontsize-option,
+ .sceditor-listtype-option {
padding: 7px 13px;
}
@@ -281,17 +289,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/unit/formats/bbcode.js b/tests/unit/formats/bbcode.js
index b58aae2d7..e733429c7 100644
--- a/tests/unit/formats/bbcode.js
+++ b/tests/unit/formats/bbcode.js
@@ -253,7 +253,7 @@ QUnit.test('New line handling', function (assert) {
this.htmlToBBCode(
'text
' +
'' + IE_BR_STR + '
' +
- ''
+ ''
),
'text\n\n[ul]\n[li]text[/li]\n[/ul]\n',
'Div siblings with a list'
@@ -264,7 +264,7 @@ QUnit.test('New line handling', function (assert) {
'text
' +
'' + IE_BR_STR + '
' +
'' + IE_BR_STR + '
' +
- ''
+ ''
),
'text\n\n\n[ul]\n[li]text[/li]\n[/ul]\n',
'Multiple div siblings with a list'
@@ -284,7 +284,7 @@ QUnit.test('New line handling', function (assert) {
assert.equal(
this.htmlToBBCode(
- ''
+ ''
),
'[ul]\n[li]newline\n[/li]\n[/ul]\n',
'List item last child block level'
@@ -571,28 +571,79 @@ QUnit.test('colour', function (assert) {
QUnit.test('List', function (assert) {
assert.equal(
- this.htmlToBBCode(''),
+ this.htmlToBBCode(''),
'[ul]\n[li]test[/li]\n[/ul]\n',
- 'UL tag'
+ 'UL tag, disc type'
);
assert.equal(
- this.htmlToBBCode('test' + IE_BR_STR + ' '),
+ this.htmlToBBCode(''),
+ '[ul=circle]\n[li]test[/li]\n[/ul]\n',
+ 'UL tag, circle type'
+ );
+
+ assert.equal(
+ this.htmlToBBCode(''),
+ '[ul=square]\n[li]test[/li]\n[/ul]\n',
+ 'UL tag, square type'
+ );
+
+ assert.equal(
+ this.htmlToBBCode(''),
+ '[ul=none]\n[li]test[/li]\n[/ul]\n',
+ 'UL tag, none type'
+ );
+
+ assert.equal(
+ this.htmlToBBCode('test' + IE_BR_STR + ' '),
'[ol]\n[li]test[/li]\n[/ol]\n',
- 'OL tag'
+ 'OL tag, type="1"'
+ );
+
+ assert.equal(
+ this.htmlToBBCode('test' + IE_BR_STR + ' '),
+ '[ol=A]\n[li]test[/li]\n[/ol]\n',
+ 'OL tag, type="A"'
+ );
+
+ assert.equal(
+ this.htmlToBBCode('test' + IE_BR_STR + ' '),
+ '[ol=a]\n[li]test[/li]\n[/ol]\n',
+ 'OL tag, type="a"'
+ );
+
+ assert.equal(
+ this.htmlToBBCode('test' + IE_BR_STR + ' '),
+ '[ol=I]\n[li]test[/li]\n[/ol]\n',
+ 'OL tag, type="I"'
+ );
+
+ assert.equal(
+ this.htmlToBBCode('test' + IE_BR_STR + ' '),
+ '[ol=i]\n[li]test[/li]\n[/ol]\n',
+ 'OL tag, type="i"'
);
assert.equal(
this.htmlToBBCode(
- '' +
+ '' +
'test' +
- '' +
+ '' +
'sub' + IE_BR_STR + ' ' +
' ' +
' ' +
' '
),
- '[ul]\n[li]test\n[ul]\n[li]sub[/li]\n[/ul]\n[/li]\n[/ul]\n',
+ '[ul]\n[li]test\n[ul=circle]\n[li]sub[/li]\n[/ul]\n[/li]\n[/ul]\n',
'Nested UL tag'
);
});
diff --git a/tests/unit/formats/bbcode.parser.js b/tests/unit/formats/bbcode.parser.js
index 21649dd9f..aa5c09c51 100644
--- a/tests/unit/formats/bbcode.parser.js
+++ b/tests/unit/formats/bbcode.parser.js
@@ -729,19 +729,87 @@ QUnit.test('Font colour', function (assert) {
QUnit.test('List', function (assert) {
assert.htmlEqual(
this.parser.toHTML('[ul][li]test[/li][/ul]'),
- '',
- 'UL'
+ '',
+ 'UL, no type'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ul=disc][li]test[/li][/ul]'),
+ '',
+ 'UL, disc type'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ul=circle][li]test[/li][/ul]'),
+ '',
+ 'UL, circle type'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ul=square][li]test[/li][/ul]'),
+ '',
+ 'UL, square type'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ul=none][li]test[/li][/ul]'),
+ '',
+ 'UL, none type'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ul=zzz][li]test[/li][/ul]'),
+ '',
+ 'UL, unknown type'
);
assert.htmlEqual(
this.parser.toHTML('[ol][li]test[/li][/ol]'),
- 'test' + IE_BR_STR + ' ',
- 'OL'
+ 'test' +
+ IE_BR_STR + ' ',
+ 'OL no type'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ol=1][li]test[/li][/ol]'),
+ 'test' +
+ IE_BR_STR + ' ',
+ 'OL, type="1"'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ol=A][li]test[/li][/ol]'),
+ 'test' +
+ IE_BR_STR + ' ',
+ 'OL, type="A"'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ol=a][li]test[/li][/ol]'),
+ 'test' +
+ IE_BR_STR + ' ',
+ 'OL, type="a"'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ol=I][li]test[/li][/ol]'),
+ 'test' +
+ IE_BR_STR + ' ',
+ 'OL, type="I"'
+ );
+
+ assert.htmlEqual(
+ this.parser.toHTML('[ol=i][li]test[/li][/ol]'),
+ 'test' +
+ IE_BR_STR + ' ',
+ 'OL, type="i"'
);
assert.htmlEqual(
- this.parser.toHTML('[ul][li]test[ul][li]sub[/li][/ul][/li][/ul]'),
- '',
+ this.parser.toHTML('[ul][li]test[ul=circle][li]sub[/li][/ul][/li][/ul]'),
+ '',
'Nested UL'
);
});