1 /**
  2  * Add removeFormat command for KISSY Editor.
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add("editor/plugin/removeFormat/cmd", function (S, Editor) {
  6     var KER = Editor.RANGE,
  7         ElementPath = Editor.ElementPath,
  8         DOM = S.DOM,
  9         /**
 10          * A comma separated list of elements to be removed
 11          * when executing the "remove format" command.
 12          * Note that only inline elements are allowed.
 13          * @type String
 14          * @default 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var'
 15          * @example
 16          */
 17             removeFormatTags = 'b,big,code,del,dfn,em,font,i,ins,kbd,' +
 18             'q,samp,small,span,strike,strong,sub,sup,tt,u,var,s',
 19         /**
 20          * A comma separated list of elements attributes to be removed
 21          * when executing the "remove format" command.
 22          * @type String
 23          * @default 'class,style,lang,width,height,align,hspace,valign'
 24          * @example
 25          */
 26             removeFormatAttributes = ('class,style,lang,width,height,' +
 27             'align,hspace,valign').split(/,/),
 28         tagsRegex = new RegExp('^(?:' +
 29             removeFormatTags.replace(/,/g, '|') +
 30             ')$', 'i');
 31 
 32     function removeAttrs(el, attrs) {
 33         for (var i = 0; i < attrs.length; i++) {
 34             el.removeAttr(attrs[i]);
 35         }
 36     }
 37 
 38     return {
 39         init:function (editor) {
 40             if (!editor.hasCommand("removeFormat")) {
 41                 editor.addCommand("removeFormat", {
 42                     exec:function () {
 43                         editor.focus();
 44                         tagsRegex.lastIndex = 0;
 45                         var ranges = editor.getSelection().getRanges();
 46                         editor.execCommand("save");
 47                         for (var i = 0, range; range = ranges[ i ]; i++) {
 48 
 49                             if (range.collapsed) {
 50                                 continue;
 51                             }
 52 
 53                             range.enlarge(KER.ENLARGE_ELEMENT);
 54 
 55                             // Bookmark the range so we can re-select it after processing.
 56                             var bookmark = range.createBookmark(),
 57                                 // The style will be applied within the bookmark boundaries.
 58                                 startNode = bookmark.startNode,
 59                                 endNode = bookmark.endNode;
 60 
 61                             // We need to check the selection boundaries (bookmark spans) to break
 62                             // the code in a way that we can properly remove partially selected nodes.
 63                             // For example, removing a <b> style from
 64                             //		<b>This is [some text</b> to show <b>the] problem</b>
 65                             // ... where [ and ] represent the selection, must result:
 66                             //		<b>This is </b>[some text to show the]<b> problem</b>
 67                             // The strategy is simple, we just break the partial nodes before the
 68                             // removal logic, having something that could be represented this way:
 69                             //		<b>This is </b>[<b>some text</b> to show <b>the</b>]<b> problem</b>
 70 
 71                             var breakParent = function (node) {
 72                                 // Let's start checking the start boundary.
 73                                 var path = new ElementPath(node),
 74                                     pathElements = path.elements;
 75 
 76                                 for (var i = 1, pathElement;
 77                                      pathElement = pathElements[ i ];
 78                                      i++) {
 79                                     if (pathElement.equals(path.block) ||
 80                                         pathElement.equals(path.blockLimit)) {
 81                                         break;
 82                                     }
 83                                     // If this element can be removed (even partially).
 84                                     if (tagsRegex.test(pathElement.nodeName())) {
 85                                         node._4e_breakParent(pathElement);
 86                                     }
 87                                 }
 88                             };
 89 
 90                             // does not make bookmark within any format tag
 91                             // but keep bookmark node is at original text posititon
 92                             breakParent(startNode);
 93                             breakParent(endNode);
 94 
 95                             // Navigate through all nodes between the bookmarks.
 96                             var currentNode = startNode
 97                                 // start from sibling , because obvious bookmark has no children
 98                                 ._4e_nextSourceNode(true, DOM.ELEMENT_NODE, undefined, undefined);
 99 
100                             while (currentNode) {
101                                 // If we have reached the end of the selection, stop looping.
102                                 if (currentNode.equals(endNode)) {
103                                     break;
104                                 }
105 
106                                 // Cache the next node to be processed. Do it now, because
107                                 // currentNode may be removed.
108                                 var nextNode = currentNode.
109                                     _4e_nextSourceNode(false, DOM.ELEMENT_NODE, undefined, undefined);
110 
111                                 // This node must not be a fake element.
112                                 if (!( currentNode.nodeName() == 'img' &&
113                                     currentNode.attr('_ke_realelement') )) {
114                                     // Remove elements nodes that match with this style rules.
115                                     if (tagsRegex.test(currentNode.nodeName()))
116                                         currentNode._4e_remove(true);
117                                     else {
118                                         removeAttrs(currentNode, removeFormatAttributes);
119                                     }
120                                 }
121                                 currentNode = nextNode;
122                             }
123                             range.moveToBookmark(bookmark);
124                         }
125                         editor.getSelection().selectRanges(ranges);
126                         editor.execCommand("save");
127                     }
128                 });
129             }
130         }
131     }
132 
133 }, {
134     requires:['editor']
135 });