~canonical-sysadmins/wordpress/4.5.2

« back to all changes in this revision

Viewing changes to wp-includes/js/tinymce/plugins/lists/plugin.js

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * plugin.js
 
3
 *
 
4
 * Copyright, Moxiecode Systems AB
 
5
 * Released under LGPL License.
 
6
 *
 
7
 * License: http://www.tinymce.com/license
 
8
 * Contributing: http://www.tinymce.com/contributing
 
9
 */
 
10
 
 
11
/*global tinymce:true */
 
12
/*eslint consistent-this:0 */
 
13
 
 
14
tinymce.PluginManager.add('lists', function(editor) {
 
15
        var self = this;
 
16
 
 
17
        function isListNode(node) {
 
18
                return node && (/^(OL|UL|DL)$/).test(node.nodeName);
 
19
        }
 
20
 
 
21
        function isFirstChild(node) {
 
22
                return node.parentNode.firstChild == node;
 
23
        }
 
24
 
 
25
        function isLastChild(node) {
 
26
                return node.parentNode.lastChild == node;
 
27
        }
 
28
 
 
29
        function isTextBlock(node) {
 
30
                return node && !!editor.schema.getTextBlockElements()[node.nodeName];
 
31
        }
 
32
 
 
33
        editor.on('init', function() {
 
34
                var dom = editor.dom, selection = editor.selection;
 
35
 
 
36
                /**
 
37
                 * Returns a range bookmark. This will convert indexed bookmarks into temporary span elements with
 
38
                 * index 0 so that they can be restored properly after the DOM has been modified. Text bookmarks will not have spans
 
39
                 * added to them since they can be restored after a dom operation.
 
40
                 *
 
41
                 * So this: <p><b>|</b><b>|</b></p>
 
42
                 * becomes: <p><b><span data-mce-type="bookmark">|</span></b><b data-mce-type="bookmark">|</span></b></p>
 
43
                 *
 
44
                 * @param  {DOMRange} rng DOM Range to get bookmark on.
 
45
                 * @return {Object} Bookmark object.
 
46
                 */
 
47
                function createBookmark(rng) {
 
48
                        var bookmark = {};
 
49
 
 
50
                        function setupEndPoint(start) {
 
51
                                var offsetNode, container, offset;
 
52
 
 
53
                                container = rng[start ? 'startContainer' : 'endContainer'];
 
54
                                offset = rng[start ? 'startOffset' : 'endOffset'];
 
55
 
 
56
                                if (container.nodeType == 1) {
 
57
                                        offsetNode = dom.create('span', {'data-mce-type': 'bookmark'});
 
58
 
 
59
                                        if (container.hasChildNodes()) {
 
60
                                                offset = Math.min(offset, container.childNodes.length - 1);
 
61
 
 
62
                                                if (start) {
 
63
                                                        container.insertBefore(offsetNode, container.childNodes[offset]);
 
64
                                                } else {
 
65
                                                        dom.insertAfter(offsetNode, container.childNodes[offset]);
 
66
                                                }
 
67
                                        } else {
 
68
                                                container.appendChild(offsetNode);
 
69
                                        }
 
70
 
 
71
                                        container = offsetNode;
 
72
                                        offset = 0;
 
73
                                }
 
74
 
 
75
                                bookmark[start ? 'startContainer' : 'endContainer'] = container;
 
76
                                bookmark[start ? 'startOffset' : 'endOffset'] = offset;
 
77
                        }
 
78
 
 
79
                        setupEndPoint(true);
 
80
 
 
81
                        if (!rng.collapsed) {
 
82
                                setupEndPoint();
 
83
                        }
 
84
 
 
85
                        return bookmark;
 
86
                }
 
87
 
 
88
                /**
 
89
                 * Moves the selection to the current bookmark and removes any selection container wrappers.
 
90
                 *
 
91
                 * @param {Object} bookmark Bookmark object to move selection to.
 
92
                 */
 
93
                function moveToBookmark(bookmark) {
 
94
                        function restoreEndPoint(start) {
 
95
                                var container, offset, node;
 
96
 
 
97
                                function nodeIndex(container) {
 
98
                                        var node = container.parentNode.firstChild, idx = 0;
 
99
 
 
100
                                        while (node) {
 
101
                                                if (node == container) {
 
102
                                                        return idx;
 
103
                                                }
 
104
 
 
105
                                                // Skip data-mce-type=bookmark nodes
 
106
                                                if (node.nodeType != 1 || node.getAttribute('data-mce-type') != 'bookmark') {
 
107
                                                        idx++;
 
108
                                                }
 
109
 
 
110
                                                node = node.nextSibling;
 
111
                                        }
 
112
 
 
113
                                        return -1;
 
114
                                }
 
115
 
 
116
                                container = node = bookmark[start ? 'startContainer' : 'endContainer'];
 
117
                                offset = bookmark[start ? 'startOffset' : 'endOffset'];
 
118
 
 
119
                                if (!container) {
 
120
                                        return;
 
121
                                }
 
122
 
 
123
                                if (container.nodeType == 1) {
 
124
                                        offset = nodeIndex(container);
 
125
                                        container = container.parentNode;
 
126
                                        dom.remove(node);
 
127
                                }
 
128
 
 
129
                                bookmark[start ? 'startContainer' : 'endContainer'] = container;
 
130
                                bookmark[start ? 'startOffset' : 'endOffset'] = offset;
 
131
                        }
 
132
 
 
133
                        restoreEndPoint(true);
 
134
                        restoreEndPoint();
 
135
 
 
136
                        var rng = dom.createRng();
 
137
 
 
138
                        rng.setStart(bookmark.startContainer, bookmark.startOffset);
 
139
 
 
140
                        if (bookmark.endContainer) {
 
141
                                rng.setEnd(bookmark.endContainer, bookmark.endOffset);
 
142
                        }
 
143
 
 
144
                        selection.setRng(rng);
 
145
                }
 
146
 
 
147
                function createNewTextBlock(contentNode, blockName) {
 
148
                        var node, textBlock, fragment = dom.createFragment(), hasContentNode;
 
149
                        var blockElements = editor.schema.getBlockElements();
 
150
 
 
151
                        if (editor.settings.forced_root_block) {
 
152
                                blockName = blockName || editor.settings.forced_root_block;
 
153
                        }
 
154
 
 
155
                        if (blockName) {
 
156
                                textBlock = dom.create(blockName);
 
157
 
 
158
                                if (textBlock.tagName === editor.settings.forced_root_block) {
 
159
                                        dom.setAttribs(textBlock, editor.settings.forced_root_block_attrs);
 
160
                                }
 
161
 
 
162
                                fragment.appendChild(textBlock);
 
163
                        }
 
164
 
 
165
                        if (contentNode) {
 
166
                                while ((node = contentNode.firstChild)) {
 
167
                                        var nodeName = node.nodeName;
 
168
 
 
169
                                        if (!hasContentNode && (nodeName != 'SPAN' || node.getAttribute('data-mce-type') != 'bookmark')) {
 
170
                                                hasContentNode = true;
 
171
                                        }
 
172
 
 
173
                                        if (blockElements[nodeName]) {
 
174
                                                fragment.appendChild(node);
 
175
                                                textBlock = null;
 
176
                                        } else {
 
177
                                                if (blockName) {
 
178
                                                        if (!textBlock) {
 
179
                                                                textBlock = dom.create(blockName);
 
180
                                                                fragment.appendChild(textBlock);
 
181
                                                        }
 
182
 
 
183
                                                        textBlock.appendChild(node);
 
184
                                                } else {
 
185
                                                        fragment.appendChild(node);
 
186
                                                }
 
187
                                        }
 
188
                                }
 
189
                        }
 
190
 
 
191
                        if (!editor.settings.forced_root_block) {
 
192
                                fragment.appendChild(dom.create('br'));
 
193
                        } else {
 
194
                                // BR is needed in empty blocks on non IE browsers
 
195
                                if (!hasContentNode && (!tinymce.Env.ie || tinymce.Env.ie > 10)) {
 
196
                                        textBlock.appendChild(dom.create('br', {'data-mce-bogus': '1'}));
 
197
                                }
 
198
                        }
 
199
 
 
200
                        return fragment;
 
201
                }
 
202
 
 
203
                function getSelectedListItems() {
 
204
                        return tinymce.grep(selection.getSelectedBlocks(), function(block) {
 
205
                                return /^(LI|DT|DD)$/.test(block.nodeName);
 
206
                        });
 
207
                }
 
208
 
 
209
                function splitList(ul, li, newBlock) {
 
210
                        var tmpRng, fragment;
 
211
 
 
212
                        var bookmarks = dom.select('span[data-mce-type="bookmark"]', ul);
 
213
 
 
214
                        newBlock = newBlock || createNewTextBlock(li);
 
215
                        tmpRng = dom.createRng();
 
216
                        tmpRng.setStartAfter(li);
 
217
                        tmpRng.setEndAfter(ul);
 
218
                        fragment = tmpRng.extractContents();
 
219
 
 
220
                        if (!dom.isEmpty(fragment)) {
 
221
                                dom.insertAfter(fragment, ul);
 
222
                        }
 
223
 
 
224
                        dom.insertAfter(newBlock, ul);
 
225
 
 
226
                        if (dom.isEmpty(li.parentNode)) {
 
227
                                tinymce.each(bookmarks, function(node) {
 
228
                                        li.parentNode.parentNode.insertBefore(node, li.parentNode);
 
229
                                });
 
230
 
 
231
                                dom.remove(li.parentNode);
 
232
                        }
 
233
 
 
234
                        dom.remove(li);
 
235
                }
 
236
 
 
237
                function mergeWithAdjacentLists(listBlock) {
 
238
                        var sibling, node;
 
239
 
 
240
                        sibling = listBlock.nextSibling;
 
241
                        if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
 
242
                                while ((node = sibling.firstChild)) {
 
243
                                        listBlock.appendChild(node);
 
244
                                }
 
245
 
 
246
                                dom.remove(sibling);
 
247
                        }
 
248
 
 
249
                        sibling = listBlock.previousSibling;
 
250
                        if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
 
251
                                while ((node = sibling.firstChild)) {
 
252
                                        listBlock.insertBefore(node, listBlock.firstChild);
 
253
                                }
 
254
 
 
255
                                dom.remove(sibling);
 
256
                        }
 
257
                }
 
258
 
 
259
                /**
 
260
                 * Normalizes the all lists in the specified element.
 
261
                 */
 
262
                function normalizeList(element) {
 
263
                        tinymce.each(tinymce.grep(dom.select('ol,ul', element)), function(ul) {
 
264
                                var sibling, parentNode = ul.parentNode;
 
265
 
 
266
                                // Move UL/OL to previous LI if it's the only child of a LI
 
267
                                if (parentNode.nodeName == 'LI' && parentNode.firstChild == ul) {
 
268
                                        sibling = parentNode.previousSibling;
 
269
                                        if (sibling && sibling.nodeName == 'LI') {
 
270
                                                sibling.appendChild(ul);
 
271
 
 
272
                                                if (dom.isEmpty(parentNode)) {
 
273
                                                        dom.remove(parentNode);
 
274
                                                }
 
275
                                        }
 
276
                                }
 
277
 
 
278
                                // Append OL/UL to previous LI if it's in a parent OL/UL i.e. old HTML4
 
279
                                if (isListNode(parentNode)) {
 
280
                                        sibling = parentNode.previousSibling;
 
281
                                        if (sibling && sibling.nodeName == 'LI') {
 
282
                                                sibling.appendChild(ul);
 
283
                                        }
 
284
                                }
 
285
                        });
 
286
                }
 
287
 
 
288
                function outdent(li) {
 
289
                        var ul = li.parentNode, ulParent = ul.parentNode, newBlock;
 
290
 
 
291
                        function removeEmptyLi(li) {
 
292
                                if (dom.isEmpty(li)) {
 
293
                                        dom.remove(li);
 
294
                                }
 
295
                        }
 
296
 
 
297
                        if (li.nodeName == 'DD') {
 
298
                                dom.rename(li, 'DT');
 
299
                                return true;
 
300
                        }
 
301
 
 
302
                        if (isFirstChild(li) && isLastChild(li)) {
 
303
                                if (ulParent.nodeName == "LI") {
 
304
                                        dom.insertAfter(li, ulParent);
 
305
                                        removeEmptyLi(ulParent);
 
306
                                        dom.remove(ul);
 
307
                                } else if (isListNode(ulParent)) {
 
308
                                        dom.remove(ul, true);
 
309
                                } else {
 
310
                                        ulParent.insertBefore(createNewTextBlock(li), ul);
 
311
                                        dom.remove(ul);
 
312
                                }
 
313
 
 
314
                                return true;
 
315
                        } else if (isFirstChild(li)) {
 
316
                                if (ulParent.nodeName == "LI") {
 
317
                                        dom.insertAfter(li, ulParent);
 
318
                                        li.appendChild(ul);
 
319
                                        removeEmptyLi(ulParent);
 
320
                                } else if (isListNode(ulParent)) {
 
321
                                        ulParent.insertBefore(li, ul);
 
322
                                } else {
 
323
                                        ulParent.insertBefore(createNewTextBlock(li), ul);
 
324
                                        dom.remove(li);
 
325
                                }
 
326
 
 
327
                                return true;
 
328
                        } else if (isLastChild(li)) {
 
329
                                if (ulParent.nodeName == "LI") {
 
330
                                        dom.insertAfter(li, ulParent);
 
331
                                } else if (isListNode(ulParent)) {
 
332
                                        dom.insertAfter(li, ul);
 
333
                                } else {
 
334
                                        dom.insertAfter(createNewTextBlock(li), ul);
 
335
                                        dom.remove(li);
 
336
                                }
 
337
 
 
338
                                return true;
 
339
                        } else {
 
340
                                if (ulParent.nodeName == 'LI') {
 
341
                                        ul = ulParent;
 
342
                                        newBlock = createNewTextBlock(li, 'LI');
 
343
                                } else if (isListNode(ulParent)) {
 
344
                                        newBlock = createNewTextBlock(li, 'LI');
 
345
                                } else {
 
346
                                        newBlock = createNewTextBlock(li);
 
347
                                }
 
348
 
 
349
                                splitList(ul, li, newBlock);
 
350
                                normalizeList(ul.parentNode);
 
351
 
 
352
                                return true;
 
353
                        }
 
354
 
 
355
                        return false;
 
356
                }
 
357
 
 
358
                function indent(li) {
 
359
                        var sibling, newList;
 
360
 
 
361
                        function mergeLists(from, to) {
 
362
                                var node;
 
363
 
 
364
                                if (isListNode(from)) {
 
365
                                        while ((node = li.lastChild.firstChild)) {
 
366
                                                to.appendChild(node);
 
367
                                        }
 
368
 
 
369
                                        dom.remove(from);
 
370
                                }
 
371
                        }
 
372
 
 
373
                        if (li.nodeName == 'DT') {
 
374
                                dom.rename(li, 'DD');
 
375
                                return true;
 
376
                        }
 
377
 
 
378
                        sibling = li.previousSibling;
 
379
 
 
380
                        if (sibling && isListNode(sibling)) {
 
381
                                sibling.appendChild(li);
 
382
                                return true;
 
383
                        }
 
384
 
 
385
                        if (sibling && sibling.nodeName == 'LI' && isListNode(sibling.lastChild)) {
 
386
                                sibling.lastChild.appendChild(li);
 
387
                                mergeLists(li.lastChild, sibling.lastChild);
 
388
                                return true;
 
389
                        }
 
390
 
 
391
                        sibling = li.nextSibling;
 
392
 
 
393
                        if (sibling && isListNode(sibling)) {
 
394
                                sibling.insertBefore(li, sibling.firstChild);
 
395
                                return true;
 
396
                        }
 
397
 
 
398
                        if (sibling && sibling.nodeName == 'LI' && isListNode(li.lastChild)) {
 
399
                                return false;
 
400
                        }
 
401
 
 
402
                        sibling = li.previousSibling;
 
403
                        if (sibling && sibling.nodeName == 'LI') {
 
404
                                newList = dom.create(li.parentNode.nodeName);
 
405
                                sibling.appendChild(newList);
 
406
                                newList.appendChild(li);
 
407
                                mergeLists(li.lastChild, newList);
 
408
                                return true;
 
409
                        }
 
410
 
 
411
                        return false;
 
412
                }
 
413
 
 
414
                function indentSelection() {
 
415
                        var listElements = getSelectedListItems();
 
416
 
 
417
                        if (listElements.length) {
 
418
                                var bookmark = createBookmark(selection.getRng(true));
 
419
 
 
420
                                for (var i = 0; i < listElements.length; i++) {
 
421
                                        if (!indent(listElements[i]) && i === 0) {
 
422
                                                break;
 
423
                                        }
 
424
                                }
 
425
 
 
426
                                moveToBookmark(bookmark);
 
427
                                editor.nodeChanged();
 
428
 
 
429
                                return true;
 
430
                        }
 
431
                }
 
432
 
 
433
                function outdentSelection() {
 
434
                        var listElements = getSelectedListItems();
 
435
 
 
436
                        if (listElements.length) {
 
437
                                var bookmark = createBookmark(selection.getRng(true));
 
438
                                var i, y, root = editor.getBody();
 
439
 
 
440
                                i = listElements.length;
 
441
                                while (i--) {
 
442
                                        var node = listElements[i].parentNode;
 
443
 
 
444
                                        while (node && node != root) {
 
445
                                                y = listElements.length;
 
446
                                                while (y--) {
 
447
                                                        if (listElements[y] === node) {
 
448
                                                                listElements.splice(i, 1);
 
449
                                                                break;
 
450
                                                        }
 
451
                                                }
 
452
 
 
453
                                                node = node.parentNode;
 
454
                                        }
 
455
                                }
 
456
 
 
457
                                for (i = 0; i < listElements.length; i++) {
 
458
                                        if (!outdent(listElements[i]) && i === 0) {
 
459
                                                break;
 
460
                                        }
 
461
                                }
 
462
 
 
463
                                moveToBookmark(bookmark);
 
464
                                editor.nodeChanged();
 
465
 
 
466
                                return true;
 
467
                        }
 
468
                }
 
469
 
 
470
                function applyList(listName) {
 
471
                        var rng = selection.getRng(true), bookmark = createBookmark(rng), listItemName = 'LI';
 
472
 
 
473
                        listName = listName.toUpperCase();
 
474
 
 
475
                        if (listName == 'DL') {
 
476
                                listItemName = 'DT';
 
477
                        }
 
478
 
 
479
                        function getSelectedTextBlocks() {
 
480
                                var textBlocks = [], root = editor.getBody();
 
481
 
 
482
                                function getEndPointNode(start) {
 
483
                                        var container, offset;
 
484
 
 
485
                                        container = rng[start ? 'startContainer' : 'endContainer'];
 
486
                                        offset = rng[start ? 'startOffset' : 'endOffset'];
 
487
 
 
488
                                        // Resolve node index
 
489
                                        if (container.nodeType == 1) {
 
490
                                                container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
 
491
                                        }
 
492
 
 
493
                                        while (container.parentNode != root) {
 
494
                                                if (isTextBlock(container)) {
 
495
                                                        return container;
 
496
                                                }
 
497
 
 
498
                                                if (/^(TD|TH)$/.test(container.parentNode.nodeName)) {
 
499
                                                        return container;
 
500
                                                }
 
501
 
 
502
                                                container = container.parentNode;
 
503
                                        }
 
504
 
 
505
                                        return container;
 
506
                                }
 
507
 
 
508
                                var startNode = getEndPointNode(true);
 
509
                                var endNode = getEndPointNode();
 
510
                                var block, siblings = [];
 
511
 
 
512
                                for (var node = startNode; node; node = node.nextSibling) {
 
513
                                        siblings.push(node);
 
514
 
 
515
                                        if (node == endNode) {
 
516
                                                break;
 
517
                                        }
 
518
                                }
 
519
 
 
520
                                tinymce.each(siblings, function(node) {
 
521
                                        if (isTextBlock(node)) {
 
522
                                                textBlocks.push(node);
 
523
                                                block = null;
 
524
                                                return;
 
525
                                        }
 
526
 
 
527
                                        if (dom.isBlock(node) || node.nodeName == 'BR') {
 
528
                                                if (node.nodeName == 'BR') {
 
529
                                                        dom.remove(node);
 
530
                                                }
 
531
 
 
532
                                                block = null;
 
533
                                                return;
 
534
                                        }
 
535
 
 
536
                                        var nextSibling = node.nextSibling;
 
537
                                        if (tinymce.dom.BookmarkManager.isBookmarkNode(node)) {
 
538
                                                if (isTextBlock(nextSibling) || (!nextSibling && node.parentNode == root)) {
 
539
                                                        block = null;
 
540
                                                        return;
 
541
                                                }
 
542
                                        }
 
543
 
 
544
                                        if (!block) {
 
545
                                                block = dom.create('p');
 
546
                                                node.parentNode.insertBefore(block, node);
 
547
                                                textBlocks.push(block);
 
548
                                        }
 
549
 
 
550
                                        block.appendChild(node);
 
551
                                });
 
552
 
 
553
                                return textBlocks;
 
554
                        }
 
555
 
 
556
                        tinymce.each(getSelectedTextBlocks(), function(block) {
 
557
                                var listBlock, sibling;
 
558
 
 
559
                                sibling = block.previousSibling;
 
560
                                if (sibling && isListNode(sibling) && sibling.nodeName == listName) {
 
561
                                        listBlock = sibling;
 
562
                                        block = dom.rename(block, listItemName);
 
563
                                        sibling.appendChild(block);
 
564
                                } else {
 
565
                                        listBlock = dom.create(listName);
 
566
                                        block.parentNode.insertBefore(listBlock, block);
 
567
                                        listBlock.appendChild(block);
 
568
                                        block = dom.rename(block, listItemName);
 
569
                                }
 
570
 
 
571
                                mergeWithAdjacentLists(listBlock);
 
572
                        });
 
573
 
 
574
                        moveToBookmark(bookmark);
 
575
                }
 
576
 
 
577
                function removeList() {
 
578
                        var bookmark = createBookmark(selection.getRng(true)), root = editor.getBody();
 
579
 
 
580
                        tinymce.each(getSelectedListItems(), function(li) {
 
581
                                var node, rootList;
 
582
 
 
583
                                if (dom.isEmpty(li)) {
 
584
                                        outdent(li);
 
585
                                        return;
 
586
                                }
 
587
 
 
588
                                for (node = li; node && node != root; node = node.parentNode) {
 
589
                                        if (isListNode(node)) {
 
590
                                                rootList = node;
 
591
                                        }
 
592
                                }
 
593
 
 
594
                                splitList(rootList, li);
 
595
                        });
 
596
 
 
597
                        moveToBookmark(bookmark);
 
598
                }
 
599
 
 
600
                function toggleList(listName) {
 
601
                        var parentList = dom.getParent(selection.getStart(), 'OL,UL,DL');
 
602
 
 
603
                        if (parentList) {
 
604
                                if (parentList.nodeName == listName) {
 
605
                                        removeList(listName);
 
606
                                } else {
 
607
                                        var bookmark = createBookmark(selection.getRng(true));
 
608
                                        mergeWithAdjacentLists(dom.rename(parentList, listName));
 
609
                                        moveToBookmark(bookmark);
 
610
                                }
 
611
                        } else {
 
612
                                applyList(listName);
 
613
                        }
 
614
                }
 
615
 
 
616
                function queryListCommandState(listName) {
 
617
                        return function() {
 
618
                                var parentList = dom.getParent(editor.selection.getStart(), 'UL,OL,DL');
 
619
 
 
620
                                return parentList && parentList.nodeName == listName;
 
621
                        };
 
622
                }
 
623
 
 
624
                self.backspaceDelete = function(isForward) {
 
625
                        function findNextCaretContainer(rng, isForward) {
 
626
                                var node = rng.startContainer, offset = rng.startOffset;
 
627
                                var nonEmptyBlocks, walker;
 
628
 
 
629
                                if (node.nodeType == 3 && (isForward ? offset < node.data.length : offset > 0)) {
 
630
                                        return node;
 
631
                                }
 
632
 
 
633
                                nonEmptyBlocks = editor.schema.getNonEmptyElements();
 
634
                                walker = new tinymce.dom.TreeWalker(rng.startContainer);
 
635
 
 
636
                                while ((node = walker[isForward ? 'next' : 'prev']())) {
 
637
                                        if (node.nodeName == 'LI' && !node.hasChildNodes()) {
 
638
                                                return node;
 
639
                                        }
 
640
 
 
641
                                        if (nonEmptyBlocks[node.nodeName]) {
 
642
                                                return node;
 
643
                                        }
 
644
 
 
645
                                        if (node.nodeType == 3 && node.data.length > 0) {
 
646
                                                return node;
 
647
                                        }
 
648
                                }
 
649
                        }
 
650
 
 
651
                        function mergeLiElements(fromElm, toElm) {
 
652
                                var node, listNode, ul = fromElm.parentNode;
 
653
 
 
654
                                if (isListNode(toElm.lastChild)) {
 
655
                                        listNode = toElm.lastChild;
 
656
                                }
 
657
 
 
658
                                node = toElm.lastChild;
 
659
                                if (node && node.nodeName == 'BR' && fromElm.hasChildNodes()) {
 
660
                                        dom.remove(node);
 
661
                                }
 
662
 
 
663
                                if (dom.isEmpty(toElm)) {
 
664
                                        dom.$(toElm).empty();
 
665
                                }
 
666
 
 
667
                                if (!dom.isEmpty(fromElm)) {
 
668
                                        while ((node = fromElm.firstChild)) {
 
669
                                                toElm.appendChild(node);
 
670
                                        }
 
671
                                }
 
672
 
 
673
                                if (listNode) {
 
674
                                        toElm.appendChild(listNode);
 
675
                                }
 
676
 
 
677
                                dom.remove(fromElm);
 
678
 
 
679
                                if (dom.isEmpty(ul)) {
 
680
                                        dom.remove(ul);
 
681
                                }
 
682
                        }
 
683
 
 
684
                        if (selection.isCollapsed()) {
 
685
                                var li = dom.getParent(selection.getStart(), 'LI');
 
686
 
 
687
                                if (li) {
 
688
                                        var rng = selection.getRng(true);
 
689
                                        var otherLi = dom.getParent(findNextCaretContainer(rng, isForward), 'LI');
 
690
 
 
691
                                        if (otherLi && otherLi != li) {
 
692
                                                var bookmark = createBookmark(rng);
 
693
 
 
694
                                                if (isForward) {
 
695
                                                        mergeLiElements(otherLi, li);
 
696
                                                } else {
 
697
                                                        mergeLiElements(li, otherLi);
 
698
                                                }
 
699
 
 
700
                                                moveToBookmark(bookmark);
 
701
 
 
702
                                                return true;
 
703
                                        } else if (!otherLi) {
 
704
                                                if (!isForward && removeList(li.parentNode.nodeName)) {
 
705
                                                        return true;
 
706
                                                }
 
707
                                        }
 
708
                                }
 
709
                        }
 
710
                };
 
711
 
 
712
                editor.addCommand('Indent', function() {
 
713
                        if (!indentSelection()) {
 
714
                                return true;
 
715
                        }
 
716
                });
 
717
 
 
718
                editor.addCommand('Outdent', function() {
 
719
                        if (!outdentSelection()) {
 
720
                                return true;
 
721
                        }
 
722
                });
 
723
 
 
724
                editor.addCommand('InsertUnorderedList', function() {
 
725
                        toggleList('UL');
 
726
                });
 
727
 
 
728
                editor.addCommand('InsertOrderedList', function() {
 
729
                        toggleList('OL');
 
730
                });
 
731
 
 
732
                editor.addCommand('InsertDefinitionList', function() {
 
733
                        toggleList('DL');
 
734
                });
 
735
 
 
736
                editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState('UL'));
 
737
                editor.addQueryStateHandler('InsertOrderedList', queryListCommandState('OL'));
 
738
                editor.addQueryStateHandler('InsertDefinitionList', queryListCommandState('DL'));
 
739
 
 
740
                editor.on('keydown', function(e) {
 
741
                        if (e.keyCode == 9 && editor.dom.getParent(editor.selection.getStart(), 'LI,DT,DD')) {
 
742
                                e.preventDefault();
 
743
 
 
744
                                if (e.shiftKey) {
 
745
                                        outdentSelection();
 
746
                                } else {
 
747
                                        indentSelection();
 
748
                                }
 
749
                        }
 
750
                });
 
751
        });
 
752
 
 
753
        editor.addButton('indent', {
 
754
                icon: 'indent',
 
755
                title: 'Increase indent',
 
756
                cmd: 'Indent',
 
757
                onPostRender: function() {
 
758
                        var ctrl = this;
 
759
 
 
760
                        editor.on('nodechange', function() {
 
761
                                var blocks = editor.selection.getSelectedBlocks();
 
762
                                var disable = false;
 
763
 
 
764
                                for (var i = 0, l = blocks.length; !disable && i < l; i++) {
 
765
                                        var tag = blocks[i].nodeName;
 
766
 
 
767
                                        disable = (tag == 'LI' && isFirstChild(blocks[i]) || tag == 'UL' || tag == 'OL' || tag == 'DD');
 
768
                                }
 
769
 
 
770
                                ctrl.disabled(disable);
 
771
                        });
 
772
                }
 
773
        });
 
774
 
 
775
        editor.on('keydown', function(e) {
 
776
                if (e.keyCode == tinymce.util.VK.BACKSPACE) {
 
777
                        if (self.backspaceDelete()) {
 
778
                                e.preventDefault();
 
779
                        }
 
780
                } else if (e.keyCode == tinymce.util.VK.DELETE) {
 
781
                        if (self.backspaceDelete(true)) {
 
782
                                e.preventDefault();
 
783
                        }
 
784
                }
 
785
        });
 
786
});