]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/tinymce/plugins/lists/plugin.js
WordPress 4.2-scripts
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / plugins / lists / plugin.js
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, bookmarks, node;
211
212                         function removeAndKeepBookmarks(targetNode) {
213                                 tinymce.each(bookmarks, function(node) {
214                                         targetNode.parentNode.insertBefore(node, li.parentNode);
215                                 });
216
217                                 dom.remove(targetNode);
218                         }
219
220                         bookmarks = dom.select('span[data-mce-type="bookmark"]', ul);
221                         newBlock = newBlock || createNewTextBlock(li);
222                         tmpRng = dom.createRng();
223                         tmpRng.setStartAfter(li);
224                         tmpRng.setEndAfter(ul);
225                         fragment = tmpRng.extractContents();
226
227                         for (node = fragment.firstChild; node; node = node.firstChild) {
228                                 if (node.nodeName == 'LI' && dom.isEmpty(node)) {
229                                         dom.remove(node);
230                                         break;
231                                 }
232                         }
233
234                         if (!dom.isEmpty(fragment)) {
235                                 dom.insertAfter(fragment, ul);
236                         }
237
238                         dom.insertAfter(newBlock, ul);
239
240                         if (dom.isEmpty(li.parentNode)) {
241                                 removeAndKeepBookmarks(li.parentNode);
242                         }
243
244                         dom.remove(li);
245
246                         if (dom.isEmpty(ul)) {
247                                 dom.remove(ul);
248                         }
249                 }
250
251                 function mergeWithAdjacentLists(listBlock) {
252                         var sibling, node;
253
254                         sibling = listBlock.nextSibling;
255                         if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
256                                 while ((node = sibling.firstChild)) {
257                                         listBlock.appendChild(node);
258                                 }
259
260                                 dom.remove(sibling);
261                         }
262
263                         sibling = listBlock.previousSibling;
264                         if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
265                                 while ((node = sibling.firstChild)) {
266                                         listBlock.insertBefore(node, listBlock.firstChild);
267                                 }
268
269                                 dom.remove(sibling);
270                         }
271                 }
272
273                 /**
274                  * Normalizes the all lists in the specified element.
275                  */
276                 function normalizeList(element) {
277                         tinymce.each(tinymce.grep(dom.select('ol,ul', element)), function(ul) {
278                                 var sibling, parentNode = ul.parentNode;
279
280                                 // Move UL/OL to previous LI if it's the only child of a LI
281                                 if (parentNode.nodeName == 'LI' && parentNode.firstChild == ul) {
282                                         sibling = parentNode.previousSibling;
283                                         if (sibling && sibling.nodeName == 'LI') {
284                                                 sibling.appendChild(ul);
285
286                                                 if (dom.isEmpty(parentNode)) {
287                                                         dom.remove(parentNode);
288                                                 }
289                                         }
290                                 }
291
292                                 // Append OL/UL to previous LI if it's in a parent OL/UL i.e. old HTML4
293                                 if (isListNode(parentNode)) {
294                                         sibling = parentNode.previousSibling;
295                                         if (sibling && sibling.nodeName == 'LI') {
296                                                 sibling.appendChild(ul);
297                                         }
298                                 }
299                         });
300                 }
301
302                 function outdent(li) {
303                         var ul = li.parentNode, ulParent = ul.parentNode, newBlock;
304
305                         function removeEmptyLi(li) {
306                                 if (dom.isEmpty(li)) {
307                                         dom.remove(li);
308                                 }
309                         }
310
311                         if (li.nodeName == 'DD') {
312                                 dom.rename(li, 'DT');
313                                 return true;
314                         }
315
316                         if (isFirstChild(li) && isLastChild(li)) {
317                                 if (ulParent.nodeName == "LI") {
318                                         dom.insertAfter(li, ulParent);
319                                         removeEmptyLi(ulParent);
320                                         dom.remove(ul);
321                                 } else if (isListNode(ulParent)) {
322                                         dom.remove(ul, true);
323                                 } else {
324                                         ulParent.insertBefore(createNewTextBlock(li), ul);
325                                         dom.remove(ul);
326                                 }
327
328                                 return true;
329                         } else if (isFirstChild(li)) {
330                                 if (ulParent.nodeName == "LI") {
331                                         dom.insertAfter(li, ulParent);
332                                         li.appendChild(ul);
333                                         removeEmptyLi(ulParent);
334                                 } else if (isListNode(ulParent)) {
335                                         ulParent.insertBefore(li, ul);
336                                 } else {
337                                         ulParent.insertBefore(createNewTextBlock(li), ul);
338                                         dom.remove(li);
339                                 }
340
341                                 return true;
342                         } else if (isLastChild(li)) {
343                                 if (ulParent.nodeName == "LI") {
344                                         dom.insertAfter(li, ulParent);
345                                 } else if (isListNode(ulParent)) {
346                                         dom.insertAfter(li, ul);
347                                 } else {
348                                         dom.insertAfter(createNewTextBlock(li), ul);
349                                         dom.remove(li);
350                                 }
351
352                                 return true;
353                         } else {
354                                 if (ulParent.nodeName == 'LI') {
355                                         ul = ulParent;
356                                         newBlock = createNewTextBlock(li, 'LI');
357                                 } else if (isListNode(ulParent)) {
358                                         newBlock = createNewTextBlock(li, 'LI');
359                                 } else {
360                                         newBlock = createNewTextBlock(li);
361                                 }
362
363                                 splitList(ul, li, newBlock);
364                                 normalizeList(ul.parentNode);
365
366                                 return true;
367                         }
368
369                         return false;
370                 }
371
372                 function indent(li) {
373                         var sibling, newList;
374
375                         function mergeLists(from, to) {
376                                 var node;
377
378                                 if (isListNode(from)) {
379                                         while ((node = li.lastChild.firstChild)) {
380                                                 to.appendChild(node);
381                                         }
382
383                                         dom.remove(from);
384                                 }
385                         }
386
387                         if (li.nodeName == 'DT') {
388                                 dom.rename(li, 'DD');
389                                 return true;
390                         }
391
392                         sibling = li.previousSibling;
393
394                         if (sibling && isListNode(sibling)) {
395                                 sibling.appendChild(li);
396                                 return true;
397                         }
398
399                         if (sibling && sibling.nodeName == 'LI' && isListNode(sibling.lastChild)) {
400                                 sibling.lastChild.appendChild(li);
401                                 mergeLists(li.lastChild, sibling.lastChild);
402                                 return true;
403                         }
404
405                         sibling = li.nextSibling;
406
407                         if (sibling && isListNode(sibling)) {
408                                 sibling.insertBefore(li, sibling.firstChild);
409                                 return true;
410                         }
411
412                         if (sibling && sibling.nodeName == 'LI' && isListNode(li.lastChild)) {
413                                 return false;
414                         }
415
416                         sibling = li.previousSibling;
417                         if (sibling && sibling.nodeName == 'LI') {
418                                 newList = dom.create(li.parentNode.nodeName);
419                                 sibling.appendChild(newList);
420                                 newList.appendChild(li);
421                                 mergeLists(li.lastChild, newList);
422                                 return true;
423                         }
424
425                         return false;
426                 }
427
428                 function indentSelection() {
429                         var listElements = getSelectedListItems();
430
431                         if (listElements.length) {
432                                 var bookmark = createBookmark(selection.getRng(true));
433
434                                 for (var i = 0; i < listElements.length; i++) {
435                                         if (!indent(listElements[i]) && i === 0) {
436                                                 break;
437                                         }
438                                 }
439
440                                 moveToBookmark(bookmark);
441                                 editor.nodeChanged();
442
443                                 return true;
444                         }
445                 }
446
447                 function outdentSelection() {
448                         var listElements = getSelectedListItems();
449
450                         if (listElements.length) {
451                                 var bookmark = createBookmark(selection.getRng(true));
452                                 var i, y, root = editor.getBody();
453
454                                 i = listElements.length;
455                                 while (i--) {
456                                         var node = listElements[i].parentNode;
457
458                                         while (node && node != root) {
459                                                 y = listElements.length;
460                                                 while (y--) {
461                                                         if (listElements[y] === node) {
462                                                                 listElements.splice(i, 1);
463                                                                 break;
464                                                         }
465                                                 }
466
467                                                 node = node.parentNode;
468                                         }
469                                 }
470
471                                 for (i = 0; i < listElements.length; i++) {
472                                         if (!outdent(listElements[i]) && i === 0) {
473                                                 break;
474                                         }
475                                 }
476
477                                 moveToBookmark(bookmark);
478                                 editor.nodeChanged();
479
480                                 return true;
481                         }
482                 }
483
484                 function applyList(listName) {
485                         var rng = selection.getRng(true), bookmark = createBookmark(rng), listItemName = 'LI';
486
487                         listName = listName.toUpperCase();
488
489                         if (listName == 'DL') {
490                                 listItemName = 'DT';
491                         }
492
493                         function getSelectedTextBlocks() {
494                                 var textBlocks = [], root = editor.getBody();
495
496                                 function getEndPointNode(start) {
497                                         var container, offset;
498
499                                         container = rng[start ? 'startContainer' : 'endContainer'];
500                                         offset = rng[start ? 'startOffset' : 'endOffset'];
501
502                                         // Resolve node index
503                                         if (container.nodeType == 1) {
504                                                 container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
505                                         }
506
507                                         while (container.parentNode != root) {
508                                                 if (isTextBlock(container)) {
509                                                         return container;
510                                                 }
511
512                                                 if (/^(TD|TH)$/.test(container.parentNode.nodeName)) {
513                                                         return container;
514                                                 }
515
516                                                 container = container.parentNode;
517                                         }
518
519                                         return container;
520                                 }
521
522                                 var startNode = getEndPointNode(true);
523                                 var endNode = getEndPointNode();
524                                 var block, siblings = [];
525
526                                 for (var node = startNode; node; node = node.nextSibling) {
527                                         siblings.push(node);
528
529                                         if (node == endNode) {
530                                                 break;
531                                         }
532                                 }
533
534                                 tinymce.each(siblings, function(node) {
535                                         if (isTextBlock(node)) {
536                                                 textBlocks.push(node);
537                                                 block = null;
538                                                 return;
539                                         }
540
541                                         if (dom.isBlock(node) || node.nodeName == 'BR') {
542                                                 if (node.nodeName == 'BR') {
543                                                         dom.remove(node);
544                                                 }
545
546                                                 block = null;
547                                                 return;
548                                         }
549
550                                         var nextSibling = node.nextSibling;
551                                         if (tinymce.dom.BookmarkManager.isBookmarkNode(node)) {
552                                                 if (isTextBlock(nextSibling) || (!nextSibling && node.parentNode == root)) {
553                                                         block = null;
554                                                         return;
555                                                 }
556                                         }
557
558                                         if (!block) {
559                                                 block = dom.create('p');
560                                                 node.parentNode.insertBefore(block, node);
561                                                 textBlocks.push(block);
562                                         }
563
564                                         block.appendChild(node);
565                                 });
566
567                                 return textBlocks;
568                         }
569
570                         tinymce.each(getSelectedTextBlocks(), function(block) {
571                                 var listBlock, sibling;
572
573                                 sibling = block.previousSibling;
574                                 if (sibling && isListNode(sibling) && sibling.nodeName == listName) {
575                                         listBlock = sibling;
576                                         block = dom.rename(block, listItemName);
577                                         sibling.appendChild(block);
578                                 } else {
579                                         listBlock = dom.create(listName);
580                                         block.parentNode.insertBefore(listBlock, block);
581                                         listBlock.appendChild(block);
582                                         block = dom.rename(block, listItemName);
583                                 }
584
585                                 mergeWithAdjacentLists(listBlock);
586                         });
587
588                         moveToBookmark(bookmark);
589                 }
590
591                 function removeList() {
592                         var bookmark = createBookmark(selection.getRng(true)), root = editor.getBody();
593
594                         tinymce.each(getSelectedListItems(), function(li) {
595                                 var node, rootList;
596
597                                 if (dom.isEmpty(li)) {
598                                         outdent(li);
599                                         return;
600                                 }
601
602                                 for (node = li; node && node != root; node = node.parentNode) {
603                                         if (isListNode(node)) {
604                                                 rootList = node;
605                                         }
606                                 }
607
608                                 splitList(rootList, li);
609                         });
610
611                         moveToBookmark(bookmark);
612                 }
613
614                 function toggleList(listName) {
615                         var parentList = dom.getParent(selection.getStart(), 'OL,UL,DL');
616
617                         if (parentList) {
618                                 if (parentList.nodeName == listName) {
619                                         removeList(listName);
620                                 } else {
621                                         var bookmark = createBookmark(selection.getRng(true));
622                                         mergeWithAdjacentLists(dom.rename(parentList, listName));
623                                         moveToBookmark(bookmark);
624                                 }
625                         } else {
626                                 applyList(listName);
627                         }
628                 }
629
630                 function queryListCommandState(listName) {
631                         return function() {
632                                 var parentList = dom.getParent(editor.selection.getStart(), 'UL,OL,DL');
633
634                                 return parentList && parentList.nodeName == listName;
635                         };
636                 }
637
638                 self.backspaceDelete = function(isForward) {
639                         function findNextCaretContainer(rng, isForward) {
640                                 var node = rng.startContainer, offset = rng.startOffset;
641                                 var nonEmptyBlocks, walker;
642
643                                 if (node.nodeType == 3 && (isForward ? offset < node.data.length : offset > 0)) {
644                                         return node;
645                                 }
646
647                                 nonEmptyBlocks = editor.schema.getNonEmptyElements();
648                                 walker = new tinymce.dom.TreeWalker(rng.startContainer);
649
650                                 while ((node = walker[isForward ? 'next' : 'prev']())) {
651                                         if (node.nodeName == 'LI' && !node.hasChildNodes()) {
652                                                 return node;
653                                         }
654
655                                         if (nonEmptyBlocks[node.nodeName]) {
656                                                 return node;
657                                         }
658
659                                         if (node.nodeType == 3 && node.data.length > 0) {
660                                                 return node;
661                                         }
662                                 }
663                         }
664
665                         function mergeLiElements(fromElm, toElm) {
666                                 var node, listNode, ul = fromElm.parentNode;
667
668                                 if (isListNode(toElm.lastChild)) {
669                                         listNode = toElm.lastChild;
670                                 }
671
672                                 node = toElm.lastChild;
673                                 if (node && node.nodeName == 'BR' && fromElm.hasChildNodes()) {
674                                         dom.remove(node);
675                                 }
676
677                                 if (dom.isEmpty(toElm)) {
678                                         dom.$(toElm).empty();
679                                 }
680
681                                 if (!dom.isEmpty(fromElm)) {
682                                         while ((node = fromElm.firstChild)) {
683                                                 toElm.appendChild(node);
684                                         }
685                                 }
686
687                                 if (listNode) {
688                                         toElm.appendChild(listNode);
689                                 }
690
691                                 dom.remove(fromElm);
692
693                                 if (dom.isEmpty(ul)) {
694                                         dom.remove(ul);
695                                 }
696                         }
697
698                         if (selection.isCollapsed()) {
699                                 var li = dom.getParent(selection.getStart(), 'LI');
700
701                                 if (li) {
702                                         var rng = selection.getRng(true);
703                                         var otherLi = dom.getParent(findNextCaretContainer(rng, isForward), 'LI');
704
705                                         if (otherLi && otherLi != li) {
706                                                 var bookmark = createBookmark(rng);
707
708                                                 if (isForward) {
709                                                         mergeLiElements(otherLi, li);
710                                                 } else {
711                                                         mergeLiElements(li, otherLi);
712                                                 }
713
714                                                 moveToBookmark(bookmark);
715
716                                                 return true;
717                                         } else if (!otherLi) {
718                                                 if (!isForward && removeList(li.parentNode.nodeName)) {
719                                                         return true;
720                                                 }
721                                         }
722                                 }
723                         }
724                 };
725
726                 editor.on('BeforeExecCommand', function(e) {
727                         var cmd = e.command.toLowerCase(), isHandled;
728
729                         if (cmd == "indent") {
730                                 if (indentSelection()) {
731                                         isHandled = true;
732                                 }
733                         } else if (cmd == "outdent") {
734                                 if (outdentSelection()) {
735                                         isHandled = true;
736                                 }
737                         }
738
739                         if (isHandled) {
740                                 editor.fire('ExecCommand', {command: e.command});
741                                 e.preventDefault();
742                                 return true;
743                         }
744                 });
745
746                 editor.addCommand('InsertUnorderedList', function() {
747                         toggleList('UL');
748                 });
749
750                 editor.addCommand('InsertOrderedList', function() {
751                         toggleList('OL');
752                 });
753
754                 editor.addCommand('InsertDefinitionList', function() {
755                         toggleList('DL');
756                 });
757
758                 editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState('UL'));
759                 editor.addQueryStateHandler('InsertOrderedList', queryListCommandState('OL'));
760                 editor.addQueryStateHandler('InsertDefinitionList', queryListCommandState('DL'));
761
762                 editor.on('keydown', function(e) {
763                         // Check for tab but not ctrl/cmd+tab since it switches browser tabs
764                         if (e.keyCode != 9 || tinymce.util.VK.metaKeyPressed(e)) {
765                                 return;
766                         }
767
768                         if (editor.dom.getParent(editor.selection.getStart(), 'LI,DT,DD')) {
769                                 e.preventDefault();
770
771                                 if (e.shiftKey) {
772                                         outdentSelection();
773                                 } else {
774                                         indentSelection();
775                                 }
776                         }
777                 });
778         });
779
780         editor.addButton('indent', {
781                 icon: 'indent',
782                 title: 'Increase indent',
783                 cmd: 'Indent',
784                 onPostRender: function() {
785                         var ctrl = this;
786
787                         editor.on('nodechange', function() {
788                                 var blocks = editor.selection.getSelectedBlocks();
789                                 var disable = false;
790
791                                 for (var i = 0, l = blocks.length; !disable && i < l; i++) {
792                                         var tag = blocks[i].nodeName;
793
794                                         disable = (tag == 'LI' && isFirstChild(blocks[i]) || tag == 'UL' || tag == 'OL' || tag == 'DD');
795                                 }
796
797                                 ctrl.disabled(disable);
798                         });
799                 }
800         });
801
802         editor.on('keydown', function(e) {
803                 if (e.keyCode == tinymce.util.VK.BACKSPACE) {
804                         if (self.backspaceDelete()) {
805                                 e.preventDefault();
806                         }
807                 } else if (e.keyCode == tinymce.util.VK.DELETE) {
808                         if (self.backspaceDelete(true)) {
809                                 e.preventDefault();
810                         }
811                 }
812         });
813 });