section.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /**
  2. * 目录大纲支持插件
  3. * @file
  4. * @since 1.3.0
  5. */
  6. UE.plugin.register('section', function (){
  7. /* 目录节点对象 */
  8. function Section(option){
  9. this.tag = '';
  10. this.level = -1,
  11. this.dom = null;
  12. this.nextSection = null;
  13. this.previousSection = null;
  14. this.parentSection = null;
  15. this.startAddress = [];
  16. this.endAddress = [];
  17. this.children = [];
  18. }
  19. function getSection(option) {
  20. var section = new Section();
  21. return utils.extend(section, option);
  22. }
  23. function getNodeFromAddress(startAddress, root) {
  24. var current = root;
  25. for(var i = 0;i < startAddress.length; i++) {
  26. if(!current.childNodes) return null;
  27. current = current.childNodes[startAddress[i]];
  28. }
  29. return current;
  30. }
  31. var me = this;
  32. return {
  33. bindMultiEvents:{
  34. type: 'aftersetcontent afterscencerestore',
  35. handler: function(){
  36. me.fireEvent('updateSections');
  37. }
  38. },
  39. bindEvents:{
  40. /* 初始化、拖拽、粘贴、执行setcontent之后 */
  41. 'ready': function (){
  42. me.fireEvent('updateSections');
  43. domUtils.on(me.body, 'drop paste', function(){
  44. me.fireEvent('updateSections');
  45. });
  46. },
  47. /* 执行paragraph命令之后 */
  48. 'afterexeccommand': function (type, cmd) {
  49. if(cmd == 'paragraph') {
  50. me.fireEvent('updateSections');
  51. }
  52. },
  53. /* 部分键盘操作,触发updateSections事件 */
  54. 'keyup': function (type, e) {
  55. var me = this,
  56. range = me.selection.getRange();
  57. if(range.collapsed != true) {
  58. me.fireEvent('updateSections');
  59. } else {
  60. var keyCode = e.keyCode || e.which;
  61. if(keyCode == 13 || keyCode == 8 || keyCode == 46) {
  62. me.fireEvent('updateSections');
  63. }
  64. }
  65. }
  66. },
  67. commands:{
  68. 'getsections': {
  69. execCommand: function (cmd, levels) {
  70. var levelFn = levels || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
  71. for (var i = 0; i < levelFn.length; i++) {
  72. if (typeof levelFn[i] == 'string') {
  73. levelFn[i] = function(fn){
  74. return function(node){
  75. return node.tagName == fn.toUpperCase()
  76. };
  77. }(levelFn[i]);
  78. } else if (typeof levelFn[i] != 'function') {
  79. levelFn[i] = function (node) {
  80. return null;
  81. }
  82. }
  83. }
  84. function getSectionLevel(node) {
  85. for (var i = 0; i < levelFn.length; i++) {
  86. if (levelFn[i](node)) return i;
  87. }
  88. return -1;
  89. }
  90. var me = this,
  91. Directory = getSection({'level':-1, 'title':'root'}),
  92. previous = Directory;
  93. function traversal(node, Directory) {
  94. var level,
  95. tmpSection = null,
  96. parent,
  97. child,
  98. children = node.childNodes;
  99. for (var i = 0, len = children.length; i < len; i++) {
  100. child = children[i];
  101. level = getSectionLevel(child);
  102. if (level >= 0) {
  103. var address = me.selection.getRange().selectNode(child).createAddress(true).startAddress,
  104. current = getSection({
  105. 'tag': child.tagName,
  106. 'title': child.innerText || child.textContent || '',
  107. 'level': level,
  108. 'dom': child,
  109. 'startAddress': utils.clone(address, []),
  110. 'endAddress': utils.clone(address, []),
  111. 'children': []
  112. });
  113. previous.nextSection = current;
  114. current.previousSection = previous;
  115. parent = previous;
  116. while(level <= parent.level){
  117. parent = parent.parentSection;
  118. }
  119. current.parentSection = parent;
  120. parent.children.push(current);
  121. tmpSection = previous = current;
  122. } else {
  123. child.nodeType === 1 && traversal(child, Directory);
  124. tmpSection && tmpSection.endAddress[tmpSection.endAddress.length - 1] ++;
  125. }
  126. }
  127. }
  128. traversal(me.body, Directory);
  129. return Directory;
  130. },
  131. notNeedUndo: true
  132. },
  133. 'movesection': {
  134. execCommand: function (cmd, sourceSection, targetSection, isAfter) {
  135. var me = this,
  136. targetAddress,
  137. target;
  138. if(!sourceSection || !targetSection || targetSection.level == -1) return;
  139. targetAddress = isAfter ? targetSection.endAddress:targetSection.startAddress;
  140. target = getNodeFromAddress(targetAddress, me.body);
  141. /* 判断目标地址是否被源章节包含 */
  142. if(!targetAddress || !target || isContainsAddress(sourceSection.startAddress, sourceSection.endAddress, targetAddress)) return;
  143. var startNode = getNodeFromAddress(sourceSection.startAddress, me.body),
  144. endNode = getNodeFromAddress(sourceSection.endAddress, me.body),
  145. current,
  146. nextNode;
  147. if(isAfter) {
  148. current = endNode;
  149. while ( current && !(domUtils.getPosition( startNode, current ) & domUtils.POSITION_FOLLOWING) ) {
  150. nextNode = current.previousSibling;
  151. domUtils.insertAfter(target, current);
  152. if(current == startNode) break;
  153. current = nextNode;
  154. }
  155. } else {
  156. current = startNode;
  157. while ( current && !(domUtils.getPosition( current, endNode ) & domUtils.POSITION_FOLLOWING) ) {
  158. nextNode = current.nextSibling;
  159. target.parentNode.insertBefore(current, target);
  160. if(current == endNode) break;
  161. current = nextNode;
  162. }
  163. }
  164. me.fireEvent('updateSections');
  165. /* 获取地址的包含关系 */
  166. function isContainsAddress(startAddress, endAddress, addressTarget){
  167. var isAfterStartAddress = false,
  168. isBeforeEndAddress = false;
  169. for(var i = 0; i< startAddress.length; i++){
  170. if(i >= addressTarget.length) break;
  171. if(addressTarget[i] > startAddress[i]) {
  172. isAfterStartAddress = true;
  173. break;
  174. } else if(addressTarget[i] < startAddress[i]) {
  175. break;
  176. }
  177. }
  178. for(var i = 0; i< endAddress.length; i++){
  179. if(i >= addressTarget.length) break;
  180. if(addressTarget[i] < startAddress[i]) {
  181. isBeforeEndAddress = true;
  182. break;
  183. } else if(addressTarget[i] > startAddress[i]) {
  184. break;
  185. }
  186. }
  187. return isAfterStartAddress && isBeforeEndAddress;
  188. }
  189. }
  190. },
  191. 'deletesection': {
  192. execCommand: function (cmd, section, keepChildren) {
  193. var me = this;
  194. if(!section) return;
  195. function getNodeFromAddress(startAddress) {
  196. var current = me.body;
  197. for(var i = 0;i < startAddress.length; i++) {
  198. if(!current.childNodes) return null;
  199. current = current.childNodes[startAddress[i]];
  200. }
  201. return current;
  202. }
  203. var startNode = getNodeFromAddress(section.startAddress),
  204. endNode = getNodeFromAddress(section.endAddress),
  205. current = startNode,
  206. nextNode;
  207. if(!keepChildren) {
  208. while ( current && domUtils.inDoc(endNode, me.document) && !(domUtils.getPosition( current, endNode ) & domUtils.POSITION_FOLLOWING) ) {
  209. nextNode = current.nextSibling;
  210. domUtils.remove(current);
  211. current = nextNode;
  212. }
  213. } else {
  214. domUtils.remove(current);
  215. }
  216. me.fireEvent('updateSections');
  217. }
  218. },
  219. 'selectsection': {
  220. execCommand: function (cmd, section) {
  221. if(!section && !section.dom) return false;
  222. var me = this,
  223. range = me.selection.getRange(),
  224. address = {
  225. 'startAddress':utils.clone(section.startAddress, []),
  226. 'endAddress':utils.clone(section.endAddress, [])
  227. };
  228. address.endAddress[address.endAddress.length - 1]++;
  229. range.moveToAddress(address).select().scrollToView();
  230. return true;
  231. },
  232. notNeedUndo: true
  233. },
  234. 'scrolltosection': {
  235. execCommand: function (cmd, section) {
  236. if(!section && !section.dom) return false;
  237. var me = this,
  238. range = me.selection.getRange(),
  239. address = {
  240. 'startAddress':section.startAddress,
  241. 'endAddress':section.endAddress
  242. };
  243. address.endAddress[address.endAddress.length - 1]++;
  244. range.moveToAddress(address).scrollToView();
  245. return true;
  246. },
  247. notNeedUndo: true
  248. }
  249. }
  250. }
  251. });