blob: f597fafa8edcd62c8f1ecfbb973290043a28517c [file] [log] [blame]
huibing.xie1f1606f2018-08-20 15:46:55 +08001/*!
2 * UEditor
3 * version: ueditor
4 * build: Wed Aug 10 2016 11:06:16 GMT+0800 (CST)
5 */
6
7(function(){
8
9// editor.js
10UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
11
12var baidu = window.baidu || {};
13
14window.baidu = baidu;
15
16window.UE = baidu.editor = window.UE || {};
17
18UE.plugins = {};
19
20UE.commands = {};
21
22UE.instants = {};
23
24UE.I18N = {};
25
26UE._customizeUI = {};
27
28UE.version = "1.4.3";
29
30var dom = UE.dom = {};
31
32// core/browser.js
33/**
34 * 浏览器判断模块
35 * @file
36 * @module UE.browser
37 * @since 1.2.6.1
38 */
39
40/**
41 * 提供浏览器检测的模块
42 * @unfile
43 * @module UE.browser
44 */
45var browser = UE.browser = function(){
46 var agent = navigator.userAgent.toLowerCase(),
47 opera = window.opera,
48 browser = {
49 /**
50 * @property {boolean} ie 检测当前浏览器是否为IE
51 * @example
52 * ```javascript
53 * if ( UE.browser.ie ) {
54 * console.log( '当前浏览器是IE' );
55 * }
56 * ```
57 */
58 ie : /(msie\s|trident.*rv:)([\w.]+)/.test(agent),
59
60 /**
61 * @property {boolean} opera 检测当前浏览器是否为Opera
62 * @example
63 * ```javascript
64 * if ( UE.browser.opera ) {
65 * console.log( '当前浏览器是Opera' );
66 * }
67 * ```
68 */
69 opera : ( !!opera && opera.version ),
70
71 /**
72 * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
73 * @example
74 * ```javascript
75 * if ( UE.browser.webkit ) {
76 * console.log( '当前浏览器是webkit内核浏览器' );
77 * }
78 * ```
79 */
80 webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ),
81
82 /**
83 * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
84 * @example
85 * ```javascript
86 * if ( UE.browser.mac ) {
87 * console.log( '当前浏览器运行在mac平台下' );
88 * }
89 * ```
90 */
91 mac : ( agent.indexOf( 'macintosh' ) > -1 ),
92
93 /**
94 * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
95 * @example
96 * ```javascript
97 * if ( UE.browser.quirks ) {
98 * console.log( '当前浏览器运行处于“怪异模式”' );
99 * }
100 * ```
101 */
102 quirks : ( document.compatMode == 'BackCompat' )
103 };
104
105 /**
106 * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
107 * @example
108 * ```javascript
109 * if ( UE.browser.gecko ) {
110 * console.log( '当前浏览器内核是gecko内核' );
111 * }
112 * ```
113 */
114 browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie);
115
116 var version = 0;
117
118 // Internet Explorer 6.0+
119 if ( browser.ie ){
120
121 var v1 = agent.match(/(?:msie\s([\w.]+))/);
122 var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
123 if(v1 && v2 && v1[1] && v2[1]){
124 version = Math.max(v1[1]*1,v2[1]*1);
125 }else if(v1 && v1[1]){
126 version = v1[1]*1;
127 }else if(v2 && v2[1]){
128 version = v2[1]*1;
129 }else{
130 version = 0;
131 }
132
133 browser.ie11Compat = document.documentMode == 11;
134 /**
135 * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
136 * @warning 如果浏览器不是IE, 则该值为undefined
137 * @example
138 * ```javascript
139 * if ( UE.browser.ie9Compat ) {
140 * console.log( '当前浏览器运行在IE9兼容模式下' );
141 * }
142 * ```
143 */
144 browser.ie9Compat = document.documentMode == 9;
145
146 /**
147 * @property { boolean } ie8 检测浏览器是否是IE8浏览器
148 * @warning 如果浏览器不是IE, 则该值为undefined
149 * @example
150 * ```javascript
151 * if ( UE.browser.ie8 ) {
152 * console.log( '当前浏览器是IE8浏览器' );
153 * }
154 * ```
155 */
156 browser.ie8 = !!document.documentMode;
157
158 /**
159 * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
160 * @warning 如果浏览器不是IE, 则该值为undefined
161 * @example
162 * ```javascript
163 * if ( UE.browser.ie8Compat ) {
164 * console.log( '当前浏览器运行在IE8兼容模式下' );
165 * }
166 * ```
167 */
168 browser.ie8Compat = document.documentMode == 8;
169
170 /**
171 * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
172 * @warning 如果浏览器不是IE, 则该值为undefined
173 * @example
174 * ```javascript
175 * if ( UE.browser.ie7Compat ) {
176 * console.log( '当前浏览器运行在IE7兼容模式下' );
177 * }
178 * ```
179 */
180 browser.ie7Compat = ( ( version == 7 && !document.documentMode )
181 || document.documentMode == 7 );
182
183 /**
184 * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
185 * @warning 如果浏览器不是IE, 则该值为undefined
186 * @example
187 * ```javascript
188 * if ( UE.browser.ie6Compat ) {
189 * console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
190 * }
191 * ```
192 */
193 browser.ie6Compat = ( version < 7 || browser.quirks );
194
195 browser.ie9above = version > 8;
196
197 browser.ie9below = version < 9;
198
199 browser.ie11above = version > 10;
200
201 browser.ie11below = version < 11;
202
203 }
204
205 // Gecko.
206 if ( browser.gecko ){
207 var geckoRelease = agent.match( /rv:([\d\.]+)/ );
208 if ( geckoRelease )
209 {
210 geckoRelease = geckoRelease[1].split( '.' );
211 version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1;
212 }
213 }
214
215 /**
216 * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
217 * @warning 如果浏览器不是chrome, 则该值为undefined
218 * @example
219 * ```javascript
220 * if ( UE.browser.chrome ) {
221 * console.log( '当前浏览器是Chrome' );
222 * }
223 * ```
224 */
225 if (/chrome\/(\d+\.\d)/i.test(agent)) {
226 browser.chrome = + RegExp['\x241'];
227 }
228
229 /**
230 * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
231 * @warning 如果浏览器不是safari, 则该值为undefined
232 * @example
233 * ```javascript
234 * if ( UE.browser.safari ) {
235 * console.log( '当前浏览器是Safari' );
236 * }
237 * ```
238 */
239 if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){
240 browser.safari = + (RegExp['\x241'] || RegExp['\x242']);
241 }
242
243
244 // Opera 9.50+
245 if ( browser.opera )
246 version = parseFloat( opera.version() );
247
248 // WebKit 522+ (Safari 3+)
249 if ( browser.webkit )
250 version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] );
251
252 /**
253 * @property { Number } version 检测当前浏览器版本号
254 * @remind
255 * <ul>
256 * <li>IE系列返回值为5,6,7,8,9,10等</li>
257 * <li>gecko系列会返回10900,158900等</li>
258 * <li>webkit系列会返回其build号 (如 522等)</li>
259 * </ul>
260 * @example
261 * ```javascript
262 * console.log( '当前浏览器版本号是: ' + UE.browser.version );
263 * ```
264 */
265 browser.version = version;
266
267 /**
268 * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
269 * @example
270 * ```javascript
271 * if ( UE.browser.isCompatible ) {
272 * console.log( '浏览器与UEditor能够良好兼容' );
273 * }
274 * ```
275 */
276 browser.isCompatible =
277 !browser.mobile && (
278 ( browser.ie && version >= 6 ) ||
279 ( browser.gecko && version >= 10801 ) ||
280 ( browser.opera && version >= 9.5 ) ||
281 ( browser.air && version >= 1 ) ||
282 ( browser.webkit && version >= 522 ) ||
283 false );
284 return browser;
285}();
286//快捷方式
287var ie = browser.ie,
288 webkit = browser.webkit,
289 gecko = browser.gecko,
290 opera = browser.opera;
291
292// core/utils.js
293/**
294 * 工具函数包
295 * @file
296 * @module UE.utils
297 * @since 1.2.6.1
298 */
299
300/**
301 * UEditor封装使用的静态工具函数
302 * @module UE.utils
303 * @unfile
304 */
305
306var utils = UE.utils = {
307
308 /**
309 * 用给定的迭代器遍历对象
310 * @method each
311 * @param { Object } obj 需要遍历的对象
312 * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
313 * @example
314 * ```javascript
315 * var demoObj = {
316 * key1: 1,
317 * key2: 2
318 * };
319 *
320 * //output: key1: 1, key2: 2
321 * UE.utils.each( demoObj, funciton ( value, key ) {
322 *
323 * console.log( key + ":" + value );
324 *
325 * } );
326 * ```
327 */
328
329 /**
330 * 用给定的迭代器遍历数组或类数组对象
331 * @method each
332 * @param { Array } array 需要遍历的数组或者类数组
333 * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
334 * @example
335 * ```javascript
336 * var divs = document.getElmentByTagNames( "div" );
337 *
338 * //output: 0: DIV, 1: DIV ...
339 * UE.utils.each( divs, funciton ( value, key ) {
340 *
341 * console.log( key + ":" + value.tagName );
342 *
343 * } );
344 * ```
345 */
346 each : function(obj, iterator, context) {
347 if (obj == null) return;
348 if (obj.length === +obj.length) {
349 for (var i = 0, l = obj.length; i < l; i++) {
350 if(iterator.call(context, obj[i], i, obj) === false)
351 return false;
352 }
353 } else {
354 for (var key in obj) {
355 if (obj.hasOwnProperty(key)) {
356 if(iterator.call(context, obj[key], key, obj) === false)
357 return false;
358 }
359 }
360 }
361 },
362
363 /**
364 * 以给定对象作为原型创建一个新对象
365 * @method makeInstance
366 * @param { Object } protoObject 该对象将作为新创建对象的原型
367 * @return { Object } 新的对象, 该对象的原型是给定的protoObject对象
368 * @example
369 * ```javascript
370 *
371 * var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } };
372 *
373 * var newObject = UE.utils.makeInstance( protoObject );
374 * //output: Hello UEditor!
375 * newObject.sayHello();
376 * ```
377 */
378 makeInstance:function (obj) {
379 var noop = new Function();
380 noop.prototype = obj;
381 obj = new noop;
382 noop.prototype = null;
383 return obj;
384 },
385
386 /**
387 * 将source对象中的属性扩展到target对象上
388 * @method extend
389 * @remind 该方法将强制把source对象上的属性复制到target对象上
390 * @see UE.utils.extend(Object,Object,Boolean)
391 * @param { Object } target 目标对象, 新的属性将附加到该对象上
392 * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
393 * @return { Object } 返回target对象
394 * @example
395 * ```javascript
396 *
397 * var target = { name: 'target', sex: 1 },
398 * source = { name: 'source', age: 17 };
399 *
400 * UE.utils.extend( target, source );
401 *
402 * //output: { name: 'source', sex: 1, age: 17 }
403 * console.log( target );
404 *
405 * ```
406 */
407
408 /**
409 * 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与
410 * 源对象属性名相同的属性值。
411 * @method extend
412 * @param { Object } target 目标对象, 新的属性将附加到该对象上
413 * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
414 * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
415 * @return { Object } 返回target对象
416 * @example
417 * ```javascript
418 *
419 * var target = { name: 'target', sex: 1 },
420 * source = { name: 'source', age: 17 };
421 *
422 * UE.utils.extend( target, source, true );
423 *
424 * //output: { name: 'target', sex: 1, age: 17 }
425 * console.log( target );
426 *
427 * ```
428 */
429 extend:function (t, s, b) {
430 if (s) {
431 for (var k in s) {
432 if (!b || !t.hasOwnProperty(k)) {
433 t[k] = s[k];
434 }
435 }
436 }
437 return t;
438 },
439
440 /**
441 * 将给定的多个对象的属性复制到目标对象target上
442 * @method extend2
443 * @remind 该方法将强制把源对象上的属性复制到target对象上
444 * @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性,
445 * 将会覆盖掉之前的值。
446 * @param { Object } target 目标对象, 新的属性将附加到该对象上
447 * @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上
448 * @return { Object } 返回target对象
449 * @example
450 * ```javascript
451 *
452 * var target = {},
453 * source1 = { name: 'source', age: 17 },
454 * source2 = { title: 'dev' };
455 *
456 * UE.utils.extend2( target, source1, source2 );
457 *
458 * //output: { name: 'source', age: 17, title: 'dev' }
459 * console.log( target );
460 *
461 * ```
462 */
463 extend2:function (t) {
464 var a = arguments;
465 for (var i = 1; i < a.length; i++) {
466 var x = a[i];
467 for (var k in x) {
468 if (!t.hasOwnProperty(k)) {
469 t[k] = x[k];
470 }
471 }
472 }
473 return t;
474 },
475
476 /**
477 * 模拟继承机制, 使得subClass继承自superClass
478 * @method inherits
479 * @param { Object } subClass 子类对象
480 * @param { Object } superClass 超类对象
481 * @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承
482 * @return { Object } 继承superClass后的子类对象
483 * @example
484 * ```javascript
485 * function SuperClass(){
486 * this.name = "小李";
487 * }
488 *
489 * SuperClass.prototype = {
490 * hello:function(str){
491 * console.log(this.name + str);
492 * }
493 * }
494 *
495 * function SubClass(){
496 * this.name = "小张";
497 * }
498 *
499 * UE.utils.inherits(SubClass,SuperClass);
500 *
501 * var sub = new SubClass();
502 * //output: '小张早上好!
503 * sub.hello("早上好!");
504 * ```
505 */
506 inherits:function (subClass, superClass) {
507 var oldP = subClass.prototype,
508 newP = utils.makeInstance(superClass.prototype);
509 utils.extend(newP, oldP, true);
510 subClass.prototype = newP;
511 return (newP.constructor = subClass);
512 },
513
514 /**
515 * 用指定的context对象作为函数fn的上下文
516 * @method bind
517 * @param { Function } fn 需要绑定上下文的函数对象
518 * @param { Object } content 函数fn新的上下文对象
519 * @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。
520 * @example
521 * ```javascript
522 *
523 * var name = 'window',
524 * newTest = null;
525 *
526 * function test () {
527 * console.log( this.name );
528 * }
529 *
530 * newTest = UE.utils.bind( test, { name: 'object' } );
531 *
532 * //output: object
533 * newTest();
534 *
535 * //output: window
536 * test();
537 *
538 * ```
539 */
540 bind:function (fn, context) {
541 return function () {
542 return fn.apply(context, arguments);
543 };
544 },
545
546 /**
547 * 创建延迟指定时间后执行的函数fn
548 * @method defer
549 * @param { Function } fn 需要延迟执行的函数对象
550 * @param { int } delay 延迟的时间, 单位是毫秒
551 * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
552 * 而不能保证刚好到达延迟时间时执行。
553 * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
554 * @example
555 * ```javascript
556 * var start = 0;
557 *
558 * function test(){
559 * console.log( new Date() - start );
560 * }
561 *
562 * var testDefer = UE.utils.defer( test, 1000 );
563 * //
564 * start = new Date();
565 * //output: (大约在1000毫秒之后输出) 1000
566 * testDefer();
567 * ```
568 */
569
570 /**
571 * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值,
572 * 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。
573 * @method defer
574 * @param { Function } fn 需要延迟执行的函数对象
575 * @param { int } delay 延迟的时间, 单位是毫秒
576 * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行,
577 * 值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。
578 * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
579 * 而不能保证刚好到达延迟时间时执行。
580 * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
581 * @example
582 * ```javascript
583 *
584 * function test(){
585 * console.log(1);
586 * }
587 *
588 * var testDefer = UE.utils.defer( test, 1000, true );
589 *
590 * //output: (两次调用仅有一次输出) 1
591 * testDefer();
592 * testDefer();
593 * ```
594 */
595 defer:function (fn, delay, exclusion) {
596 var timerID;
597 return function () {
598 if (exclusion) {
599 clearTimeout(timerID);
600 }
601 timerID = setTimeout(fn, delay);
602 };
603 },
604
605 /**
606 * 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1
607 * @method indexOf
608 * @remind 该方法的匹配过程使用的是恒等“===”
609 * @param { Array } array 需要查找的数组对象
610 * @param { * } item 需要在目标数组中查找的值
611 * @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1
612 * @example
613 * ```javascript
614 * var item = 1,
615 * arr = [ 3, 4, 6, 8, 1, 1, 2 ];
616 *
617 * //output: 4
618 * console.log( UE.utils.indexOf( arr, item ) );
619 * ```
620 */
621
622 /**
623 * 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。
624 * @method indexOf
625 * @remind 该方法的匹配过程使用的是恒等“===”
626 * @param { Array } array 需要查找的数组对象
627 * @param { * } item 需要在目标数组中查找的值
628 * @param { int } start 搜索的起始位置
629 * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1
630 * @example
631 * ```javascript
632 * var item = 1,
633 * arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
634 *
635 * //output: 9
636 * console.log( UE.utils.indexOf( arr, item, 5 ) );
637 * ```
638 */
639 indexOf:function (array, item, start) {
640 var index = -1;
641 start = this.isNumber(start) ? start : 0;
642 this.each(array, function (v, i) {
643 if (i >= start && v === item) {
644 index = i;
645 return false;
646 }
647 });
648 return index;
649 },
650
651 /**
652 * 移除数组array中所有的元素item
653 * @method removeItem
654 * @param { Array } array 要移除元素的目标数组
655 * @param { * } item 将要被移除的元素
656 * @remind 该方法的匹配过程使用的是恒等“===”
657 * @example
658 * ```javascript
659 * var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
660 *
661 * UE.utils.removeItem( arr, 4 );
662 * //output: [ 5, 7, 1, 3, 6 ]
663 * console.log( arr );
664 *
665 * ```
666 */
667 removeItem:function (array, item) {
668 for (var i = 0, l = array.length; i < l; i++) {
669 if (array[i] === item) {
670 array.splice(i, 1);
671 i--;
672 }
673 }
674 },
675
676 /**
677 * 删除字符串str的首尾空格
678 * @method trim
679 * @param { String } str 需要删除首尾空格的字符串
680 * @return { String } 删除了首尾的空格后的字符串
681 * @example
682 * ```javascript
683 *
684 * var str = " UEdtior ";
685 *
686 * //output: 9
687 * console.log( str.length );
688 *
689 * //output: 7
690 * console.log( UE.utils.trim( " UEdtior " ).length );
691 *
692 * //output: 9
693 * console.log( str.length );
694 *
695 * ```
696 */
697 trim:function (str) {
698 return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
699 },
700
701 /**
702 * 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
703 * @method listToMap
704 * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
705 * @param { String } str 该字符串将被以','分割为数组, 然后进行转化
706 * @return { Object } 转化之后的hash对象
707 * @example
708 * ```javascript
709 *
710 * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
711 * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
712 *
713 * ```
714 */
715
716 /**
717 * 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
718 * @method listToMap
719 * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
720 * @param { Array } arr 字符串数组
721 * @return { Object } 转化之后的hash对象
722 * @example
723 * ```javascript
724 *
725 * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
726 * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
727 *
728 * ```
729 */
730 listToMap:function (list) {
731 if (!list)return {};
732 list = utils.isArray(list) ? list : list.split(',');
733 for (var i = 0, ci, obj = {}; ci = list[i++];) {
734 obj[ci.toUpperCase()] = obj[ci] = 1;
735 }
736 return obj;
737 },
738
739 /**
740 * 将str中的html符号转义,将转义“',&,<,",>”五个字符
741 * @method unhtml
742 * @param { String } str 需要转义的字符串
743 * @return { String } 转义后的字符串
744 * @example
745 * ```javascript
746 * var html = '<body>&</body>';
747 *
748 * //output: &lt;body&gt;&amp;&lt;/body&gt;
749 * console.log( UE.utils.unhtml( html ) );
750 *
751 * ```
752 */
753 unhtml:function (str, reg) {
754 return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
755 if (b) {
756 return a;
757 } else {
758 return {
759 '<':'&lt;',
760 '&':'&amp;',
761 '"':'&quot;',
762 '>':'&gt;',
763 "'":'&#39;'
764 }[a]
765 }
766
767 }) : '';
768 },
769 /**
770 * 将url中的html字符转义, 仅转义 ', ", <, > 四个字符
771 * @param { String } str 需要转义的字符串
772 * @param { RegExp } reg 自定义的正则
773 * @return { String } 转义后的字符串
774 */
775 unhtmlForUrl:function (str, reg) {
776 return str ? str.replace(reg || /[<">']/g, function (a) {
777 return {
778 '<':'&lt;',
779 '&':'&amp;',
780 '"':'&quot;',
781 '>':'&gt;',
782 "'":'&#39;'
783 }[a]
784
785 }) : '';
786 },
787
788 /**
789 * 将str中的转义字符还原成html字符
790 * @see UE.utils.unhtml(String);
791 * @method html
792 * @param { String } str 需要逆转义的字符串
793 * @return { String } 逆转义后的字符串
794 * @example
795 * ```javascript
796 *
797 * var str = '&lt;body&gt;&amp;&lt;/body&gt;';
798 *
799 * //output: <body>&</body>
800 * console.log( UE.utils.html( str ) );
801 *
802 * ```
803 */
804 html:function (str) {
805 return str ? str.replace(/&((g|l|quo)t|amp|#39|nbsp);/g, function (m) {
806 return {
807 '&lt;':'<',
808 '&amp;':'&',
809 '&quot;':'"',
810 '&gt;':'>',
811 '&#39;':"'",
812 '&nbsp;':' '
813 }[m]
814 }) : '';
815 },
816
817 /**
818 * 将css样式转换为驼峰的形式
819 * @method cssStyleToDomStyle
820 * @param { String } cssName 需要转换的css样式名
821 * @return { String } 转换成驼峰形式后的css样式名
822 * @example
823 * ```javascript
824 *
825 * var str = 'border-top';
826 *
827 * //output: borderTop
828 * console.log( UE.utils.cssStyleToDomStyle( str ) );
829 *
830 * ```
831 */
832 cssStyleToDomStyle:function () {
833 var test = document.createElement('div').style,
834 cache = {
835 'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
836 };
837
838 return function (cssName) {
839 return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
840 return match.charAt(1).toUpperCase();
841 }));
842 };
843 }(),
844
845 /**
846 * 动态加载文件到doc中
847 * @method loadFile
848 * @param { DomDocument } document 需要加载资源文件的文档对象
849 * @param { Object } options 加载资源文件的属性集合, 取值请参考代码示例
850 * @example
851 * ```javascript
852 *
853 * UE.utils.loadFile( document, {
854 * src:"test.js",
855 * tag:"script",
856 * type:"text/javascript",
857 * defer:"defer"
858 * } );
859 *
860 * ```
861 */
862
863 /**
864 * 动态加载文件到doc中,加载成功后执行的回调函数fn
865 * @method loadFile
866 * @param { DomDocument } document 需要加载资源文件的文档对象
867 * @param { Object } options 加载资源文件的属性集合, 该集合支持的值是script标签和style标签支持的所有属性。
868 * @param { Function } fn 资源文件加载成功之后执行的回调
869 * @warning 对于在同一个文档中多次加载同一URL的文件, 该方法会在第一次加载之后缓存该请求,
870 * 在此之后的所有同一URL的请求, 将会直接触发回调。
871 * @example
872 * ```javascript
873 *
874 * UE.utils.loadFile( document, {
875 * src:"test.js",
876 * tag:"script",
877 * type:"text/javascript",
878 * defer:"defer"
879 * }, function () {
880 * console.log('加载成功');
881 * } );
882 *
883 * ```
884 */
885 loadFile:function () {
886 var tmpList = [];
887
888 function getItem(doc, obj) {
889 try {
890 for (var i = 0, ci; ci = tmpList[i++];) {
891 if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
892 return ci;
893 }
894 }
895 } catch (e) {
896 return null;
897 }
898
899 }
900
901 return function (doc, obj, fn) {
902 var item = getItem(doc, obj);
903 if (item) {
904 if (item.ready) {
905 fn && fn();
906 } else {
907 item.funs.push(fn)
908 }
909 return;
910 }
911 tmpList.push({
912 doc:doc,
913 url:obj.src || obj.href,
914 funs:[fn]
915 });
916 if (!doc.body) {
917 var html = [];
918 for (var p in obj) {
919 if (p == 'tag')continue;
920 html.push(p + '="' + obj[p] + '"')
921 }
922 doc.write('<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>');
923 return;
924 }
925 if (obj.id && doc.getElementById(obj.id)) {
926 return;
927 }
928 var element = doc.createElement(obj.tag);
929 delete obj.tag;
930 for (var p in obj) {
931 element.setAttribute(p, obj[p]);
932 }
933 element.onload = element.onreadystatechange = function () {
934 if (!this.readyState || /loaded|complete/.test(this.readyState)) {
935 item = getItem(doc, obj);
936 if (item.funs.length > 0) {
937 item.ready = 1;
938 for (var fi; fi = item.funs.pop();) {
939 fi();
940 }
941 }
942 element.onload = element.onreadystatechange = null;
943 }
944 };
945 element.onerror = function () {
946 throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file ueditor.config.js ')
947 };
948 doc.getElementsByTagName("head")[0].appendChild(element);
949 }
950 }(),
951
952 /**
953 * 判断obj对象是否为空
954 * @method isEmptyObject
955 * @param { * } obj 需要判断的对象
956 * @remind 如果判断的对象是NULL, 将直接返回true, 如果是数组且为空, 返回true, 如果是字符串, 且字符串为空,
957 * 返回true, 如果是普通对象, 且该对象没有任何实例属性, 返回true
958 * @return { Boolean } 对象是否为空
959 * @example
960 * ```javascript
961 *
962 * //output: true
963 * console.log( UE.utils.isEmptyObject( {} ) );
964 *
965 * //output: true
966 * console.log( UE.utils.isEmptyObject( [] ) );
967 *
968 * //output: true
969 * console.log( UE.utils.isEmptyObject( "" ) );
970 *
971 * //output: false
972 * console.log( UE.utils.isEmptyObject( { key: 1 } ) );
973 *
974 * //output: false
975 * console.log( UE.utils.isEmptyObject( [1] ) );
976 *
977 * //output: false
978 * console.log( UE.utils.isEmptyObject( "1" ) );
979 *
980 * ```
981 */
982 isEmptyObject:function (obj) {
983 if (obj == null) return true;
984 if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
985 for (var key in obj) if (obj.hasOwnProperty(key)) return false;
986 return true;
987 },
988
989 /**
990 * 把rgb格式的颜色值转换成16进制格式
991 * @method fixColor
992 * @param { String } rgb格式的颜色值
993 * @param { String }
994 * @example
995 * rgb(255,255,255) => "#ffffff"
996 */
997 fixColor:function (name, value) {
998 if (/color/i.test(name) && /rgba?/.test(value)) {
999 var array = value.split(",");
1000 if (array.length > 3)
1001 return "";
1002 value = "#";
1003 for (var i = 0, color; color = array[i++];) {
1004 color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
1005 value += color.length == 1 ? "0" + color : color;
1006 }
1007 value = value.toUpperCase();
1008 }
1009 return value;
1010 },
1011 /**
1012 * 只针对border,padding,margin做了处理,因为性能问题
1013 * @public
1014 * @function
1015 * @param {String} val style字符串
1016 */
1017 optCss:function (val) {
1018 var padding, margin, border;
1019 val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function (str, key, name, val) {
1020 if (val.split(' ').length == 1) {
1021 switch (key) {
1022 case 'padding':
1023 !padding && (padding = {});
1024 padding[name] = val;
1025 return '';
1026 case 'margin':
1027 !margin && (margin = {});
1028 margin[name] = val;
1029 return '';
1030 case 'border':
1031 return val == 'initial' ? '' : str;
1032 }
1033 }
1034 return str;
1035 });
1036
1037 function opt(obj, name) {
1038 if (!obj) {
1039 return '';
1040 }
1041 var t = obj.top , b = obj.bottom, l = obj.left, r = obj.right, val = '';
1042 if (!t || !l || !b || !r) {
1043 for (var p in obj) {
1044 val += ';' + name + '-' + p + ':' + obj[p] + ';';
1045 }
1046 } else {
1047 val += ';' + name + ':' +
1048 (t == b && b == l && l == r ? t :
1049 t == b && l == r ? (t + ' ' + l) :
1050 l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l)) + ';'
1051 }
1052 return val;
1053 }
1054
1055 val += opt(padding, 'padding') + opt(margin, 'margin');
1056 return val.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '').replace(/;([ \n\r\t]+)|\1;/g, ';')
1057 .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) {
1058 return b ? b + ";;" : ';'
1059 });
1060 },
1061
1062 /**
1063 * 克隆对象
1064 * @method clone
1065 * @param { Object } source 源对象
1066 * @return { Object } source的一个副本
1067 */
1068
1069 /**
1070 * 深度克隆对象,将source的属性克隆到target对象, 会覆盖target重名的属性。
1071 * @method clone
1072 * @param { Object } source 源对象
1073 * @param { Object } target 目标对象
1074 * @return { Object } 附加了source对象所有属性的target对象
1075 */
1076 clone:function (source, target) {
1077 var tmp;
1078 target = target || {};
1079 for (var i in source) {
1080 if (source.hasOwnProperty(i)) {
1081 tmp = source[i];
1082 if (typeof tmp == 'object') {
1083 target[i] = utils.isArray(tmp) ? [] : {};
1084 utils.clone(source[i], target[i])
1085 } else {
1086 target[i] = tmp;
1087 }
1088 }
1089 }
1090 return target;
1091 },
1092
1093 /**
1094 * 把cm/pt为单位的值转换为px为单位的值
1095 * @method transUnitToPx
1096 * @param { String } 待转换的带单位的字符串
1097 * @return { String } 转换为px为计量单位的值的字符串
1098 * @example
1099 * ```javascript
1100 *
1101 * //output: 500px
1102 * console.log( UE.utils.transUnitToPx( '20cm' ) );
1103 *
1104 * //output: 27px
1105 * console.log( UE.utils.transUnitToPx( '20pt' ) );
1106 *
1107 * ```
1108 */
1109 transUnitToPx:function (val) {
1110 if (!/(pt|cm)/.test(val)) {
1111 return val
1112 }
1113 var unit;
1114 val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
1115 val = v;
1116 unit = u;
1117 });
1118 switch (unit) {
1119 case 'cm':
1120 val = parseFloat(val) * 25;
1121 break;
1122 case 'pt':
1123 val = Math.round(parseFloat(val) * 96 / 72);
1124 }
1125 return val + (val ? 'px' : '');
1126 },
1127
1128 /**
1129 * 在dom树ready之后执行给定的回调函数
1130 * @method domReady
1131 * @remind 如果在执行该方法的时候, dom树已经ready, 那么回调函数将立刻执行
1132 * @param { Function } fn dom树ready之后的回调函数
1133 * @example
1134 * ```javascript
1135 *
1136 * UE.utils.domReady( function () {
1137 *
1138 * console.log('123');
1139 *
1140 * } );
1141 *
1142 * ```
1143 */
1144 domReady:function () {
1145
1146 var fnArr = [];
1147
1148 function doReady(doc) {
1149 //确保onready只执行一次
1150 doc.isReady = true;
1151 for (var ci; ci = fnArr.pop(); ci()) {
1152 }
1153 }
1154
1155 return function (onready, win) {
1156 win = win || window;
1157 var doc = win.document;
1158 onready && fnArr.push(onready);
1159 if (doc.readyState === "complete") {
1160 doReady(doc);
1161 } else {
1162 doc.isReady && doReady(doc);
1163 if (browser.ie && browser.version != 11) {
1164 (function () {
1165 if (doc.isReady) return;
1166 try {
1167 doc.documentElement.doScroll("left");
1168 } catch (error) {
1169 setTimeout(arguments.callee, 0);
1170 return;
1171 }
1172 doReady(doc);
1173 })();
1174 win.attachEvent('onload', function () {
1175 doReady(doc)
1176 });
1177 } else {
1178 doc.addEventListener("DOMContentLoaded", function () {
1179 doc.removeEventListener("DOMContentLoaded", arguments.callee, false);
1180 doReady(doc);
1181 }, false);
1182 win.addEventListener('load', function () {
1183 doReady(doc)
1184 }, false);
1185 }
1186 }
1187
1188 }
1189 }(),
1190
1191 /**
1192 * 动态添加css样式
1193 * @method cssRule
1194 * @param { String } 节点名称
1195 * @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
1196 * @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
1197 * @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
1198 * @grammar UE.utils.cssRule('body',document) => 返回指定key的样式,并且指定是哪个document
1199 * @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
1200 */
1201 cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {
1202 var indexList, index;
1203 if(style === undefined || style && style.nodeType && style.nodeType == 9){
1204 //获取样式
1205 doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
1206 indexList = doc.indexList || (doc.indexList = {});
1207 index = indexList[key];
1208 if(index !== undefined){
1209 return doc.styleSheets[index].cssText
1210 }
1211 return undefined;
1212 }
1213 doc = doc || document;
1214 indexList = doc.indexList || (doc.indexList = {});
1215 index = indexList[key];
1216 //清除样式
1217 if(style === ''){
1218 if(index!== undefined){
1219 doc.styleSheets[index].cssText = '';
1220 delete indexList[key];
1221 return true
1222 }
1223 return false;
1224 }
1225
1226 //添加样式
1227 if(index!== undefined){
1228 sheetStyle = doc.styleSheets[index];
1229 }else{
1230 sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
1231 indexList[key] = index;
1232 }
1233 sheetStyle.cssText = style;
1234 }: function (key, style, doc) {
1235 var head, node;
1236 if(style === undefined || style && style.nodeType && style.nodeType == 9){
1237 //获取样式
1238 doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
1239 node = doc.getElementById(key);
1240 return node ? node.innerHTML : undefined;
1241 }
1242 doc = doc || document;
1243 node = doc.getElementById(key);
1244
1245 //清除样式
1246 if(style === ''){
1247 if(node){
1248 node.parentNode.removeChild(node);
1249 return true
1250 }
1251 return false;
1252 }
1253
1254 //添加样式
1255 if(node){
1256 node.innerHTML = style;
1257 }else{
1258 node = doc.createElement('style');
1259 node.id = key;
1260 node.innerHTML = style;
1261 doc.getElementsByTagName('head')[0].appendChild(node);
1262 }
1263 },
1264 sort:function(array,compareFn){
1265 compareFn = compareFn || function(item1, item2){ return item1.localeCompare(item2);};
1266 for(var i= 0,len = array.length; i<len; i++){
1267 for(var j = i,length = array.length; j<length; j++){
1268 if(compareFn(array[i], array[j]) > 0){
1269 var t = array[i];
1270 array[i] = array[j];
1271 array[j] = t;
1272 }
1273 }
1274 }
1275 return array;
1276 },
1277 serializeParam:function (json) {
1278 var strArr = [];
1279 for (var i in json) {
1280 //忽略默认的几个参数
1281 if(i=="method" || i=="timeout" || i=="async") continue;
1282 //传递过来的对象和函数不在提交之列
1283 if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
1284 strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) );
1285 } else if (utils.isArray(json[i])) {
1286 //支持传数组内容
1287 for(var j = 0; j < json[i].length; j++) {
1288 strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) );
1289 }
1290 }
1291 }
1292 return strArr.join("&");
1293 },
1294 formatUrl:function (url) {
1295 var u = url.replace(/&&/g, '&');
1296 u = u.replace(/\?&/g, '?');
1297 u = u.replace(/&$/g, '');
1298 u = u.replace(/&#/g, '#');
1299 u = u.replace(/&+/g, '&');
1300 return u;
1301 },
1302 isCrossDomainUrl:function (url) {
1303 var a = document.createElement('a');
1304 a.href = url;
1305 if (browser.ie) {
1306 a.href = a.href;
1307 }
1308 return !(a.protocol == location.protocol && a.hostname == location.hostname &&
1309 (a.port == location.port || (a.port == '80' && location.port == '') || (a.port == '' && location.port == '80')));
1310 },
1311 clearEmptyAttrs : function(obj){
1312 for(var p in obj){
1313 if(obj[p] === ''){
1314 delete obj[p]
1315 }
1316 }
1317 return obj;
1318 },
1319 str2json : function(s){
1320
1321 if (!utils.isString(s)) return null;
1322 if (window.JSON) {
1323 return JSON.parse(s);
1324 } else {
1325 return (new Function("return " + utils.trim(s || '')))();
1326 }
1327
1328 },
1329 json2str : (function(){
1330
1331 if (window.JSON) {
1332
1333 return JSON.stringify;
1334
1335 } else {
1336
1337 var escapeMap = {
1338 "\b": '\\b',
1339 "\t": '\\t',
1340 "\n": '\\n',
1341 "\f": '\\f',
1342 "\r": '\\r',
1343 '"' : '\\"',
1344 "\\": '\\\\'
1345 };
1346
1347 function encodeString(source) {
1348 if (/["\\\x00-\x1f]/.test(source)) {
1349 source = source.replace(
1350 /["\\\x00-\x1f]/g,
1351 function (match) {
1352 var c = escapeMap[match];
1353 if (c) {
1354 return c;
1355 }
1356 c = match.charCodeAt();
1357 return "\\u00"
1358 + Math.floor(c / 16).toString(16)
1359 + (c % 16).toString(16);
1360 });
1361 }
1362 return '"' + source + '"';
1363 }
1364
1365 function encodeArray(source) {
1366 var result = ["["],
1367 l = source.length,
1368 preComma, i, item;
1369
1370 for (i = 0; i < l; i++) {
1371 item = source[i];
1372
1373 switch (typeof item) {
1374 case "undefined":
1375 case "function":
1376 case "unknown":
1377 break;
1378 default:
1379 if(preComma) {
1380 result.push(',');
1381 }
1382 result.push(utils.json2str(item));
1383 preComma = 1;
1384 }
1385 }
1386 result.push("]");
1387 return result.join("");
1388 }
1389
1390 function pad(source) {
1391 return source < 10 ? '0' + source : source;
1392 }
1393
1394 function encodeDate(source){
1395 return '"' + source.getFullYear() + "-"
1396 + pad(source.getMonth() + 1) + "-"
1397 + pad(source.getDate()) + "T"
1398 + pad(source.getHours()) + ":"
1399 + pad(source.getMinutes()) + ":"
1400 + pad(source.getSeconds()) + '"';
1401 }
1402
1403 return function (value) {
1404 switch (typeof value) {
1405 case 'undefined':
1406 return 'undefined';
1407
1408 case 'number':
1409 return isFinite(value) ? String(value) : "null";
1410
1411 case 'string':
1412 return encodeString(value);
1413
1414 case 'boolean':
1415 return String(value);
1416
1417 default:
1418 if (value === null) {
1419 return 'null';
1420 } else if (utils.isArray(value)) {
1421 return encodeArray(value);
1422 } else if (utils.isDate(value)) {
1423 return encodeDate(value);
1424 } else {
1425 var result = ['{'],
1426 encode = utils.json2str,
1427 preComma,
1428 item;
1429
1430 for (var key in value) {
1431 if (Object.prototype.hasOwnProperty.call(value, key)) {
1432 item = value[key];
1433 switch (typeof item) {
1434 case 'undefined':
1435 case 'unknown':
1436 case 'function':
1437 break;
1438 default:
1439 if (preComma) {
1440 result.push(',');
1441 }
1442 preComma = 1;
1443 result.push(encode(key) + ':' + encode(item));
1444 }
1445 }
1446 }
1447 result.push('}');
1448 return result.join('');
1449 }
1450 }
1451 };
1452 }
1453
1454 })()
1455
1456};
1457/**
1458 * 判断给定的对象是否是字符串
1459 * @method isString
1460 * @param { * } object 需要判断的对象
1461 * @return { Boolean } 给定的对象是否是字符串
1462 */
1463
1464/**
1465 * 判断给定的对象是否是数组
1466 * @method isArray
1467 * @param { * } object 需要判断的对象
1468 * @return { Boolean } 给定的对象是否是数组
1469 */
1470
1471/**
1472 * 判断给定的对象是否是一个Function
1473 * @method isFunction
1474 * @param { * } object 需要判断的对象
1475 * @return { Boolean } 给定的对象是否是Function
1476 */
1477
1478/**
1479 * 判断给定的对象是否是Number
1480 * @method isNumber
1481 * @param { * } object 需要判断的对象
1482 * @return { Boolean } 给定的对象是否是Number
1483 */
1484
1485/**
1486 * 判断给定的对象是否是一个正则表达式
1487 * @method isRegExp
1488 * @param { * } object 需要判断的对象
1489 * @return { Boolean } 给定的对象是否是正则表达式
1490 */
1491
1492/**
1493 * 判断给定的对象是否是一个普通对象
1494 * @method isObject
1495 * @param { * } object 需要判断的对象
1496 * @return { Boolean } 给定的对象是否是普通对象
1497 */
1498utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object', 'Date'], function (v) {
1499 UE.utils['is' + v] = function (obj) {
1500 return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
1501 }
1502});
1503
1504
1505// core/EventBase.js
1506/**
1507 * UE采用的事件基类
1508 * @file
1509 * @module UE
1510 * @class EventBase
1511 * @since 1.2.6.1
1512 */
1513
1514/**
1515 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
1516 * @unfile
1517 * @module UE
1518 */
1519
1520/**
1521 * UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
1522 * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
1523 * @unfile
1524 * @module UE
1525 * @class EventBase
1526 */
1527
1528/**
1529 * 通过此构造器,子类可以继承EventBase获取事件监听的方法
1530 * @constructor
1531 * @example
1532 * ```javascript
1533 * UE.EventBase.call(editor);
1534 * ```
1535 */
1536var EventBase = UE.EventBase = function () {};
1537
1538EventBase.prototype = {
1539
1540 /**
1541 * 注册事件监听器
1542 * @method addListener
1543 * @param { String } types 监听的事件名称,同时监听多个事件使用空格分隔
1544 * @param { Function } fn 监听的事件被触发时,会执行该回调函数
1545 * @waining 事件被触发时,监听的函数假如返回的值恒等于true,回调函数的队列中后面的函数将不执行
1546 * @example
1547 * ```javascript
1548 * editor.addListener('selectionchange',function(){
1549 * console.log("选区已经变化!");
1550 * })
1551 * editor.addListener('beforegetcontent aftergetcontent',function(type){
1552 * if(type == 'beforegetcontent'){
1553 * //do something
1554 * }else{
1555 * //do something
1556 * }
1557 * console.log(this.getContent) // this是注册的事件的编辑器实例
1558 * })
1559 * ```
1560 * @see UE.EventBase:fireEvent(String)
1561 */
1562 addListener:function (types, listener) {
1563 types = utils.trim(types).split(/\s+/);
1564 for (var i = 0, ti; ti = types[i++];) {
1565 getListener(this, ti, true).push(listener);
1566 }
1567 },
1568
1569 on : function(types, listener){
1570 return this.addListener(types,listener);
1571 },
1572 off : function(types, listener){
1573 return this.removeListener(types, listener)
1574 },
1575 trigger:function(){
1576 return this.fireEvent.apply(this,arguments);
1577 },
1578 /**
1579 * 移除事件监听器
1580 * @method removeListener
1581 * @param { String } types 移除的事件名称,同时移除多个事件使用空格分隔
1582 * @param { Function } fn 移除监听事件的函数引用
1583 * @example
1584 * ```javascript
1585 * //changeCallback为方法体
1586 * editor.removeListener("selectionchange",changeCallback);
1587 * ```
1588 */
1589 removeListener:function (types, listener) {
1590 types = utils.trim(types).split(/\s+/);
1591 for (var i = 0, ti; ti = types[i++];) {
1592 utils.removeItem(getListener(this, ti) || [], listener);
1593 }
1594 },
1595
1596 /**
1597 * 触发事件
1598 * @method fireEvent
1599 * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
1600 * @remind 该方法会触发addListener
1601 * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
1602 * @example
1603 * ```javascript
1604 * editor.fireEvent("selectionchange");
1605 * ```
1606 */
1607
1608 /**
1609 * 触发事件
1610 * @method fireEvent
1611 * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
1612 * @param { *... } options 可选参数,可以传入一个或多个参数,会传给事件触发的回调函数
1613 * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
1614 * @example
1615 * ```javascript
1616 *
1617 * editor.addListener( "selectionchange", function ( type, arg1, arg2 ) {
1618 *
1619 * console.log( arg1 + " " + arg2 );
1620 *
1621 * } );
1622 *
1623 * //触发selectionchange事件, 会执行上面的事件监听器
1624 * //output: Hello World
1625 * editor.fireEvent("selectionchange", "Hello", "World");
1626 * ```
1627 */
1628 fireEvent:function () {
1629 var types = arguments[0];
1630 types = utils.trim(types).split(' ');
1631 for (var i = 0, ti; ti = types[i++];) {
1632 var listeners = getListener(this, ti),
1633 r, t, k;
1634 if (listeners) {
1635 k = listeners.length;
1636 while (k--) {
1637 if(!listeners[k])continue;
1638 t = listeners[k].apply(this, arguments);
1639 if(t === true){
1640 return t;
1641 }
1642 if (t !== undefined) {
1643 r = t;
1644 }
1645 }
1646 }
1647 if (t = this['on' + ti.toLowerCase()]) {
1648 r = t.apply(this, arguments);
1649 }
1650 }
1651 return r;
1652 }
1653};
1654/**
1655 * 获得对象所拥有监听类型的所有监听器
1656 * @unfile
1657 * @module UE
1658 * @since 1.2.6.1
1659 * @method getListener
1660 * @public
1661 * @param { Object } obj 查询监听器的对象
1662 * @param { String } type 事件类型
1663 * @param { Boolean } force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
1664 * @return { Array } 监听器数组
1665 */
1666function getListener(obj, type, force) {
1667 var allListeners;
1668 type = type.toLowerCase();
1669 return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )
1670 && ( allListeners[type] || force && ( allListeners[type] = [] ) ) );
1671}
1672
1673
1674
1675// core/dtd.js
1676///import editor.js
1677///import core/dom/dom.js
1678///import core/utils.js
1679/**
1680 * dtd html语义化的体现类
1681 * @constructor
1682 * @namespace dtd
1683 */
1684var dtd = dom.dtd = (function() {
1685 function _( s ) {
1686 for (var k in s) {
1687 s[k.toUpperCase()] = s[k];
1688 }
1689 return s;
1690 }
1691 var X = utils.extend2;
1692 var A = _({isindex:1,fieldset:1}),
1693 B = _({input:1,button:1,select:1,textarea:1,label:1}),
1694 C = X( _({a:1}), B ),
1695 D = X( {iframe:1}, C ),
1696 E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}),
1697 F = _({ins:1,del:1,script:1,style:1}),
1698 G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ),
1699 H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ),
1700 I = X( _({p:1}), H ),
1701 J = X( _({iframe:1}), H, B ),
1702 K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}),
1703
1704 L = X( _({a:0}), J ),//a不能被切开,所以把他
1705 M = _({tr:1}),
1706 N = _({'#':1}),
1707 O = X( _({param:1}), K ),
1708 P = X( _({form:1}), A, D, E, I ),
1709 Q = _({li:1,ol:1,ul:1}),
1710 R = _({style:1,script:1}),
1711 S = _({base:1,link:1,meta:1,title:1}),
1712 T = X( S, R ),
1713 U = _({head:1,body:1}),
1714 V = _({html:1});
1715
1716 var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}),
1717
1718 empty = _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1});
1719
1720 return _({
1721
1722 // $ 表示自定的属性
1723
1724 // body外的元素列表.
1725 $nonBodyContent: X( V, U, S ),
1726
1727 //块结构元素列表
1728 $block : block,
1729
1730 //内联元素列表
1731 $inline : L,
1732
1733 $inlineWithA : X(_({a:1}),L),
1734
1735 $body : X( _({script:1,style:1}), block ),
1736
1737 $cdata : _({script:1,style:1}),
1738
1739 //自闭和元素
1740 $empty : empty,
1741
1742 //不是自闭合,但不能让range选中里边
1743 $nonChild : _({iframe:1,textarea:1}),
1744 //列表元素列表
1745 $listItem : _({dd:1,dt:1,li:1}),
1746
1747 //列表根元素列表
1748 $list: _({ul:1,ol:1,dl:1}),
1749
1750 //不能认为是空的元素
1751 $isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}),
1752
1753 //如果没有子节点就可以删除的元素列表,像span,a
1754 $removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}),
1755
1756 $removeEmptyBlock : _({'p':1,'div':1}),
1757
1758 //在table元素里的元素列表
1759 $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),
1760 //不转换的标签
1761 $notTransContent : _({pre:1,script:1,style:1,textarea:1}),
1762 html: U,
1763 head: T,
1764 style: N,
1765 script: N,
1766 body: P,
1767 base: {},
1768 link: {},
1769 meta: {},
1770 title: N,
1771 col : {},
1772 tr : _({td:1,th:1}),
1773 img : {},
1774 embed: {},
1775 colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),
1776 noscript : P,
1777 td : P,
1778 br : {},
1779 th : P,
1780 center : P,
1781 kbd : L,
1782 button : X( I, E ),
1783 basefont : {},
1784 h5 : L,
1785 h4 : L,
1786 samp : L,
1787 h6 : L,
1788 ol : Q,
1789 h1 : L,
1790 h3 : L,
1791 option : N,
1792 h2 : L,
1793 form : X( A, D, E, I ),
1794 select : _({optgroup:1,option:1}),
1795 font : L,
1796 ins : L,
1797 menu : Q,
1798 abbr : L,
1799 label : L,
1800 table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),
1801 code : L,
1802 tfoot : M,
1803 cite : L,
1804 li : P,
1805 input : {},
1806 iframe : P,
1807 strong : L,
1808 textarea : N,
1809 noframes : P,
1810 big : L,
1811 small : L,
1812 //trace:
1813 span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),
1814 hr : L,
1815 dt : L,
1816 sub : L,
1817 optgroup : _({option:1}),
1818 param : {},
1819 bdo : L,
1820 'var' : L,
1821 div : P,
1822 object : O,
1823 sup : L,
1824 dd : P,
1825 strike : L,
1826 area : {},
1827 dir : Q,
1828 map : X( _({area:1,form:1,p:1}), A, F, E ),
1829 applet : O,
1830 dl : _({dt:1,dd:1}),
1831 del : L,
1832 isindex : {},
1833 fieldset : X( _({legend:1}), K ),
1834 thead : M,
1835 ul : Q,
1836 acronym : L,
1837 b : L,
1838 a : X( _({a:1}), J ),
1839 blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
1840 caption : L,
1841 i : L,
1842 u : L,
1843 tbody : M,
1844 s : L,
1845 address : X( D, I ),
1846 tt : L,
1847 legend : L,
1848 q : L,
1849 pre : X( G, C ),
1850 p : X(_({'a':1}),L),
1851 em :L,
1852 dfn : L
1853 });
1854})();
1855
1856
1857// core/domUtils.js
1858/**
1859 * Dom操作工具包
1860 * @file
1861 * @module UE.dom.domUtils
1862 * @since 1.2.6.1
1863 */
1864
1865/**
1866 * Dom操作工具包
1867 * @unfile
1868 * @module UE.dom.domUtils
1869 */
1870function getDomNode(node, start, ltr, startFromChild, fn, guard) {
1871 var tmpNode = startFromChild && node[start],
1872 parent;
1873 !tmpNode && (tmpNode = node[ltr]);
1874 while (!tmpNode && (parent = (parent || node).parentNode)) {
1875 if (parent.tagName == 'BODY' || guard && !guard(parent)) {
1876 return null;
1877 }
1878 tmpNode = parent[ltr];
1879 }
1880 if (tmpNode && fn && !fn(tmpNode)) {
1881 return getDomNode(tmpNode, start, ltr, false, fn);
1882 }
1883 return tmpNode;
1884}
1885var attrFix = ie && browser.version < 9 ? {
1886 tabindex:"tabIndex",
1887 readonly:"readOnly",
1888 "for":"htmlFor",
1889 "class":"className",
1890 maxlength:"maxLength",
1891 cellspacing:"cellSpacing",
1892 cellpadding:"cellPadding",
1893 rowspan:"rowSpan",
1894 colspan:"colSpan",
1895 usemap:"useMap",
1896 frameborder:"frameBorder"
1897 } : {
1898 tabindex:"tabIndex",
1899 readonly:"readOnly"
1900 },
1901 styleBlock = utils.listToMap([
1902 '-webkit-box', '-moz-box', 'block' ,
1903 'list-item' , 'table' , 'table-row-group' ,
1904 'table-header-group', 'table-footer-group' ,
1905 'table-row' , 'table-column-group' , 'table-column' ,
1906 'table-cell' , 'table-caption'
1907 ]);
1908var domUtils = dom.domUtils = {
1909 //节点常量
1910 NODE_ELEMENT:1,
1911 NODE_DOCUMENT:9,
1912 NODE_TEXT:3,
1913 NODE_COMMENT:8,
1914 NODE_DOCUMENT_FRAGMENT:11,
1915
1916 //位置关系
1917 POSITION_IDENTICAL:0,
1918 POSITION_DISCONNECTED:1,
1919 POSITION_FOLLOWING:2,
1920 POSITION_PRECEDING:4,
1921 POSITION_IS_CONTAINED:8,
1922 POSITION_CONTAINS:16,
1923 //ie6使用其他的会有一段空白出现
1924 fillChar:ie && browser.version == '6' ? '\ufeff' : '\u200B',
1925 //-------------------------Node部分--------------------------------
1926 keys:{
1927 /*Backspace*/ 8:1, /*Delete*/ 46:1,
1928 /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
1929 37:1, 38:1, 39:1, 40:1,
1930 13:1 /*enter*/
1931 },
1932 /**
1933 * 获取节点A相对于节点B的位置关系
1934 * @method getPosition
1935 * @param { Node } nodeA 需要查询位置关系的节点A
1936 * @param { Node } nodeB 需要查询位置关系的节点B
1937 * @return { Number } 节点A与节点B的关系
1938 * @example
1939 * ```javascript
1940 * //output: 20
1941 * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
1942 *
1943 * switch ( position ) {
1944 *
1945 * //0
1946 * case UE.dom.domUtils.POSITION_IDENTICAL:
1947 * console.log('元素相同');
1948 * break;
1949 * //1
1950 * case UE.dom.domUtils.POSITION_DISCONNECTED:
1951 * console.log('两个节点在不同的文档中');
1952 * break;
1953 * //2
1954 * case UE.dom.domUtils.POSITION_FOLLOWING:
1955 * console.log('节点A在节点B之后');
1956 * break;
1957 * //4
1958 * case UE.dom.domUtils.POSITION_PRECEDING;
1959 * console.log('节点A在节点B之前');
1960 * break;
1961 * //8
1962 * case UE.dom.domUtils.POSITION_IS_CONTAINED:
1963 * console.log('节点A被节点B包含');
1964 * break;
1965 * case 10:
1966 * console.log('节点A被节点B包含且节点A在节点B之后');
1967 * break;
1968 * //16
1969 * case UE.dom.domUtils.POSITION_CONTAINS:
1970 * console.log('节点A包含节点B');
1971 * break;
1972 * case 20:
1973 * console.log('节点A包含节点B且节点A在节点B之前');
1974 * break;
1975 *
1976 * }
1977 * ```
1978 */
1979 getPosition:function (nodeA, nodeB) {
1980 // 如果两个节点是同一个节点
1981 if (nodeA === nodeB) {
1982 // domUtils.POSITION_IDENTICAL
1983 return 0;
1984 }
1985 var node,
1986 parentsA = [nodeA],
1987 parentsB = [nodeB];
1988 node = nodeA;
1989 while (node = node.parentNode) {
1990 // 如果nodeB是nodeA的祖先节点
1991 if (node === nodeB) {
1992 // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
1993 return 10;
1994 }
1995 parentsA.push(node);
1996 }
1997 node = nodeB;
1998 while (node = node.parentNode) {
1999 // 如果nodeA是nodeB的祖先节点
2000 if (node === nodeA) {
2001 // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
2002 return 20;
2003 }
2004 parentsB.push(node);
2005 }
2006 parentsA.reverse();
2007 parentsB.reverse();
2008 if (parentsA[0] !== parentsB[0]) {
2009 // domUtils.POSITION_DISCONNECTED
2010 return 1;
2011 }
2012 var i = -1;
2013 while (i++, parentsA[i] === parentsB[i]) {
2014 }
2015 nodeA = parentsA[i];
2016 nodeB = parentsB[i];
2017 while (nodeA = nodeA.nextSibling) {
2018 if (nodeA === nodeB) {
2019 // domUtils.POSITION_PRECEDING
2020 return 4
2021 }
2022 }
2023 // domUtils.POSITION_FOLLOWING
2024 return 2;
2025 },
2026
2027 /**
2028 * 检测节点node在父节点中的索引位置
2029 * @method getNodeIndex
2030 * @param { Node } node 需要检测的节点对象
2031 * @return { Number } 该节点在父节点中的位置
2032 * @see UE.dom.domUtils.getNodeIndex(Node,Boolean)
2033 */
2034
2035 /**
2036 * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点
2037 * @method getNodeIndex
2038 * @param { Node } node 需要检测的节点对象
2039 * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点
2040 * @return { Number } 该节点在父节点中的位置
2041 * @example
2042 * ```javascript
2043 *
2044 * var node = document.createElement("div");
2045 *
2046 * node.appendChild( document.createTextNode( "hello" ) );
2047 * node.appendChild( document.createTextNode( "world" ) );
2048 * node.appendChild( node = document.createElement( "div" ) );
2049 *
2050 * //output: 2
2051 * console.log( UE.dom.domUtils.getNodeIndex( node ) );
2052 *
2053 * //output: 1
2054 * console.log( UE.dom.domUtils.getNodeIndex( node, true ) );
2055 *
2056 * ```
2057 */
2058 getNodeIndex:function (node, ignoreTextNode) {
2059 var preNode = node,
2060 i = 0;
2061 while (preNode = preNode.previousSibling) {
2062 if (ignoreTextNode && preNode.nodeType == 3) {
2063 if(preNode.nodeType != preNode.nextSibling.nodeType ){
2064 i++;
2065 }
2066 continue;
2067 }
2068 i++;
2069 }
2070 return i;
2071 },
2072
2073 /**
2074 * 检测节点node是否在给定的document对象上
2075 * @method inDoc
2076 * @param { Node } node 需要检测的节点对象
2077 * @param { DomDocument } doc 需要检测的document对象
2078 * @return { Boolean } 该节点node是否在给定的document的dom树上
2079 * @example
2080 * ```javascript
2081 *
2082 * var node = document.createElement("div");
2083 *
2084 * //output: false
2085 * console.log( UE.do.domUtils.inDoc( node, document ) );
2086 *
2087 * document.body.appendChild( node );
2088 *
2089 * //output: true
2090 * console.log( UE.do.domUtils.inDoc( node, document ) );
2091 *
2092 * ```
2093 */
2094 inDoc:function (node, doc) {
2095 return domUtils.getPosition(node, doc) == 10;
2096 },
2097 /**
2098 * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
2099 * 查找的起点是给定node节点的父节点。
2100 * @method findParent
2101 * @param { Node } node 需要查找的节点
2102 * @param { Function } filterFn 自定义的过滤方法。
2103 * @warning 查找的终点是到body节点为止
2104 * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
2105 * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
2106 * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
2107 * @example
2108 * ```javascript
2109 * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) {
2110 *
2111 * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false
2112 * return node.tagName === "HTML";
2113 *
2114 * } );
2115 *
2116 * //output: true
2117 * console.log( filterNode === null );
2118 * ```
2119 */
2120
2121 /**
2122 * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
2123 * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点
2124 * @method findParent
2125 * @param { Node } node 需要查找的节点
2126 * @param { Function } filterFn 自定义的过滤方法。
2127 * @param { Boolean } includeSelf 查找过程是否包含自身
2128 * @warning 查找的终点是到body节点为止
2129 * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
2130 * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
2131 * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。
2132 * 反之, 过滤器第一次执行时的参数将是该节点的父节点。
2133 * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
2134 * @example
2135 * ```html
2136 * <body>
2137 *
2138 * <div id="test">
2139 * </div>
2140 *
2141 * <script type="text/javascript">
2142 *
2143 * //output: DIV, BODY
2144 * var filterNode = UE.dom.domUtils.findParent( document.getElementById( "test" ), function ( node ) {
2145 *
2146 * console.log( node.tagName );
2147 * return false;
2148 *
2149 * }, true );
2150 *
2151 * </script>
2152 * </body>
2153 * ```
2154 */
2155 findParent:function (node, filterFn, includeSelf) {
2156 if (node && !domUtils.isBody(node)) {
2157 node = includeSelf ? node : node.parentNode;
2158 while (node) {
2159 if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
2160 return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
2161 }
2162 node = node.parentNode;
2163 }
2164 }
2165 return null;
2166 },
2167 /**
2168 * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。
2169 * @method findParentByTagName
2170 * @param { Node } node 需要查找的节点对象
2171 * @param { Array } tagNames 需要查找的父节点的名称数组
2172 * @warning 查找的终点是到body节点为止
2173 * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
2174 * @example
2175 * ```javascript
2176 * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] );
2177 * //output: BODY
2178 * console.log( node.tagName );
2179 * ```
2180 */
2181
2182 /**
2183 * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node,
2184 * 否则, 起点是node的父节点。
2185 * @method findParentByTagName
2186 * @param { Node } node 需要查找的节点对象
2187 * @param { Array } tagNames 需要查找的父节点的名称数组
2188 * @param { Boolean } includeSelf 查找过程是否包含node节点自身
2189 * @warning 查找的终点是到body节点为止
2190 * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
2191 * @example
2192 * ```javascript
2193 * var queryTarget = document.getElementsByTagName("div")[0];
2194 * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true );
2195 * //output: true
2196 * console.log( queryTarget === node );
2197 * ```
2198 */
2199 findParentByTagName:function (node, tagNames, includeSelf, excludeFn) {
2200 tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
2201 return domUtils.findParent(node, function (node) {
2202 return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
2203 }, includeSelf);
2204 },
2205 /**
2206 * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。
2207 * @method findParents
2208 * @param { Node } node 需要查找的节点对象
2209 * @return { Array } 给定节点的祖先节点数组
2210 * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
2211 * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
2212 * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
2213 * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
2214 */
2215
2216 /**
2217 * 查找节点node的祖先节点集合, 如果includeSelf的值为true,
2218 * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。
2219 * @method findParents
2220 * @param { Node } node 需要查找的节点对象
2221 * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象
2222 * @return { Array } 给定节点的祖先节点数组
2223 */
2224 findParents:function (node, includeSelf, filterFn, closerFirst) {
2225 var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : [];
2226 while (node = domUtils.findParent(node, filterFn)) {
2227 parents.push(node);
2228 }
2229 return closerFirst ? parents : parents.reverse();
2230 },
2231
2232 /**
2233 * 在节点node后面插入新节点newNode
2234 * @method insertAfter
2235 * @param { Node } node 目标节点
2236 * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
2237 * @return { Node } 新插入的节点
2238 */
2239 insertAfter:function (node, newNode) {
2240 return node.nextSibling ? node.parentNode.insertBefore(newNode, node.nextSibling):
2241 node.parentNode.appendChild(newNode);
2242 },
2243
2244 /**
2245 * 删除节点node及其下属的所有节点
2246 * @method remove
2247 * @param { Node } node 需要删除的节点对象
2248 * @return { Node } 返回刚删除的节点对象
2249 * @example
2250 * ```html
2251 * <div id="test">
2252 * <div id="child">你好</div>
2253 * </div>
2254 * <script>
2255 * UE.dom.domUtils.remove( document.body, false );
2256 * //output: false
2257 * console.log( document.getElementById( "child" ) !== null );
2258 * </script>
2259 * ```
2260 */
2261
2262 /**
2263 * 删除节点node,并根据keepChildren的值决定是否保留子节点
2264 * @method remove
2265 * @param { Node } node 需要删除的节点对象
2266 * @param { Boolean } keepChildren 是否需要保留子节点
2267 * @return { Node } 返回刚删除的节点对象
2268 * @example
2269 * ```html
2270 * <div id="test">
2271 * <div id="child">你好</div>
2272 * </div>
2273 * <script>
2274 * UE.dom.domUtils.remove( document.body, true );
2275 * //output: true
2276 * console.log( document.getElementById( "child" ) !== null );
2277 * </script>
2278 * ```
2279 */
2280 remove:function (node, keepChildren) {
2281 var parent = node.parentNode,
2282 child;
2283 if (parent) {
2284 if (keepChildren && node.hasChildNodes()) {
2285 while (child = node.firstChild) {
2286 parent.insertBefore(child, node);
2287 }
2288 }
2289 parent.removeChild(node);
2290 }
2291 return node;
2292 },
2293
2294 /**
2295 * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
2296 * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
2297 * @method getNextDomNode
2298 * @param { Node } node 需要获取其后的兄弟节点的节点对象
2299 * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
2300 * @example
2301 * ```html
2302 * <body>
2303 * <div id="test">
2304 * <span></span>
2305 * </div>
2306 * <i>xxx</i>
2307 * </body>
2308 * <script>
2309 *
2310 * //output: i节点
2311 * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
2312 *
2313 * </script>
2314 * ```
2315 * @example
2316 * ```html
2317 * <body>
2318 * <div>
2319 * <span></span>
2320 * <i id="test">xxx</i>
2321 * </div>
2322 * <b>xxx</b>
2323 * </body>
2324 * <script>
2325 *
2326 * //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
2327 * //output: b节点
2328 * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
2329 *
2330 * </script>
2331 * ```
2332 */
2333
2334 /**
2335 * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
2336 * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
2337 * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
2338 * @method getNextDomNode
2339 * @param { Node } node 需要获取其后的兄弟节点的节点对象
2340 * @param { Boolean } startFromChild 查找过程是否从其子节点开始
2341 * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
2342 * @see UE.dom.domUtils.getNextDomNode(Node)
2343 */
2344 getNextDomNode:function (node, startFromChild, filterFn, guard) {
2345 return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
2346 },
2347 getPreDomNode:function (node, startFromChild, filterFn, guard) {
2348 return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
2349 },
2350 /**
2351 * 检测节点node是否属是UEditor定义的bookmark节点
2352 * @method isBookmarkNode
2353 * @private
2354 * @param { Node } node 需要检测的节点对象
2355 * @return { Boolean } 是否是bookmark节点
2356 * @example
2357 * ```html
2358 * <span id="_baidu_bookmark_1"></span>
2359 * <script>
2360 * var bookmarkNode = document.getElementById("_baidu_bookmark_1");
2361 * //output: true
2362 * console.log( UE.dom.domUtils.isBookmarkNode( bookmarkNode ) );
2363 * </script>
2364 * ```
2365 */
2366 isBookmarkNode:function (node) {
2367 return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
2368 },
2369 /**
2370 * 获取节点node所属的window对象
2371 * @method getWindow
2372 * @param { Node } node 节点对象
2373 * @return { Window } 当前节点所属的window对象
2374 * @example
2375 * ```javascript
2376 * //output: true
2377 * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
2378 * ```
2379 */
2380 getWindow:function (node) {
2381 var doc = node.ownerDocument || node;
2382 return doc.defaultView || doc.parentWindow;
2383 },
2384 /**
2385 * 获取离nodeA与nodeB最近的公共的祖先节点
2386 * @method getCommonAncestor
2387 * @param { Node } nodeA 第一个节点
2388 * @param { Node } nodeB 第二个节点
2389 * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
2390 * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
2391 * @example
2392 * ```javascript
2393 * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
2394 * //output: true
2395 * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
2396 * ```
2397 */
2398 getCommonAncestor:function (nodeA, nodeB) {
2399 if (nodeA === nodeB)
2400 return nodeA;
2401 var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
2402 while (parent = parent.parentNode) {
2403 if (parent === nodeB) {
2404 return parent;
2405 }
2406 parentsA.push(parent);
2407 }
2408 parent = nodeB;
2409 while (parent = parent.parentNode) {
2410 if (parent === nodeA)
2411 return parent;
2412 parentsB.push(parent);
2413 }
2414 parentsA.reverse();
2415 parentsB.reverse();
2416 while (i++, parentsA[i] === parentsB[i]) {
2417 }
2418 return i == 0 ? null : parentsA[i - 1];
2419
2420 },
2421 /**
2422 * 清除node节点左右连续为空的兄弟inline节点
2423 * @method clearEmptySibling
2424 * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
2425 * 则这些兄弟节点将被删除
2426 * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
2427 * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
2428 * @example
2429 * ```html
2430 * <body>
2431 * <div></div>
2432 * <span id="test"></span>
2433 * <i></i>
2434 * <b></b>
2435 * <em>xxx</em>
2436 * <span></span>
2437 * </body>
2438 * <script>
2439 *
2440 * UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
2441 *
2442 * //output: <div></div><span id="test"></span><em>xxx</em><span></span>
2443 * console.log( document.body.innerHTML );
2444 *
2445 * </script>
2446 * ```
2447 */
2448
2449 /**
2450 * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
2451 * 则忽略对右边兄弟节点的操作。
2452 * @method clearEmptySibling
2453 * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
2454 * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
2455 * 则这些兄弟节点将被删除
2456 * @see UE.dom.domUtils.clearEmptySibling(Node)
2457 */
2458
2459 /**
2460 * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
2461 * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
2462 * @method clearEmptySibling
2463 * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
2464 * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
2465 * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
2466 * 则这些兄弟节点将被删除
2467 * @see UE.dom.domUtils.clearEmptySibling(Node)
2468 */
2469 clearEmptySibling:function (node, ignoreNext, ignorePre) {
2470 function clear(next, dir) {
2471 var tmpNode;
2472 while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
2473 //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
2474 || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) {
2475 tmpNode = next[dir];
2476 domUtils.remove(next);
2477 next = tmpNode;
2478 }
2479 }
2480 !ignoreNext && clear(node.nextSibling, 'nextSibling');
2481 !ignorePre && clear(node.previousSibling, 'previousSibling');
2482 },
2483 /**
2484 * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置
2485 * @method split
2486 * @param { Node } textNode 需要拆分的文本节点对象
2487 * @param { int } offset 需要拆分的位置, 位置计算从0开始
2488 * @return { Node } 拆分后形成的新节点
2489 * @example
2490 * ```html
2491 * <div id="test">abcdef</div>
2492 * <script>
2493 * var newNode = UE.dom.domUtils.split( document.getElementById( "test" ).firstChild, 3 );
2494 * //output: def
2495 * console.log( newNode.nodeValue );
2496 * </script>
2497 * ```
2498 */
2499 split:function (node, offset) {
2500 var doc = node.ownerDocument;
2501 if (browser.ie && offset == node.nodeValue.length) {
2502 var next = doc.createTextNode('');
2503 return domUtils.insertAfter(node, next);
2504 }
2505 var retval = node.splitText(offset);
2506 //ie8下splitText不会跟新childNodes,我们手动触发他的更新
2507 if (browser.ie8) {
2508 var tmpNode = doc.createTextNode('');
2509 domUtils.insertAfter(retval, tmpNode);
2510 domUtils.remove(tmpNode);
2511 }
2512 return retval;
2513 },
2514
2515 /**
2516 * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符)
2517 * @method isWhitespace
2518 * @param { Node } node 需要检测的节点对象
2519 * @return { Boolean } 检测的节点是否为空
2520 * @example
2521 * ```html
2522 * <div id="test">
2523 *
2524 * </div>
2525 * <script>
2526 * //output: true
2527 * console.log( UE.dom.domUtils.isWhitespace( document.getElementById("test").firstChild ) );
2528 * </script>
2529 * ```
2530 */
2531 isWhitespace:function (node) {
2532 return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
2533 },
2534 /**
2535 * 获取元素element相对于viewport的位置坐标
2536 * @method getXY
2537 * @param { Node } element 需要计算位置的节点对象
2538 * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离,
2539 * y代表垂直偏移距离。
2540 *
2541 * @example
2542 * ```javascript
2543 * var location = UE.dom.domUtils.getXY( document.getElementById("test") );
2544 * //output: test的坐标为: 12, 24
2545 * console.log( 'test的坐标为: ', location.x, ',', location.y );
2546 * ```
2547 */
2548 getXY:function (element) {
2549 var x = 0, y = 0;
2550 while (element.offsetParent) {
2551 y += element.offsetTop;
2552 x += element.offsetLeft;
2553 element = element.offsetParent;
2554 }
2555 return { 'x':x, 'y':y};
2556 },
2557 /**
2558 * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
2559 * @method on
2560 * @param { Node } element 需要绑定事件的节点对象
2561 * @param { String } type 绑定的事件类型
2562 * @param { Function } handler 事件处理器
2563 * @example
2564 * ```javascript
2565 * UE.dom.domUtils.on(document.body,"click",function(e){
2566 * //e为事件对象,this为被点击元素对戏那个
2567 * });
2568 * ```
2569 */
2570
2571 /**
2572 * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
2573 * @method on
2574 * @param { Node } element 需要绑定事件的节点对象
2575 * @param { Array } type 绑定的事件类型数组
2576 * @param { Function } handler 事件处理器
2577 * @example
2578 * ```javascript
2579 * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
2580 * //evt为事件对象,this为被点击元素对象
2581 * });
2582 * ```
2583 */
2584 on:function (element, type, handler) {
2585
2586 var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
2587 k = types.length;
2588 if (k) while (k--) {
2589 type = types[k];
2590 if (element.addEventListener) {
2591 element.addEventListener(type, handler, false);
2592 } else {
2593 if (!handler._d) {
2594 handler._d = {
2595 els : []
2596 };
2597 }
2598 var key = type + handler.toString(),index = utils.indexOf(handler._d.els,element);
2599 if (!handler._d[key] || index == -1) {
2600 if(index == -1){
2601 handler._d.els.push(element);
2602 }
2603 if(!handler._d[key]){
2604 handler._d[key] = function (evt) {
2605 return handler.call(evt.srcElement, evt || window.event);
2606 };
2607 }
2608
2609
2610 element.attachEvent('on' + type, handler._d[key]);
2611 }
2612 }
2613 }
2614 element = null;
2615 },
2616 /**
2617 * 解除DOM事件绑定
2618 * @method un
2619 * @param { Node } element 需要解除事件绑定的节点对象
2620 * @param { String } type 需要接触绑定的事件类型
2621 * @param { Function } handler 对应的事件处理器
2622 * @example
2623 * ```javascript
2624 * UE.dom.domUtils.un(document.body,"click",function(evt){
2625 * //evt为事件对象,this为被点击元素对象
2626 * });
2627 * ```
2628 */
2629
2630 /**
2631 * 解除DOM事件绑定
2632 * @method un
2633 * @param { Node } element 需要解除事件绑定的节点对象
2634 * @param { Array } type 需要接触绑定的事件类型数组
2635 * @param { Function } handler 对应的事件处理器
2636 * @example
2637 * ```javascript
2638 * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
2639 * //evt为事件对象,this为被点击元素对象
2640 * });
2641 * ```
2642 */
2643 un:function (element, type, handler) {
2644 var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
2645 k = types.length;
2646 if (k) while (k--) {
2647 type = types[k];
2648 if (element.removeEventListener) {
2649 element.removeEventListener(type, handler, false);
2650 } else {
2651 var key = type + handler.toString();
2652 try{
2653 element.detachEvent('on' + type, handler._d ? handler._d[key] : handler);
2654 }catch(e){}
2655 if (handler._d && handler._d[key]) {
2656 var index = utils.indexOf(handler._d.els,element);
2657 if(index!=-1){
2658 handler._d.els.splice(index,1);
2659 }
2660 handler._d.els.length == 0 && delete handler._d[key];
2661 }
2662 }
2663 }
2664 },
2665
2666 /**
2667 * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
2668 * @method isSameElement
2669 * @param { Node } nodeA 需要比较的节点
2670 * @param { Node } nodeB 需要比较的节点
2671 * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值
2672 * @example
2673 * ```html
2674 * <span style="font-size:12px">ssss</span>
2675 * <span style="font-size:12px">bbbbb</span>
2676 * <span style="font-size:13px">ssss</span>
2677 * <span style="font-size:14px">bbbbb</span>
2678 *
2679 * <script>
2680 *
2681 * var nodes = document.getElementsByTagName( "span" );
2682 *
2683 * //output: true
2684 * console.log( UE.dom.domUtils.isSameElement( nodes[0], nodes[1] ) );
2685 *
2686 * //output: false
2687 * console.log( UE.dom.domUtils.isSameElement( nodes[2], nodes[3] ) );
2688 *
2689 * </script>
2690 * ```
2691 */
2692 isSameElement:function (nodeA, nodeB) {
2693 if (nodeA.tagName != nodeB.tagName) {
2694 return false;
2695 }
2696 var thisAttrs = nodeA.attributes,
2697 otherAttrs = nodeB.attributes;
2698 if (!ie && thisAttrs.length != otherAttrs.length) {
2699 return false;
2700 }
2701 var attrA, attrB, al = 0, bl = 0;
2702 for (var i = 0; attrA = thisAttrs[i++];) {
2703 if (attrA.nodeName == 'style') {
2704 if (attrA.specified) {
2705 al++;
2706 }
2707 if (domUtils.isSameStyle(nodeA, nodeB)) {
2708 continue;
2709 } else {
2710 return false;
2711 }
2712 }
2713 if (ie) {
2714 if (attrA.specified) {
2715 al++;
2716 attrB = otherAttrs.getNamedItem(attrA.nodeName);
2717 } else {
2718 continue;
2719 }
2720 } else {
2721 attrB = nodeB.attributes[attrA.nodeName];
2722 }
2723 if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
2724 return false;
2725 }
2726 }
2727 // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
2728 if (ie) {
2729 for (i = 0; attrB = otherAttrs[i++];) {
2730 if (attrB.specified) {
2731 bl++;
2732 }
2733 }
2734 if (al != bl) {
2735 return false;
2736 }
2737 }
2738 return true;
2739 },
2740
2741 /**
2742 * 判断节点nodeA与节点nodeB的元素的style属性是否一致
2743 * @method isSameStyle
2744 * @param { Node } nodeA 需要比较的节点
2745 * @param { Node } nodeB 需要比较的节点
2746 * @return { Boolean } 两个节点是否具有相同的style属性值
2747 * @example
2748 * ```html
2749 * <span style="font-size:12px">ssss</span>
2750 * <span style="font-size:12px">bbbbb</span>
2751 * <span style="font-size:13px">ssss</span>
2752 * <span style="font-size:14px">bbbbb</span>
2753 *
2754 * <script>
2755 *
2756 * var nodes = document.getElementsByTagName( "span" );
2757 *
2758 * //output: true
2759 * console.log( UE.dom.domUtils.isSameStyle( nodes[0], nodes[1] ) );
2760 *
2761 * //output: false
2762 * console.log( UE.dom.domUtils.isSameStyle( nodes[2], nodes[3] ) );
2763 *
2764 * </script>
2765 * ```
2766 */
2767 isSameStyle:function (nodeA, nodeB) {
2768 var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
2769 styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
2770 if (browser.opera) {
2771 styleA = nodeA.style;
2772 styleB = nodeB.style;
2773 if (styleA.length != styleB.length)
2774 return false;
2775 for (var p in styleA) {
2776 if (/^(\d+|csstext)$/i.test(p)) {
2777 continue;
2778 }
2779 if (styleA[p] != styleB[p]) {
2780 return false;
2781 }
2782 }
2783 return true;
2784 }
2785 if (!styleA || !styleB) {
2786 return styleA == styleB;
2787 }
2788 styleA = styleA.split(';');
2789 styleB = styleB.split(';');
2790 if (styleA.length != styleB.length) {
2791 return false;
2792 }
2793 for (var i = 0, ci; ci = styleA[i++];) {
2794 if (utils.indexOf(styleB, ci) == -1) {
2795 return false;
2796 }
2797 }
2798 return true;
2799 },
2800 /**
2801 * 检查节点node是否为block元素
2802 * @method isBlockElm
2803 * @param { Node } node 需要检测的节点对象
2804 * @return { Boolean } 是否是block元素节点
2805 * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true;
2806 * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。
2807 * @example
2808 * ```html
2809 * <span id="test1" style="display: block"></span>
2810 * <span id="test2"></span>
2811 * <div id="test3" style="display: inline"></div>
2812 *
2813 * <script>
2814 *
2815 * //output: true
2816 * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test1") ) );
2817 *
2818 * //output: false
2819 * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test2") ) );
2820 *
2821 * //output: true
2822 * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test3") ) );
2823 *
2824 * </script>
2825 * ```
2826 */
2827 isBlockElm:function (node) {
2828 return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
2829 },
2830 /**
2831 * 检测node节点是否为body节点
2832 * @method isBody
2833 * @param { Element } node 需要检测的dom元素
2834 * @return { Boolean } 给定的元素是否是body元素
2835 * @example
2836 * ```javascript
2837 * //output: true
2838 * console.log( UE.dom.domUtils.isBody( document.body ) );
2839 * ```
2840 */
2841 isBody:function (node) {
2842 return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
2843 },
2844 /**
2845 * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点,
2846 * 拆分形成的两个节点之间是node节点
2847 * @method breakParent
2848 * @param { Node } node 作为分界的节点对象
2849 * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。
2850 * @return { Node } 给定的node分界节点
2851 * @example
2852 * ```javascript
2853 *
2854 * var node = document.createElement("span"),
2855 * wrapNode = document.createElement( "div" ),
2856 * parent = document.createElement("p");
2857 *
2858 * parent.appendChild( node );
2859 * wrapNode.appendChild( parent );
2860 *
2861 * //拆分前
2862 * //output: <p><span></span></p>
2863 * console.log( wrapNode.innerHTML );
2864 *
2865 *
2866 * UE.dom.domUtils.breakParent( node, parent );
2867 * //拆分后
2868 * //output: <p></p><span></span><p></p>
2869 * console.log( wrapNode.innerHTML );
2870 *
2871 * ```
2872 */
2873 breakParent:function (node, parent) {
2874 var tmpNode,
2875 parentClone = node,
2876 clone = node,
2877 leftNodes,
2878 rightNodes;
2879 do {
2880 parentClone = parentClone.parentNode;
2881 if (leftNodes) {
2882 tmpNode = parentClone.cloneNode(false);
2883 tmpNode.appendChild(leftNodes);
2884 leftNodes = tmpNode;
2885 tmpNode = parentClone.cloneNode(false);
2886 tmpNode.appendChild(rightNodes);
2887 rightNodes = tmpNode;
2888 } else {
2889 leftNodes = parentClone.cloneNode(false);
2890 rightNodes = leftNodes.cloneNode(false);
2891 }
2892 while (tmpNode = clone.previousSibling) {
2893 leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
2894 }
2895 while (tmpNode = clone.nextSibling) {
2896 rightNodes.appendChild(tmpNode);
2897 }
2898 clone = parentClone;
2899 } while (parent !== parentClone);
2900 tmpNode = parent.parentNode;
2901 tmpNode.insertBefore(leftNodes, parent);
2902 tmpNode.insertBefore(rightNodes, parent);
2903 tmpNode.insertBefore(node, rightNodes);
2904 domUtils.remove(parent);
2905 return node;
2906 },
2907 /**
2908 * 检查节点node是否是空inline节点
2909 * @method isEmptyInlineElement
2910 * @param { Node } node 需要检测的节点对象
2911 * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。
2912 * @example
2913 * ```html
2914 * <b><i></i></b> => 1
2915 * <b><i></i><u></u></b> => 1
2916 * <b></b> => 1
2917 * <b>xx<i></i></b> => 0
2918 * ```
2919 */
2920 isEmptyInlineElement:function (node) {
2921 if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
2922 return 0;
2923 }
2924 node = node.firstChild;
2925 while (node) {
2926 //如果是创建的bookmark就跳过
2927 if (domUtils.isBookmarkNode(node)) {
2928 return 0;
2929 }
2930 if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
2931 node.nodeType == 3 && !domUtils.isWhitespace(node)
2932 ) {
2933 return 0;
2934 }
2935 node = node.nextSibling;
2936 }
2937 return 1;
2938
2939 },
2940
2941 /**
2942 * 删除node节点下首尾两端的空白文本子节点
2943 * @method trimWhiteTextNode
2944 * @param { Element } node 需要执行删除操作的元素对象
2945 * @example
2946 * ```javascript
2947 * var node = document.createElement("div");
2948 *
2949 * node.appendChild( document.createTextNode( "" ) );
2950 *
2951 * node.appendChild( document.createElement("div") );
2952 *
2953 * node.appendChild( document.createTextNode( "" ) );
2954 *
2955 * //3
2956 * console.log( node.childNodes.length );
2957 *
2958 * UE.dom.domUtils.trimWhiteTextNode( node );
2959 *
2960 * //1
2961 * console.log( node.childNodes.length );
2962 * ```
2963 */
2964 trimWhiteTextNode:function (node) {
2965 function remove(dir) {
2966 var child;
2967 while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
2968 node.removeChild(child);
2969 }
2970 }
2971 remove('firstChild');
2972 remove('lastChild');
2973 },
2974
2975 /**
2976 * 合并node节点下相同的子节点
2977 * @name mergeChild
2978 * @desc
2979 * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
2980 * @example
2981 * <p><span style="font-size:12px;">xx<span style="font-size:12px;">aa</span>xx</span></p>
2982 * ==> UE.dom.domUtils.mergeChild(node,'span')
2983 * <p><span style="font-size:12px;">xxaaxx</span></p>
2984 */
2985 mergeChild:function (node, tagName, attrs) {
2986 var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
2987 for (var i = 0, ci; ci = list[i++];) {
2988 if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
2989 continue;
2990 }
2991 //span单独处理
2992 if (ci.tagName.toLowerCase() == 'span') {
2993 if (node === ci.parentNode) {
2994 domUtils.trimWhiteTextNode(node);
2995 if (node.childNodes.length == 1) {
2996 node.style.cssText = ci.style.cssText + ";" + node.style.cssText;
2997 domUtils.remove(ci, true);
2998 continue;
2999 }
3000 }
3001 ci.style.cssText = node.style.cssText + ';' + ci.style.cssText;
3002 if (attrs) {
3003 var style = attrs.style;
3004 if (style) {
3005 style = style.split(';');
3006 for (var j = 0, s; s = style[j++];) {
3007 ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1];
3008 }
3009 }
3010 }
3011 if (domUtils.isSameStyle(ci, node)) {
3012 domUtils.remove(ci, true);
3013 }
3014 continue;
3015 }
3016 if (domUtils.isSameElement(node, ci)) {
3017 domUtils.remove(ci, true);
3018 }
3019 }
3020 },
3021
3022 /**
3023 * 原生方法getElementsByTagName的封装
3024 * @method getElementsByTagName
3025 * @param { Node } node 目标节点对象
3026 * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割
3027 * @return { Array } 符合条件的节点集合
3028 */
3029 getElementsByTagName:function (node, name,filter) {
3030 if(filter && utils.isString(filter)){
3031 var className = filter;
3032 filter = function(node){return domUtils.hasClass(node,className)}
3033 }
3034 name = utils.trim(name).replace(/[ ]{2,}/g,' ').split(' ');
3035 var arr = [];
3036 for(var n = 0,ni;ni=name[n++];){
3037 var list = node.getElementsByTagName(ni);
3038 for (var i = 0, ci; ci = list[i++];) {
3039 if(!filter || filter(ci))
3040 arr.push(ci);
3041 }
3042 }
3043
3044 return arr;
3045 },
3046 /**
3047 * 将节点node提取到父节点上
3048 * @method mergeToParent
3049 * @param { Element } node 需要提取的元素对象
3050 * @example
3051 * ```html
3052 * <div id="parent">
3053 * <div id="sub">
3054 * <span id="child"></span>
3055 * </div>
3056 * </div>
3057 *
3058 * <script>
3059 *
3060 * var child = document.getElementById( "child" );
3061 *
3062 * //output: sub
3063 * console.log( child.parentNode.id );
3064 *
3065 * UE.dom.domUtils.mergeToParent( child );
3066 *
3067 * //output: parent
3068 * console.log( child.parentNode.id );
3069 *
3070 * </script>
3071 * ```
3072 */
3073 mergeToParent:function (node) {
3074 var parent = node.parentNode;
3075 while (parent && dtd.$removeEmpty[parent.tagName]) {
3076 if (parent.tagName == node.tagName || parent.tagName == 'A') {//针对a标签单独处理
3077 domUtils.trimWhiteTextNode(parent);
3078 //span需要特殊处理 不处理这样的情况 <span stlye="color:#fff">xxx<span style="color:#ccc">xxx</span>xxx</span>
3079 if (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node)
3080 || (parent.tagName == 'A' && node.tagName == 'SPAN')) {
3081 if (parent.childNodes.length > 1 || parent !== node.parentNode) {
3082 node.style.cssText = parent.style.cssText + ";" + node.style.cssText;
3083 parent = parent.parentNode;
3084 continue;
3085 } else {
3086 parent.style.cssText += ";" + node.style.cssText;
3087 //trace:952 a标签要保持下划线
3088 if (parent.tagName == 'A') {
3089 parent.style.textDecoration = 'underline';
3090 }
3091 }
3092 }
3093 if (parent.tagName != 'A') {
3094 parent === node.parentNode && domUtils.remove(node, true);
3095 break;
3096 }
3097 }
3098 parent = parent.parentNode;
3099 }
3100 },
3101 /**
3102 * 合并节点node的左右兄弟节点
3103 * @method mergeSibling
3104 * @param { Element } node 需要合并的目标节点
3105 * @example
3106 * ```html
3107 * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
3108 *
3109 * <script>
3110 * var demoNode = document.getElementById("test");
3111 * UE.dom.domUtils.mergeSibling( demoNode );
3112 * //output: xxxxoooxxxx
3113 * console.log( demoNode.innerHTML );
3114 * </script>
3115 * ```
3116 */
3117
3118 /**
3119 * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
3120 * @method mergeSibling
3121 * @param { Element } node 需要合并的目标节点
3122 * @param { Boolean } ignorePre 是否忽略合并左节点
3123 * @example
3124 * ```html
3125 * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
3126 *
3127 * <script>
3128 * var demoNode = document.getElementById("test");
3129 * UE.dom.domUtils.mergeSibling( demoNode, true );
3130 * //output: oooxxxx
3131 * console.log( demoNode.innerHTML );
3132 * </script>
3133 * ```
3134 */
3135
3136 /**
3137 * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
3138 * @method mergeSibling
3139 * @param { Element } node 需要合并的目标节点
3140 * @param { Boolean } ignorePre 是否忽略合并左节点
3141 * @param { Boolean } ignoreNext 是否忽略合并右节点
3142 * @remind 如果同时忽略左右节点, 则该操作什么也不会做
3143 * @example
3144 * ```html
3145 * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
3146 *
3147 * <script>
3148 * var demoNode = document.getElementById("test");
3149 * UE.dom.domUtils.mergeSibling( demoNode, false, true );
3150 * //output: xxxxooo
3151 * console.log( demoNode.innerHTML );
3152 * </script>
3153 * ```
3154 */
3155 mergeSibling:function (node, ignorePre, ignoreNext) {
3156 function merge(rtl, start, node) {
3157 var next;
3158 if ((next = node[rtl]) && !domUtils.isBookmarkNode(next) && next.nodeType == 1 && domUtils.isSameElement(node, next)) {
3159 while (next.firstChild) {
3160 if (start == 'firstChild') {
3161 node.insertBefore(next.lastChild, node.firstChild);
3162 } else {
3163 node.appendChild(next.firstChild);
3164 }
3165 }
3166 domUtils.remove(next);
3167 }
3168 }
3169 !ignorePre && merge('previousSibling', 'firstChild', node);
3170 !ignoreNext && merge('nextSibling', 'lastChild', node);
3171 },
3172
3173 /**
3174 * 设置节点node及其子节点不会被选中
3175 * @method unSelectable
3176 * @param { Element } node 需要执行操作的dom元素
3177 * @remind 执行该操作后的节点, 将不能被鼠标选中
3178 * @example
3179 * ```javascript
3180 * UE.dom.domUtils.unSelectable( document.body );
3181 * ```
3182 */
3183 unSelectable:ie && browser.ie9below || browser.opera ? function (node) {
3184 //for ie9
3185 node.onselectstart = function () {
3186 return false;
3187 };
3188 node.onclick = node.onkeyup = node.onkeydown = function () {
3189 return false;
3190 };
3191 node.unselectable = 'on';
3192 node.setAttribute("unselectable", "on");
3193 for (var i = 0, ci; ci = node.all[i++];) {
3194 switch (ci.tagName.toLowerCase()) {
3195 case 'iframe' :
3196 case 'textarea' :
3197 case 'input' :
3198 case 'select' :
3199 break;
3200 default :
3201 ci.unselectable = 'on';
3202 node.setAttribute("unselectable", "on");
3203 }
3204 }
3205 } : function (node) {
3206 node.style.MozUserSelect =
3207 node.style.webkitUserSelect =
3208 node.style.msUserSelect =
3209 node.style.KhtmlUserSelect = 'none';
3210 },
3211 /**
3212 * 删除节点node上的指定属性名称的属性
3213 * @method removeAttributes
3214 * @param { Node } node 需要删除属性的节点对象
3215 * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性
3216 * @example
3217 * ```html
3218 * <div id="wrap">
3219 * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
3220 * </div>
3221 *
3222 * <script>
3223 *
3224 * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), "id name" );
3225 *
3226 * //output: <span style="font-size:14px;">xxxxx</span>
3227 * console.log( document.getElementById("wrap").innerHTML );
3228 *
3229 * </script>
3230 * ```
3231 */
3232
3233 /**
3234 * 删除节点node上的指定属性名称的属性
3235 * @method removeAttributes
3236 * @param { Node } node 需要删除属性的节点对象
3237 * @param { Array } attrNames 需要删除的属性名数组
3238 * @example
3239 * ```html
3240 * <div id="wrap">
3241 * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
3242 * </div>
3243 *
3244 * <script>
3245 *
3246 * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), ["id", "name"] );
3247 *
3248 * //output: <span style="font-size:14px;">xxxxx</span>
3249 * console.log( document.getElementById("wrap").innerHTML );
3250 *
3251 * </script>
3252 * ```
3253 */
3254 removeAttributes:function (node, attrNames) {
3255 attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g,' ').split(' ');
3256 for (var i = 0, ci; ci = attrNames[i++];) {
3257 ci = attrFix[ci] || ci;
3258 switch (ci) {
3259 case 'className':
3260 node[ci] = '';
3261 break;
3262 case 'style':
3263 node.style.cssText = '';
3264 var val = node.getAttributeNode('style');
3265 !browser.ie && val && node.removeAttributeNode(val);
3266 }
3267 node.removeAttribute(ci);
3268 }
3269 },
3270 /**
3271 * 在doc下创建一个标签名为tag,属性为attrs的元素
3272 * @method createElement
3273 * @param { DomDocument } doc 新创建的元素属于该document节点创建
3274 * @param { String } tagName 需要创建的元素的标签名
3275 * @param { Object } attrs 新创建的元素的属性key-value集合
3276 * @return { Element } 新创建的元素对象
3277 * @example
3278 * ```javascript
3279 * var ele = UE.dom.domUtils.createElement( document, 'div', {
3280 * id: 'test'
3281 * } );
3282 *
3283 * //output: DIV
3284 * console.log( ele.tagName );
3285 *
3286 * //output: test
3287 * console.log( ele.id );
3288 *
3289 * ```
3290 */
3291 createElement:function (doc, tag, attrs) {
3292 return domUtils.setAttributes(doc.createElement(tag), attrs)
3293 },
3294 /**
3295 * 为节点node添加属性attrs,attrs为属性键值对
3296 * @method setAttributes
3297 * @param { Element } node 需要设置属性的元素对象
3298 * @param { Object } attrs 需要设置的属性名-值对
3299 * @return { Element } 设置属性的元素对象
3300 * @example
3301 * ```html
3302 * <span id="test"></span>
3303 *
3304 * <script>
3305 *
3306 * var testNode = UE.dom.domUtils.setAttributes( document.getElementById( "test" ), {
3307 * id: 'demo'
3308 * } );
3309 *
3310 * //output: demo
3311 * console.log( testNode.id );
3312 *
3313 * </script>
3314 *
3315 */
3316 setAttributes:function (node, attrs) {
3317 for (var attr in attrs) {
3318 if(attrs.hasOwnProperty(attr)){
3319 var value = attrs[attr];
3320 switch (attr) {
3321 case 'class':
3322 //ie下要这样赋值,setAttribute不起作用
3323 node.className = value;
3324 break;
3325 case 'style' :
3326 node.style.cssText = node.style.cssText + ";" + value;
3327 break;
3328 case 'innerHTML':
3329 node[attr] = value;
3330 break;
3331 case 'value':
3332 node.value = value;
3333 break;
3334 default:
3335 node.setAttribute(attrFix[attr] || attr, value);
3336 }
3337 }
3338 }
3339 return node;
3340 },
3341
3342 /**
3343 * 获取元素element经过计算后的样式值
3344 * @method getComputedStyle
3345 * @param { Element } element 需要获取样式的元素对象
3346 * @param { String } styleName 需要获取的样式名
3347 * @return { String } 获取到的样式值
3348 * @example
3349 * ```html
3350 * <style type="text/css">
3351 * #test {
3352 * font-size: 15px;
3353 * }
3354 * </style>
3355 *
3356 * <span id="test"></span>
3357 *
3358 * <script>
3359 * //output: 15px
3360 * console.log( UE.dom.domUtils.getComputedStyle( document.getElementById( "test" ), 'font-size' ) );
3361 * </script>
3362 * ```
3363 */
3364 getComputedStyle:function (element, styleName) {
3365 //一下的属性单独处理
3366 var pros = 'width height top left';
3367
3368 if(pros.indexOf(styleName) > -1){
3369 return element['offset' + styleName.replace(/^\w/,function(s){return s.toUpperCase()})] + 'px';
3370 }
3371 //忽略文本节点
3372 if (element.nodeType == 3) {
3373 element = element.parentNode;
3374 }
3375 //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
3376 if (browser.ie && browser.version < 9 && styleName == 'font-size' && !element.style.fontSize &&
3377 !dtd.$empty[element.tagName] && !dtd.$nonChild[element.tagName]) {
3378 var span = element.ownerDocument.createElement('span');
3379 span.style.cssText = 'padding:0;border:0;font-family:simsun;';
3380 span.innerHTML = '.';
3381 element.appendChild(span);
3382 var result = span.offsetHeight;
3383 element.removeChild(span);
3384 span = null;
3385 return result + 'px';
3386 }
3387 try {
3388 var value = domUtils.getStyle(element, styleName) ||
3389 (window.getComputedStyle ? domUtils.getWindow(element).getComputedStyle(element, '').getPropertyValue(styleName) :
3390 ( element.currentStyle || element.style )[utils.cssStyleToDomStyle(styleName)]);
3391
3392 } catch (e) {
3393 return "";
3394 }
3395 return utils.transUnitToPx(utils.fixColor(styleName, value));
3396 },
3397 /**
3398 * 删除元素element指定的className
3399 * @method removeClasses
3400 * @param { Element } ele 需要删除class的元素节点
3401 * @param { String } classNames 需要删除的className, 多个className之间以空格分开
3402 * @example
3403 * ```html
3404 * <span id="test" class="test1 test2 test3">xxx</span>
3405 *
3406 * <script>
3407 *
3408 * var testNode = document.getElementById( "test" );
3409 * UE.dom.domUtils.removeClasses( testNode, "test1 test2" );
3410 *
3411 * //output: test3
3412 * console.log( testNode.className );
3413 *
3414 * </script>
3415 * ```
3416 */
3417
3418 /**
3419 * 删除元素element指定的className
3420 * @method removeClasses
3421 * @param { Element } ele 需要删除class的元素节点
3422 * @param { Array } classNames 需要删除的className数组
3423 * @example
3424 * ```html
3425 * <span id="test" class="test1 test2 test3">xxx</span>
3426 *
3427 * <script>
3428 *
3429 * var testNode = document.getElementById( "test" );
3430 * UE.dom.domUtils.removeClasses( testNode, ["test1", "test2"] );
3431 *
3432 * //output: test3
3433 * console.log( testNode.className );
3434 *
3435 * </script>
3436 * ```
3437 */
3438 removeClasses:function (elm, classNames) {
3439 classNames = utils.isArray(classNames) ? classNames :
3440 utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' ');
3441 for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){
3442 cls = cls.replace(new RegExp('\\b' + ci + '\\b'),'')
3443 }
3444 cls = utils.trim(cls).replace(/[ ]{2,}/g,' ');
3445 if(cls){
3446 elm.className = cls;
3447 }else{
3448 domUtils.removeAttributes(elm,['class']);
3449 }
3450 },
3451 /**
3452 * 给元素element添加className
3453 * @method addClass
3454 * @param { Node } ele 需要增加className的元素
3455 * @param { String } classNames 需要添加的className, 多个className之间以空格分割
3456 * @remind 相同的类名不会被重复添加
3457 * @example
3458 * ```html
3459 * <span id="test" class="cls1 cls2"></span>
3460 *
3461 * <script>
3462 * var testNode = document.getElementById("test");
3463 *
3464 * UE.dom.domUtils.addClass( testNode, "cls2 cls3 cls4" );
3465 *
3466 * //output: cl1 cls2 cls3 cls4
3467 * console.log( testNode.className );
3468 *
3469 * <script>
3470 * ```
3471 */
3472
3473 /**
3474 * 给元素element添加className
3475 * @method addClass
3476 * @param { Node } ele 需要增加className的元素
3477 * @param { Array } classNames 需要添加的className的数组
3478 * @remind 相同的类名不会被重复添加
3479 * @example
3480 * ```html
3481 * <span id="test" class="cls1 cls2"></span>
3482 *
3483 * <script>
3484 * var testNode = document.getElementById("test");
3485 *
3486 * UE.dom.domUtils.addClass( testNode, ["cls2", "cls3", "cls4"] );
3487 *
3488 * //output: cl1 cls2 cls3 cls4
3489 * console.log( testNode.className );
3490 *
3491 * <script>
3492 * ```
3493 */
3494 addClass:function (elm, classNames) {
3495 if(!elm)return;
3496 classNames = utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' ');
3497 for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){
3498 if(!new RegExp('\\b' + ci + '\\b').test(cls)){
3499 cls += ' ' + ci;
3500 }
3501 }
3502 elm.className = utils.trim(cls);
3503 },
3504 /**
3505 * 判断元素element是否包含给定的样式类名className
3506 * @method hasClass
3507 * @param { Node } ele 需要检测的元素
3508 * @param { String } classNames 需要检测的className, 多个className之间用空格分割
3509 * @return { Boolean } 元素是否包含所有给定的className
3510 * @example
3511 * ```html
3512 * <span id="test1" class="cls1 cls2"></span>
3513 *
3514 * <script>
3515 * var test1 = document.getElementById("test1");
3516 *
3517 * //output: false
3518 * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1 cls3" ) );
3519 *
3520 * //output: true
3521 * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1" ) );
3522 * </script>
3523 * ```
3524 */
3525
3526 /**
3527 * 判断元素element是否包含给定的样式类名className
3528 * @method hasClass
3529 * @param { Node } ele 需要检测的元素
3530 * @param { Array } classNames 需要检测的className数组
3531 * @return { Boolean } 元素是否包含所有给定的className
3532 * @example
3533 * ```html
3534 * <span id="test1" class="cls1 cls2"></span>
3535 *
3536 * <script>
3537 * var test1 = document.getElementById("test1");
3538 *
3539 * //output: false
3540 * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1", "cls3" ] ) );
3541 *
3542 * //output: true
3543 * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1" ]) );
3544 * </script>
3545 * ```
3546 */
3547 hasClass:function (element, className) {
3548 if(utils.isRegExp(className)){
3549 return className.test(element.className)
3550 }
3551 className = utils.trim(className).replace(/[ ]{2,}/g,' ').split(' ');
3552 for(var i = 0,ci,cls = element.className;ci=className[i++];){
3553 if(!new RegExp('\\b' + ci + '\\b','i').test(cls)){
3554 return false;
3555 }
3556 }
3557 return i - 1 == className.length;
3558 },
3559
3560 /**
3561 * 阻止事件默认行为
3562 * @method preventDefault
3563 * @param { Event } evt 需要阻止默认行为的事件对象
3564 * @example
3565 * ```javascript
3566 * UE.dom.domUtils.preventDefault( evt );
3567 * ```
3568 */
3569 preventDefault:function (evt) {
3570 evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
3571 },
3572 /**
3573 * 删除元素element指定的样式
3574 * @method removeStyle
3575 * @param { Element } element 需要删除样式的元素
3576 * @param { String } styleName 需要删除的样式名
3577 * @example
3578 * ```html
3579 * <span id="test" style="color: red; background: blue;"></span>
3580 *
3581 * <script>
3582 *
3583 * var testNode = document.getElementById("test");
3584 *
3585 * UE.dom.domUtils.removeStyle( testNode, 'color' );
3586 *
3587 * //output: background: blue;
3588 * console.log( testNode.style.cssText );
3589 *
3590 * </script>
3591 * ```
3592 */
3593 removeStyle:function (element, name) {
3594 if(browser.ie ){
3595 //针对color先单独处理一下
3596 if(name == 'color'){
3597 name = '(^|;)' + name;
3598 }
3599 element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'')
3600 }else{
3601 if (element.style.removeProperty) {
3602 element.style.removeProperty (name);
3603 }else {
3604 element.style.removeAttribute (utils.cssStyleToDomStyle(name));
3605 }
3606 }
3607
3608
3609 if (!element.style.cssText) {
3610 domUtils.removeAttributes(element, ['style']);
3611 }
3612 },
3613 /**
3614 * 获取元素element的style属性的指定值
3615 * @method getStyle
3616 * @param { Element } element 需要获取属性值的元素
3617 * @param { String } styleName 需要获取的style的名称
3618 * @warning 该方法仅获取元素style属性中所标明的值
3619 * @return { String } 该元素包含指定的style属性值
3620 * @example
3621 * ```html
3622 * <div id="test" style="color: red;"></div>
3623 *
3624 * <script>
3625 *
3626 * var testNode = document.getElementById( "test" );
3627 *
3628 * //output: red
3629 * console.log( UE.dom.domUtils.getStyle( testNode, "color" ) );
3630 *
3631 * //output: ""
3632 * console.log( UE.dom.domUtils.getStyle( testNode, "background" ) );
3633 *
3634 * </script>
3635 * ```
3636 */
3637 getStyle:function (element, name) {
3638 var value = element.style[ utils.cssStyleToDomStyle(name) ];
3639 return utils.fixColor(name, value);
3640 },
3641 /**
3642 * 为元素element设置样式属性值
3643 * @method setStyle
3644 * @param { Element } element 需要设置样式的元素
3645 * @param { String } styleName 样式名
3646 * @param { String } styleValue 样式值
3647 * @example
3648 * ```html
3649 * <div id="test"></div>
3650 *
3651 * <script>
3652 *
3653 * var testNode = document.getElementById( "test" );
3654 *
3655 * //output: ""
3656 * console.log( testNode.style.color );
3657 *
3658 * UE.dom.domUtils.setStyle( testNode, 'color', 'red' );
3659 * //output: "red"
3660 * console.log( testNode.style.color );
3661 *
3662 * </script>
3663 * ```
3664 */
3665 setStyle:function (element, name, value) {
3666 element.style[utils.cssStyleToDomStyle(name)] = value;
3667 if(!utils.trim(element.style.cssText)){
3668 this.removeAttributes(element,'style')
3669 }
3670 },
3671 /**
3672 * 为元素element设置多个样式属性值
3673 * @method setStyles
3674 * @param { Element } element 需要设置样式的元素
3675 * @param { Object } styles 样式名值对
3676 * @example
3677 * ```html
3678 * <div id="test"></div>
3679 *
3680 * <script>
3681 *
3682 * var testNode = document.getElementById( "test" );
3683 *
3684 * //output: ""
3685 * console.log( testNode.style.color );
3686 *
3687 * UE.dom.domUtils.setStyles( testNode, {
3688 * 'color': 'red'
3689 * } );
3690 * //output: "red"
3691 * console.log( testNode.style.color );
3692 *
3693 * </script>
3694 * ```
3695 */
3696 setStyles:function (element, styles) {
3697 for (var name in styles) {
3698 if (styles.hasOwnProperty(name)) {
3699 domUtils.setStyle(element, name, styles[name]);
3700 }
3701 }
3702 },
3703 /**
3704 * 删除_moz_dirty属性
3705 * @private
3706 * @method removeDirtyAttr
3707 */
3708 removeDirtyAttr:function (node) {
3709 for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
3710 ci.removeAttribute('_moz_dirty');
3711 }
3712 node.removeAttribute('_moz_dirty');
3713 },
3714 /**
3715 * 获取子节点的数量
3716 * @method getChildCount
3717 * @param { Element } node 需要检测的元素
3718 * @return { Number } 给定的node元素的子节点数量
3719 * @example
3720 * ```html
3721 * <div id="test">
3722 * <span></span>
3723 * </div>
3724 *
3725 * <script>
3726 *
3727 * //output: 3
3728 * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test") ) );
3729 *
3730 * </script>
3731 * ```
3732 */
3733
3734 /**
3735 * 根据给定的过滤规则, 获取符合条件的子节点的数量
3736 * @method getChildCount
3737 * @param { Element } node 需要检测的元素
3738 * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
3739 * @return { Number } 符合过滤条件的node元素的子节点数量
3740 * @example
3741 * ```html
3742 * <div id="test">
3743 * <span></span>
3744 * </div>
3745 *
3746 * <script>
3747 *
3748 * //output: 1
3749 * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test"), function ( node ) {
3750 *
3751 * return node.nodeType === 1;
3752 *
3753 * } ) );
3754 *
3755 * </script>
3756 * ```
3757 */
3758 getChildCount:function (node, fn) {
3759 var count = 0, first = node.firstChild;
3760 fn = fn || function () {
3761 return 1;
3762 };
3763 while (first) {
3764 if (fn(first)) {
3765 count++;
3766 }
3767 first = first.nextSibling;
3768 }
3769 return count;
3770 },
3771
3772 /**
3773 * 判断给定节点是否为空节点
3774 * @method isEmptyNode
3775 * @param { Node } node 需要检测的节点对象
3776 * @return { Boolean } 节点是否为空
3777 * @example
3778 * ```javascript
3779 * UE.dom.domUtils.isEmptyNode( document.body );
3780 * ```
3781 */
3782 isEmptyNode:function (node) {
3783 return !node.firstChild || domUtils.getChildCount(node, function (node) {
3784 return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
3785 }) == 0
3786 },
3787 clearSelectedArr:function (nodes) {
3788 var node;
3789 while (node = nodes.pop()) {
3790 domUtils.removeAttributes(node, ['class']);
3791 }
3792 },
3793 /**
3794 * 将显示区域滚动到指定节点的位置
3795 * @method scrollToView
3796 * @param {Node} node 节点
3797 * @param {window} win window对象
3798 * @param {Number} offsetTop 距离上方的偏移量
3799 */
3800 scrollToView:function (node, win, offsetTop) {
3801 var getViewPaneSize = function () {
3802 var doc = win.document,
3803 mode = doc.compatMode == 'CSS1Compat';
3804 return {
3805 width:( mode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0,
3806 height:( mode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0
3807 };
3808 },
3809 getScrollPosition = function (win) {
3810 if ('pageXOffset' in win) {
3811 return {
3812 x:win.pageXOffset || 0,
3813 y:win.pageYOffset || 0
3814 };
3815 }
3816 else {
3817 var doc = win.document;
3818 return {
3819 x:doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
3820 y:doc.documentElement.scrollTop || doc.body.scrollTop || 0
3821 };
3822 }
3823 };
3824 var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetTop;
3825 offset += (node.offsetHeight || 0);
3826 var elementPosition = domUtils.getXY(node);
3827 offset += elementPosition.y;
3828 var currentScroll = getScrollPosition(win).y;
3829 // offset += 50;
3830 if (offset > currentScroll || offset < currentScroll - winHeight) {
3831 win.scrollTo(0, offset + (offset < 0 ? -20 : 20));
3832 }
3833 },
3834 /**
3835 * 判断给定节点是否为br
3836 * @method isBr
3837 * @param { Node } node 需要判断的节点对象
3838 * @return { Boolean } 给定的节点是否是br节点
3839 */
3840 isBr:function (node) {
3841 return node.nodeType == 1 && node.tagName == 'BR';
3842 },
3843 /**
3844 * 判断给定的节点是否是一个“填充”节点
3845 * @private
3846 * @method isFillChar
3847 * @param { Node } node 需要判断的节点
3848 * @param { Boolean } isInStart 是否从节点内容的开始位置匹配
3849 * @returns { Boolean } 节点是否是填充节点
3850 */
3851 isFillChar:function (node,isInStart) {
3852 if(node.nodeType != 3)
3853 return false;
3854 var text = node.nodeValue;
3855 if(isInStart){
3856 return new RegExp('^' + domUtils.fillChar).test(text)
3857 }
3858 return !text.replace(new RegExp(domUtils.fillChar,'g'), '').length
3859 },
3860 isStartInblock:function (range) {
3861 var tmpRange = range.cloneRange(),
3862 flag = 0,
3863 start = tmpRange.startContainer,
3864 tmp;
3865 if(start.nodeType == 1 && start.childNodes[tmpRange.startOffset]){
3866 start = start.childNodes[tmpRange.startOffset];
3867 var pre = start.previousSibling;
3868 while(pre && domUtils.isFillChar(pre)){
3869 start = pre;
3870 pre = pre.previousSibling;
3871 }
3872 }
3873 if(this.isFillChar(start,true) && tmpRange.startOffset == 1){
3874 tmpRange.setStartBefore(start);
3875 start = tmpRange.startContainer;
3876 }
3877
3878 while (start && domUtils.isFillChar(start)) {
3879 tmp = start;
3880 start = start.previousSibling
3881 }
3882 if (tmp) {
3883 tmpRange.setStartBefore(tmp);
3884 start = tmpRange.startContainer;
3885 }
3886 if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) {
3887 tmpRange.setStart(start, 0).collapse(true);
3888 }
3889 while (!tmpRange.startOffset) {
3890 start = tmpRange.startContainer;
3891 if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
3892 flag = 1;
3893 break;
3894 }
3895 var pre = tmpRange.startContainer.previousSibling,
3896 tmpNode;
3897 if (!pre) {
3898 tmpRange.setStartBefore(tmpRange.startContainer);
3899 } else {
3900 while (pre && domUtils.isFillChar(pre)) {
3901 tmpNode = pre;
3902 pre = pre.previousSibling;
3903 }
3904 if (tmpNode) {
3905 tmpRange.setStartBefore(tmpNode);
3906 } else {
3907 tmpRange.setStartBefore(tmpRange.startContainer);
3908 }
3909 }
3910 }
3911 return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0;
3912 },
3913
3914 /**
3915 * 判断给定的元素是否是一个空元素
3916 * @method isEmptyBlock
3917 * @param { Element } node 需要判断的元素
3918 * @return { Boolean } 是否是空元素
3919 * @example
3920 * ```html
3921 * <div id="test"></div>
3922 *
3923 * <script>
3924 * //output: true
3925 * console.log( UE.dom.domUtils.isEmptyBlock( document.getElementById("test") ) );
3926 * </script>
3927 * ```
3928 */
3929
3930 /**
3931 * 根据指定的判断规则判断给定的元素是否是一个空元素
3932 * @method isEmptyBlock
3933 * @param { Element } node 需要判断的元素
3934 * @param { RegExp } reg 对内容执行判断的正则表达式对象
3935 * @return { Boolean } 是否是空元素
3936 */
3937 isEmptyBlock:function (node,reg) {
3938 if(node.nodeType != 1)
3939 return 0;
3940 reg = reg || new RegExp('[ \xa0\t\r\n' + domUtils.fillChar + ']', 'g');
3941
3942 if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
3943 return 0;
3944 }
3945 for (var n in dtd.$isNotEmpty) {
3946 if (node.getElementsByTagName(n).length) {
3947 return 0;
3948 }
3949 }
3950 return 1;
3951 },
3952
3953 /**
3954 * 移动元素使得该元素的位置移动指定的偏移量的距离
3955 * @method setViewportOffset
3956 * @param { Element } element 需要设置偏移量的元素
3957 * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在
3958 * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移
3959 * offset.top的距离
3960 * @example
3961 * ```html
3962 * <div id="test" style="top: 100px; left: 50px; position: absolute;"></div>
3963 *
3964 * <script>
3965 *
3966 * var testNode = document.getElementById("test");
3967 *
3968 * UE.dom.domUtils.setViewportOffset( testNode, {
3969 * left: 200,
3970 * top: 50
3971 * } );
3972 *
3973 * //output: top: 300px; left: 100px; position: absolute;
3974 * console.log( testNode.style.cssText );
3975 *
3976 * </script>
3977 * ```
3978 */
3979 setViewportOffset:function (element, offset) {
3980 var left = parseInt(element.style.left) | 0;
3981 var top = parseInt(element.style.top) | 0;
3982 var rect = element.getBoundingClientRect();
3983 var offsetLeft = offset.left - rect.left;
3984 var offsetTop = offset.top - rect.top;
3985 if (offsetLeft) {
3986 element.style.left = left + offsetLeft + 'px';
3987 }
3988 if (offsetTop) {
3989 element.style.top = top + offsetTop + 'px';
3990 }
3991 },
3992
3993 /**
3994 * 用“填充字符”填充节点
3995 * @method fillNode
3996 * @private
3997 * @param { DomDocument } doc 填充的节点所在的docment对象
3998 * @param { Node } node 需要填充的节点对象
3999 * @example
4000 * ```html
4001 * <div id="test"></div>
4002 *
4003 * <script>
4004 * var testNode = document.getElementById("test");
4005 *
4006 * //output: 0
4007 * console.log( testNode.childNodes.length );
4008 *
4009 * UE.dom.domUtils.fillNode( document, testNode );
4010 *
4011 * //output: 1
4012 * console.log( testNode.childNodes.length );
4013 *
4014 * </script>
4015 * ```
4016 */
4017 fillNode:function (doc, node) {
4018 var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
4019 node.innerHTML = '';
4020 node.appendChild(tmpNode);
4021 },
4022
4023 /**
4024 * 把节点src的所有子节点追加到另一个节点tag上去
4025 * @method moveChild
4026 * @param { Node } src 源节点, 该节点下的所有子节点将被移除
4027 * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下
4028 * @example
4029 * ```html
4030 * <div id="test1">
4031 * <span></span>
4032 * </div>
4033 * <div id="test2">
4034 * <div></div>
4035 * </div>
4036 *
4037 * <script>
4038 *
4039 * var test1 = document.getElementById("test1"),
4040 * test2 = document.getElementById("test2");
4041 *
4042 * UE.dom.domUtils.moveChild( test1, test2 );
4043 *
4044 * //output: ""(空字符串)
4045 * console.log( test1.innerHTML );
4046 *
4047 * //output: "<div></div><span></span>"
4048 * console.log( test2.innerHTML );
4049 *
4050 * </script>
4051 * ```
4052 */
4053
4054 /**
4055 * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
4056 * @method moveChild
4057 * @param { Node } src 源节点, 该节点下的所有子节点将被移除
4058 * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
4059 * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
4060 * @example
4061 * ```html
4062 * <div id="test1">
4063 * <span></span>
4064 * </div>
4065 * <div id="test2">
4066 * <div></div>
4067 * </div>
4068 *
4069 * <script>
4070 *
4071 * var test1 = document.getElementById("test1"),
4072 * test2 = document.getElementById("test2");
4073 *
4074 * UE.dom.domUtils.moveChild( test1, test2, true );
4075 *
4076 * //output: ""(空字符串)
4077 * console.log( test1.innerHTML );
4078 *
4079 * //output: "<span></span><div></div>"
4080 * console.log( test2.innerHTML );
4081 *
4082 * </script>
4083 * ```
4084 */
4085 moveChild:function (src, tag, dir) {
4086 while (src.firstChild) {
4087 if (dir && tag.firstChild) {
4088 tag.insertBefore(src.lastChild, tag.firstChild);
4089 } else {
4090 tag.appendChild(src.firstChild);
4091 }
4092 }
4093 },
4094
4095 /**
4096 * 判断节点的标签上是否不存在任何属性
4097 * @method hasNoAttributes
4098 * @private
4099 * @param { Node } node 需要检测的节点对象
4100 * @return { Boolean } 节点是否不包含任何属性
4101 * @example
4102 * ```html
4103 * <div id="test"><span>xxxx</span></div>
4104 *
4105 * <script>
4106 *
4107 * //output: false
4108 * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test") ) );
4109 *
4110 * //output: true
4111 * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test").firstChild ) );
4112 *
4113 * </script>
4114 * ```
4115 */
4116 hasNoAttributes:function (node) {
4117 return browser.ie ? /^<\w+\s*?>/.test(node.outerHTML) : node.attributes.length == 0;
4118 },
4119
4120 /**
4121 * 检测节点是否是UEditor所使用的辅助节点
4122 * @method isCustomeNode
4123 * @private
4124 * @param { Node } node 需要检测的节点
4125 * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。
4126 * @return { Boolean } 给定的节点是否是一个辅助节点
4127 */
4128 isCustomeNode:function (node) {
4129 return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
4130 },
4131
4132 /**
4133 * 检测节点的标签是否是给定的标签
4134 * @method isTagNode
4135 * @param { Node } node 需要检测的节点对象
4136 * @param { String } tagName 标签
4137 * @return { Boolean } 节点的标签是否是给定的标签
4138 * @example
4139 * ```html
4140 * <div id="test"></div>
4141 *
4142 * <script>
4143 *
4144 * //output: true
4145 * console.log( UE.dom.domUtils.isTagNode( document.getElementById("test"), "div" ) );
4146 *
4147 * </script>
4148 * ```
4149 */
4150 isTagNode:function (node, tagNames) {
4151 return node.nodeType == 1 && new RegExp('\\b' + node.tagName + '\\b','i').test(tagNames)
4152 },
4153
4154 /**
4155 * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点
4156 * @method filterNodeList
4157 * @param { Array } nodeList 需要过滤的节点数组
4158 * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
4159 * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL
4160 * @example
4161 * ```javascript
4162 * var divNodes = document.getElementsByTagName("div");
4163 * divNodes = [].slice.call( divNodes, 0 );
4164 *
4165 * //output: null
4166 * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
4167 * return node.tagName.toLowerCase() !== 'div';
4168 * } ) );
4169 * ```
4170 */
4171
4172 /**
4173 * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点
4174 * @method filterNodeList
4175 * @param { Array } nodeList 需要过滤的节点数组
4176 * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割
4177 * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL
4178 * @example
4179 * ```javascript
4180 * var divNodes = document.getElementsByTagName("div");
4181 * divNodes = [].slice.call( divNodes, 0 );
4182 *
4183 * //output: null
4184 * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) );
4185 * ```
4186 */
4187
4188 /**
4189 * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤
4190 * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点
4191 * @method filterNodeList
4192 * @param { Array } nodeList 需要过滤的节点数组
4193 * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
4194 * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点
4195 * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足
4196 * 过滤条件的节点数组或第一个节点, 否则返回NULL
4197 * @example
4198 * ```javascript
4199 * var divNodes = document.getElementsByTagName("div");
4200 * divNodes = [].slice.call( divNodes, 0 );
4201 *
4202 * //output: 3(假定有3个div)
4203 * console.log( divNodes.length );
4204 *
4205 * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
4206 * return node.tagName.toLowerCase() === 'div';
4207 * }, true );
4208 *
4209 * //output: 3
4210 * console.log( nodes.length );
4211 *
4212 * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
4213 * return node.tagName.toLowerCase() === 'div';
4214 * }, false );
4215 *
4216 * //output: div
4217 * console.log( node.nodeName );
4218 * ```
4219 */
4220 filterNodeList : function(nodelist,filter,forAll){
4221 var results = [];
4222 if(!utils .isFunction(filter)){
4223 var str = filter;
4224 filter = function(n){
4225 return utils.indexOf(utils.isArray(str) ? str:str.split(' '), n.tagName.toLowerCase()) != -1
4226 };
4227 }
4228 utils.each(nodelist,function(n){
4229 filter(n) && results.push(n)
4230 });
4231 return results.length == 0 ? null : results.length == 1 || !forAll ? results[0] : results
4232 },
4233
4234 /**
4235 * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾
4236 * @method isInNodeEndBoundary
4237 * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL
4238 * @param node 需要检测的节点对象
4239 * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0
4240 */
4241 isInNodeEndBoundary : function (rng,node){
4242 var start = rng.startContainer;
4243 if(start.nodeType == 3 && rng.startOffset != start.nodeValue.length){
4244 return 0;
4245 }
4246 if(start.nodeType == 1 && rng.startOffset != start.childNodes.length){
4247 return 0;
4248 }
4249 while(start !== node){
4250 if(start.nextSibling){
4251 return 0
4252 };
4253 start = start.parentNode;
4254 }
4255 return 1;
4256 },
4257 isBoundaryNode : function (node,dir){
4258 var tmp;
4259 while(!domUtils.isBody(node)){
4260 tmp = node;
4261 node = node.parentNode;
4262 if(tmp !== node[dir]){
4263 return false;
4264 }
4265 }
4266 return true;
4267 },
4268 fillHtml : browser.ie11below ? '&nbsp;' : '<br/>'
4269};
4270var fillCharReg = new RegExp(domUtils.fillChar, 'g');
4271
4272// core/Range.js
4273/**
4274 * Range封装
4275 * @file
4276 * @module UE.dom
4277 * @class Range
4278 * @since 1.2.6.1
4279 */
4280
4281/**
4282 * dom操作封装
4283 * @unfile
4284 * @module UE.dom
4285 */
4286
4287/**
4288 * Range实现类,本类是UEditor底层核心类,封装不同浏览器之间的Range操作。
4289 * @unfile
4290 * @module UE.dom
4291 * @class Range
4292 */
4293
4294
4295(function () {
4296 var guid = 0,
4297 fillChar = domUtils.fillChar,
4298 fillData;
4299
4300 /**
4301 * 更新range的collapse状态
4302 * @param {Range} range range对象
4303 */
4304 function updateCollapse(range) {
4305 range.collapsed =
4306 range.startContainer && range.endContainer &&
4307 range.startContainer === range.endContainer &&
4308 range.startOffset == range.endOffset;
4309 }
4310
4311 function selectOneNode(rng){
4312 return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
4313 }
4314 function setEndPoint(toStart, node, offset, range) {
4315 //如果node是自闭合标签要处理
4316 if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
4317 offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
4318 node = node.parentNode;
4319 }
4320 if (toStart) {
4321 range.startContainer = node;
4322 range.startOffset = offset;
4323 if (!range.endContainer) {
4324 range.collapse(true);
4325 }
4326 } else {
4327 range.endContainer = node;
4328 range.endOffset = offset;
4329 if (!range.startContainer) {
4330 range.collapse(false);
4331 }
4332 }
4333 updateCollapse(range);
4334 return range;
4335 }
4336
4337 function execContentsAction(range, action) {
4338 //调整边界
4339 //range.includeBookmark();
4340 var start = range.startContainer,
4341 end = range.endContainer,
4342 startOffset = range.startOffset,
4343 endOffset = range.endOffset,
4344 doc = range.document,
4345 frag = doc.createDocumentFragment(),
4346 tmpStart, tmpEnd;
4347 if (start.nodeType == 1) {
4348 start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
4349 }
4350 if (end.nodeType == 1) {
4351 end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
4352 }
4353 if (start === end && start.nodeType == 3) {
4354 frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
4355 //is not clone
4356 if (action) {
4357 start.deleteData(startOffset, endOffset - startOffset);
4358 range.collapse(true);
4359 }
4360 return frag;
4361 }
4362 var current, currentLevel, clone = frag,
4363 startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
4364 for (var i = 0; startParents[i] == endParents[i];) {
4365 i++;
4366 }
4367 for (var j = i, si; si = startParents[j]; j++) {
4368 current = si.nextSibling;
4369 if (si == start) {
4370 if (!tmpStart) {
4371 if (range.startContainer.nodeType == 3) {
4372 clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
4373 //is not clone
4374 if (action) {
4375 start.deleteData(startOffset, start.nodeValue.length - startOffset);
4376 }
4377 } else {
4378 clone.appendChild(!action ? start.cloneNode(true) : start);
4379 }
4380 }
4381 } else {
4382 currentLevel = si.cloneNode(false);
4383 clone.appendChild(currentLevel);
4384 }
4385 while (current) {
4386 if (current === end || current === endParents[j]) {
4387 break;
4388 }
4389 si = current.nextSibling;
4390 clone.appendChild(!action ? current.cloneNode(true) : current);
4391 current = si;
4392 }
4393 clone = currentLevel;
4394 }
4395 clone = frag;
4396 if (!startParents[i]) {
4397 clone.appendChild(startParents[i - 1].cloneNode(false));
4398 clone = clone.firstChild;
4399 }
4400 for (var j = i, ei; ei = endParents[j]; j++) {
4401 current = ei.previousSibling;
4402 if (ei == end) {
4403 if (!tmpEnd && range.endContainer.nodeType == 3) {
4404 clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
4405 //is not clone
4406 if (action) {
4407 end.deleteData(0, endOffset);
4408 }
4409 }
4410 } else {
4411 currentLevel = ei.cloneNode(false);
4412 clone.appendChild(currentLevel);
4413 }
4414 //如果两端同级,右边第一次已经被开始做了
4415 if (j != i || !startParents[i]) {
4416 while (current) {
4417 if (current === start) {
4418 break;
4419 }
4420 ei = current.previousSibling;
4421 clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
4422 current = ei;
4423 }
4424 }
4425 clone = currentLevel;
4426 }
4427 if (action) {
4428 range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
4429 }
4430 tmpStart && domUtils.remove(tmpStart);
4431 tmpEnd && domUtils.remove(tmpEnd);
4432 return frag;
4433 }
4434
4435 /**
4436 * 创建一个跟document绑定的空的Range实例
4437 * @constructor
4438 * @param { Document } document 新建的选区所属的文档对象
4439 */
4440
4441 /**
4442 * @property { Node } startContainer 当前Range的开始边界的容器节点, 可以是一个元素节点或者是文本节点
4443 */
4444
4445 /**
4446 * @property { Node } startOffset 当前Range的开始边界容器节点的偏移量, 如果是元素节点,
4447 * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
4448 */
4449
4450 /**
4451 * @property { Node } endContainer 当前Range的结束边界的容器节点, 可以是一个元素节点或者是文本节点
4452 */
4453
4454 /**
4455 * @property { Node } endOffset 当前Range的结束边界容器节点的偏移量, 如果是元素节点,
4456 * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
4457 */
4458
4459 /**
4460 * @property { Boolean } collapsed 当前Range是否闭合
4461 * @default true
4462 * @remind Range是闭合的时候, startContainer === endContainer && startOffset === endOffset
4463 */
4464
4465 /**
4466 * @property { Document } document 当前Range所属的Document对象
4467 * @remind 不同range的的document属性可以是不同的
4468 */
4469 var Range = dom.Range = function (document) {
4470 var me = this;
4471 me.startContainer =
4472 me.startOffset =
4473 me.endContainer =
4474 me.endOffset = null;
4475 me.document = document;
4476 me.collapsed = true;
4477 };
4478
4479 /**
4480 * 删除fillData
4481 * @param doc
4482 * @param excludeNode
4483 */
4484 function removeFillData(doc, excludeNode) {
4485 try {
4486 if (fillData && domUtils.inDoc(fillData, doc)) {
4487 if (!fillData.nodeValue.replace(fillCharReg, '').length) {
4488 var tmpNode = fillData.parentNode;
4489 domUtils.remove(fillData);
4490 while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
4491 //safari的contains有bug
4492 (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
4493 ) {
4494 fillData = tmpNode.parentNode;
4495 domUtils.remove(tmpNode);
4496 tmpNode = fillData;
4497 }
4498 } else {
4499 fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
4500 }
4501 }
4502 } catch (e) {
4503 }
4504 }
4505
4506 /**
4507 * @param node
4508 * @param dir
4509 */
4510 function mergeSibling(node, dir) {
4511 var tmpNode;
4512 node = node[dir];
4513 while (node && domUtils.isFillChar(node)) {
4514 tmpNode = node[dir];
4515 domUtils.remove(node);
4516 node = tmpNode;
4517 }
4518 }
4519
4520 Range.prototype = {
4521
4522 /**
4523 * 克隆选区的内容到一个DocumentFragment里
4524 * @method cloneContents
4525 * @return { DocumentFragment | NULL } 如果选区是闭合的将返回null, 否则, 返回包含所clone内容的DocumentFragment元素
4526 * @example
4527 * ```html
4528 * <body>
4529 * <!-- 中括号表示选区 -->
4530 * <b>x<i>x[x</i>xx]x</b>
4531 *
4532 * <script>
4533 * //range是已选中的选区
4534 * var fragment = range.cloneContents(),
4535 * node = document.createElement("div");
4536 *
4537 * node.appendChild( fragment );
4538 *
4539 * //output: <i>x</i>xx
4540 * console.log( node.innerHTML );
4541 *
4542 * </script>
4543 * </body>
4544 * ```
4545 */
4546 cloneContents:function () {
4547 return this.collapsed ? null : execContentsAction(this, 0);
4548 },
4549
4550 /**
4551 * 删除当前选区范围中的所有内容
4552 * @method deleteContents
4553 * @remind 执行完该操作后, 当前Range对象变成了闭合状态
4554 * @return { UE.dom.Range } 当前操作的Range对象
4555 * @example
4556 * ```html
4557 * <body>
4558 * <!-- 中括号表示选区 -->
4559 * <b>x<i>x[x</i>xx]x</b>
4560 *
4561 * <script>
4562 * //range是已选中的选区
4563 * range.deleteContents();
4564 *
4565 * //竖线表示闭合后的选区位置
4566 * //output: <b>x<i>x</i>|x</b>
4567 * console.log( document.body.innerHTML );
4568 *
4569 * //此时, range的各项属性为
4570 * //output: B
4571 * console.log( range.startContainer.tagName );
4572 * //output: 2
4573 * console.log( range.startOffset );
4574 * //output: B
4575 * console.log( range.endContainer.tagName );
4576 * //output: 2
4577 * console.log( range.endOffset );
4578 * //output: true
4579 * console.log( range.collapsed );
4580 *
4581 * </script>
4582 * </body>
4583 * ```
4584 */
4585 deleteContents:function () {
4586 var txt;
4587 if (!this.collapsed) {
4588 execContentsAction(this, 1);
4589 }
4590 if (browser.webkit) {
4591 txt = this.startContainer;
4592 if (txt.nodeType == 3 && !txt.nodeValue.length) {
4593 this.setStartBefore(txt).collapse(true);
4594 domUtils.remove(txt);
4595 }
4596 }
4597 return this;
4598 },
4599
4600 /**
4601 * 将当前选区的内容提取到一个DocumentFragment里
4602 * @method extractContents
4603 * @remind 执行该操作后, 选区将变成闭合状态
4604 * @warning 执行该操作后, 原来选区所选中的内容将从dom树上剥离出来
4605 * @return { DocumentFragment } 返回包含所提取内容的DocumentFragment对象
4606 * @example
4607 * ```html
4608 * <body>
4609 * <!-- 中括号表示选区 -->
4610 * <b>x<i>x[x</i>xx]x</b>
4611 *
4612 * <script>
4613 * //range是已选中的选区
4614 * var fragment = range.extractContents(),
4615 * node = document.createElement( "div" );
4616 *
4617 * node.appendChild( fragment );
4618 *
4619 * //竖线表示闭合后的选区位置
4620 *
4621 * //output: <b>x<i>x</i>|x</b>
4622 * console.log( document.body.innerHTML );
4623 * //output: <i>x</i>xx
4624 * console.log( node.innerHTML );
4625 *
4626 * //此时, range的各项属性为
4627 * //output: B
4628 * console.log( range.startContainer.tagName );
4629 * //output: 2
4630 * console.log( range.startOffset );
4631 * //output: B
4632 * console.log( range.endContainer.tagName );
4633 * //output: 2
4634 * console.log( range.endOffset );
4635 * //output: true
4636 * console.log( range.collapsed );
4637 *
4638 * </script>
4639 * </body>
4640 */
4641 extractContents:function () {
4642 return this.collapsed ? null : execContentsAction(this, 2);
4643 },
4644
4645 /**
4646 * 设置Range的开始容器节点和偏移量
4647 * @method setStart
4648 * @remind 如果给定的节点是元素节点,那么offset指的是其子元素中索引为offset的元素,
4649 * 如果是文本节点,那么offset指的是其文本内容的第offset个字符
4650 * @remind 如果提供的容器节点是一个不能包含子元素的节点, 则该选区的开始容器将被设置
4651 * 为该节点的父节点, 此时, 其距离开始容器的偏移量也变成了该节点在其父节点
4652 * 中的索引
4653 * @param { Node } node 将被设为当前选区开始边界容器的节点对象
4654 * @param { int } offset 选区的开始位置偏移量
4655 * @return { UE.dom.Range } 当前range对象
4656 * @example
4657 * ```html
4658 * <!-- 选区 -->
4659 * <b>xxx<i>x<span>xx</span>xx<em>xx</em>xxx</i>[xxx]</b>
4660 *
4661 * <script>
4662 *
4663 * //执行操作
4664 * range.setStart( document.getElementsByTagName("i")[0], 1 );
4665 *
4666 * //此时, 选区变成了
4667 * //<b>xxx<i>x[<span>xx</span>xx<em>xx</em>xxx</i>xxx]</b>
4668 *
4669 * </script>
4670 * ```
4671 * @example
4672 * ```html
4673 * <!-- 选区 -->
4674 * <b>xxx<img>[xx]x</b>
4675 *
4676 * <script>
4677 *
4678 * //执行操作
4679 * range.setStart( document.getElementsByTagName("img")[0], 3 );
4680 *
4681 * //此时, 选区变成了
4682 * //<b>xxx[<img>xx]x</b>
4683 *
4684 * </script>
4685 * ```
4686 */
4687 setStart:function (node, offset) {
4688 return setEndPoint(true, node, offset, this);
4689 },
4690
4691 /**
4692 * 设置Range的结束容器和偏移量
4693 * @method setEnd
4694 * @param { Node } node 作为当前选区结束边界容器的节点对象
4695 * @param { int } offset 结束边界的偏移量
4696 * @see UE.dom.Range:setStart(Node,int)
4697 * @return { UE.dom.Range } 当前range对象
4698 */
4699 setEnd:function (node, offset) {
4700 return setEndPoint(false, node, offset, this);
4701 },
4702
4703 /**
4704 * 将Range开始位置设置到node节点之后
4705 * @method setStartAfter
4706 * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引+1
4707 * @param { Node } node 选区的开始边界将紧接着该节点之后
4708 * @return { UE.dom.Range } 当前range对象
4709 * @example
4710 * ```html
4711 * <!-- 选区示例 -->
4712 * <b>xx<i>xxx</i><span>xx[x</span>xxx]</b>
4713 *
4714 * <script>
4715 *
4716 * //执行操作
4717 * range.setStartAfter( document.getElementsByTagName("i")[0] );
4718 *
4719 * //结果选区
4720 * //<b>xx<i>xxx</i>[<span>xxx</span>xxx]</b>
4721 *
4722 * </script>
4723 * ```
4724 */
4725 setStartAfter:function (node) {
4726 return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
4727 },
4728
4729 /**
4730 * 将Range开始位置设置到node节点之前
4731 * @method setStartBefore
4732 * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引
4733 * @param { Node } node 新的选区开始位置在该节点之前
4734 * @see UE.dom.Range:setStartAfter(Node)
4735 * @return { UE.dom.Range } 当前range对象
4736 */
4737 setStartBefore:function (node) {
4738 return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
4739 },
4740
4741 /**
4742 * 将Range结束位置设置到node节点之后
4743 * @method setEndAfter
4744 * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引+1
4745 * @param { Node } node 目标节点
4746 * @see UE.dom.Range:setStartAfter(Node)
4747 * @return { UE.dom.Range } 当前range对象
4748 * @example
4749 * ```html
4750 * <!-- 选区示例 -->
4751 * <b>[xx<i>xxx</i><span>xx]x</span>xxx</b>
4752 *
4753 * <script>
4754 *
4755 * //执行操作
4756 * range.setStartAfter( document.getElementsByTagName("span")[0] );
4757 *
4758 * //结果选区
4759 * //<b>[xx<i>xxx</i><span>xxx</span>]xxx</b>
4760 *
4761 * </script>
4762 * ```
4763 */
4764 setEndAfter:function (node) {
4765 return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
4766 },
4767
4768 /**
4769 * 将Range结束位置设置到node节点之前
4770 * @method setEndBefore
4771 * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引
4772 * @param { Node } node 目标节点
4773 * @see UE.dom.Range:setEndAfter(Node)
4774 * @return { UE.dom.Range } 当前range对象
4775 */
4776 setEndBefore:function (node) {
4777 return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
4778 },
4779
4780 /**
4781 * 设置Range的开始位置到node节点内的第一个子节点之前
4782 * @method setStartAtFirst
4783 * @remind 选区的开始容器将变成给定的节点, 且偏移量为0
4784 * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
4785 * @param { Node } node 目标节点
4786 * @see UE.dom.Range:setStartBefore(Node)
4787 * @return { UE.dom.Range } 当前range对象
4788 * @example
4789 * ```html
4790 * <!-- 选区示例 -->
4791 * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
4792 *
4793 * <script>
4794 *
4795 * //执行操作
4796 * range.setStartAtFirst( document.getElementsByTagName("i")[0] );
4797 *
4798 * //结果选区
4799 * //<b>xx<i>[xxx</i><span>xx]x</span>xxx</b>
4800 *
4801 * </script>
4802 * ```
4803 */
4804 setStartAtFirst:function (node) {
4805 return this.setStart(node, 0);
4806 },
4807
4808 /**
4809 * 设置Range的开始位置到node节点内的最后一个节点之后
4810 * @method setStartAtLast
4811 * @remind 选区的开始容器将变成给定的节点, 且偏移量为该节点的子节点数
4812 * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
4813 * @param { Node } node 目标节点
4814 * @see UE.dom.Range:setStartAtFirst(Node)
4815 * @return { UE.dom.Range } 当前range对象
4816 */
4817 setStartAtLast:function (node) {
4818 return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
4819 },
4820
4821 /**
4822 * 设置Range的结束位置到node节点内的第一个节点之前
4823 * @method setEndAtFirst
4824 * @param { Node } node 目标节点
4825 * @remind 选区的结束容器将变成给定的节点, 且偏移量为0
4826 * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
4827 * @see UE.dom.Range:setStartAtFirst(Node)
4828 * @return { UE.dom.Range } 当前range对象
4829 */
4830 setEndAtFirst:function (node) {
4831 return this.setEnd(node, 0);
4832 },
4833
4834 /**
4835 * 设置Range的结束位置到node节点内的最后一个节点之后
4836 * @method setEndAtLast
4837 * @param { Node } node 目标节点
4838 * @remind 选区的结束容器将变成给定的节点, 且偏移量为该节点的子节点数量
4839 * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
4840 * @see UE.dom.Range:setStartAtFirst(Node)
4841 * @return { UE.dom.Range } 当前range对象
4842 */
4843 setEndAtLast:function (node) {
4844 return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
4845 },
4846
4847 /**
4848 * 选中给定节点
4849 * @method selectNode
4850 * @remind 此时, 选区的开始容器和结束容器都是该节点的父节点, 其startOffset是该节点在父节点中的位置索引,
4851 * 而endOffset为startOffset+1
4852 * @param { Node } node 需要选中的节点
4853 * @return { UE.dom.Range } 当前range对象,此时的range仅包含当前给定的节点对象
4854 * @example
4855 * ```html
4856 * <!-- 选区示例 -->
4857 * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
4858 *
4859 * <script>
4860 *
4861 * //执行操作
4862 * range.selectNode( document.getElementsByTagName("i")[0] );
4863 *
4864 * //结果选区
4865 * //<b>xx[<i>xxx</i>]<span>xxx</span>xxx</b>
4866 *
4867 * </script>
4868 * ```
4869 */
4870 selectNode:function (node) {
4871 return this.setStartBefore(node).setEndAfter(node);
4872 },
4873
4874 /**
4875 * 选中给定节点内部的所有节点
4876 * @method selectNodeContents
4877 * @remind 此时, 选区的开始容器和结束容器都是该节点, 其startOffset为0,
4878 * 而endOffset是该节点的子节点数。
4879 * @param { Node } node 目标节点, 当前range将包含该节点内的所有节点
4880 * @return { UE.dom.Range } 当前range对象, 此时range仅包含给定节点的所有子节点
4881 * @example
4882 * ```html
4883 * <!-- 选区示例 -->
4884 * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
4885 *
4886 * <script>
4887 *
4888 * //执行操作
4889 * range.selectNode( document.getElementsByTagName("b")[0] );
4890 *
4891 * //结果选区
4892 * //<b>[xx<i>xxx</i><span>xxx</span>xxx]</b>
4893 *
4894 * </script>
4895 * ```
4896 */
4897 selectNodeContents:function (node) {
4898 return this.setStart(node, 0).setEndAtLast(node);
4899 },
4900
4901 /**
4902 * clone当前Range对象
4903 * @method cloneRange
4904 * @remind 返回的range是一个全新的range对象, 其内部所有属性与当前被clone的range相同。
4905 * @return { UE.dom.Range } 当前range对象的一个副本
4906 */
4907 cloneRange:function () {
4908 var me = this;
4909 return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
4910
4911 },
4912
4913 /**
4914 * 向当前选区的结束处闭合选区
4915 * @method collapse
4916 * @return { UE.dom.Range } 当前range对象
4917 * @example
4918 * ```html
4919 * <!-- 选区示例 -->
4920 * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
4921 *
4922 * <script>
4923 *
4924 * //执行操作
4925 * range.collapse();
4926 *
4927 * //结果选区
4928 * //“|”表示选区已闭合
4929 * //<b>xx<i>xxx</i><span>xx|x</span>xxx</b>
4930 *
4931 * </script>
4932 * ```
4933 */
4934
4935 /**
4936 * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合,
4937 * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。
4938 * @method collapse
4939 * @param { Boolean } toStart 是否向选区开始处闭合
4940 * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态
4941 * @see UE.dom.Range:collapse()
4942 * @example
4943 * ```html
4944 * <!-- 选区示例 -->
4945 * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
4946 *
4947 * <script>
4948 *
4949 * //执行操作
4950 * range.collapse( true );
4951 *
4952 * //结果选区
4953 * //“|”表示选区已闭合
4954 * //<b>xx<i>xxx</i><span>|xxx</span>xxx</b>
4955 *
4956 * </script>
4957 * ```
4958 */
4959 collapse:function (toStart) {
4960 var me = this;
4961 if (toStart) {
4962 me.endContainer = me.startContainer;
4963 me.endOffset = me.startOffset;
4964 } else {
4965 me.startContainer = me.endContainer;
4966 me.startOffset = me.endOffset;
4967 }
4968 me.collapsed = true;
4969 return me;
4970 },
4971
4972 /**
4973 * 调整range的开始位置和结束位置,使其"收缩"到最小的位置
4974 * @method shrinkBoundary
4975 * @return { UE.dom.Range } 当前range对象
4976 * @example
4977 * ```html
4978 * <span>xx<b>xx[</b>xxxxx]</span> => <span>xx<b>xx</b>[xxxxx]</span>
4979 * ```
4980 *
4981 * @example
4982 * ```html
4983 * <!-- 选区示例 -->
4984 * <b>x[xx</b><i>]xxx</i>
4985 *
4986 * <script>
4987 *
4988 * //执行收缩
4989 * range.shrinkBoundary();
4990 *
4991 * //结果选区
4992 * //<b>x[xx]</b><i>xxx</i>
4993 * </script>
4994 * ```
4995 *
4996 * @example
4997 * ```html
4998 * [<b><i>xxxx</i>xxxxxxx</b>] => <b><i>[xxxx</i>xxxxxxx]</b>
4999 * ```
5000 */
5001
5002 /**
5003 * 调整range的开始位置和结束位置,使其"收缩"到最小的位置,
5004 * 如果ignoreEnd的值为true,则忽略对结束位置的调整
5005 * @method shrinkBoundary
5006 * @param { Boolean } ignoreEnd 是否忽略对结束位置的调整
5007 * @return { UE.dom.Range } 当前range对象
5008 * @see UE.dom.domUtils.Range:shrinkBoundary()
5009 */
5010 shrinkBoundary:function (ignoreEnd) {
5011 var me = this, child,
5012 collapsed = me.collapsed;
5013 function check(node){
5014 return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
5015 }
5016 while (me.startContainer.nodeType == 1 //是element
5017 && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
5018 && check(child)) {
5019 me.setStart(child, 0);
5020 }
5021 if (collapsed) {
5022 return me.collapse(true);
5023 }
5024 if (!ignoreEnd) {
5025 while (me.endContainer.nodeType == 1//是element
5026 && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
5027 && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
5028 && check(child)) {
5029 me.setEnd(child, child.childNodes.length);
5030 }
5031 }
5032 return me;
5033 },
5034
5035 /**
5036 * 获取离当前选区内包含的所有节点最近的公共祖先节点,
5037 * @method getCommonAncestor
5038 * @remind 返回的公共祖先节点一定不是range自身的容器节点, 但有可能是一个文本节点
5039 * @return { Node } 当前range对象内所有节点的公共祖先节点
5040 * @example
5041 * ```html
5042 * //选区示例
5043 * <span>xxx<b>x[x<em>xx]x</em>xxx</b>xx</span>
5044 * <script>
5045 *
5046 * var node = range.getCommonAncestor();
5047 *
5048 * //公共祖先节点是: b节点
5049 * //输出: B
5050 * console.log(node.tagName);
5051 *
5052 * </script>
5053 * ```
5054 */
5055
5056 /**
5057 * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
5058 * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
5059 * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点
5060 * @method getCommonAncestor
5061 * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
5062 * @return { Node } 当前range对象内所有节点的公共祖先节点
5063 * @see UE.dom.Range:getCommonAncestor()
5064 * @example
5065 * ```html
5066 * <body>
5067 *
5068 * <!-- 选区示例 -->
5069 * <b>xxx<i>xxxx<span>xx[x</span>xx]x</i>xxxxxxx</b>
5070 *
5071 * <script>
5072 *
5073 * var node = range.getCommonAncestor( false );
5074 *
5075 * //这里的公共祖先节点是B而不是I, 是因为参数限制了获取到的节点不能是容器节点
5076 * //output: B
5077 * console.log( node.tagName );
5078 *
5079 * </script>
5080 *
5081 * </body>
5082 * ```
5083 */
5084
5085 /**
5086 * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
5087 * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
5088 * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点; 同时可以根据
5089 * ignoreTextNode 参数的取值决定是否忽略类型为文本节点的祖先节点。
5090 * @method getCommonAncestor
5091 * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
5092 * @param { Boolean } ignoreTextNode 获取祖先节点的过程中是否忽略类型为文本节点的祖先节点
5093 * @return { Node } 当前range对象内所有节点的公共祖先节点
5094 * @see UE.dom.Range:getCommonAncestor()
5095 * @see UE.dom.Range:getCommonAncestor(Boolean)
5096 * @example
5097 * ```html
5098 * <body>
5099 *
5100 * <!-- 选区示例 -->
5101 * <b>xxx<i>xxxx<span>x[x]x</span>xxx</i>xxxxxxx</b>
5102 *
5103 * <script>
5104 *
5105 * var node = range.getCommonAncestor( true, false );
5106 *
5107 * //output: SPAN
5108 * console.log( node.tagName );
5109 *
5110 * </script>
5111 *
5112 * </body>
5113 * ```
5114 */
5115 getCommonAncestor:function (includeSelf, ignoreTextNode) {
5116 var me = this,
5117 start = me.startContainer,
5118 end = me.endContainer;
5119 if (start === end) {
5120 if (includeSelf && selectOneNode(this)) {
5121 start = start.childNodes[me.startOffset];
5122 if(start.nodeType == 1)
5123 return start;
5124 }
5125 //只有在上来就相等的情况下才会出现是文本的情况
5126 return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start;
5127 }
5128 return domUtils.getCommonAncestor(start, end);
5129 },
5130
5131 /**
5132 * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上
5133 * @method trimBoundary
5134 * @remind 该操作有可能会引起文本节点被切开
5135 * @return { UE.dom.Range } 当前range对象
5136 * @example
5137 * ```html
5138 *
5139 * //选区示例
5140 * <b>xxx<i>[xxxxx]</i>xxx</b>
5141 *
5142 * <script>
5143 * //未调整前, 选区的开始容器和结束都是文本节点
5144 * //执行调整
5145 * range.trimBoundary();
5146 *
5147 * //调整之后, 容器节点变成了i节点
5148 * //<b>xxx[<i>xxxxx</i>]xxx</b>
5149 * </script>
5150 * ```
5151 */
5152
5153 /**
5154 * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上,
5155 * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整
5156 * @method trimBoundary
5157 * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整
5158 * @return { UE.dom.Range } 当前range对象
5159 * @example
5160 * ```html
5161 *
5162 * //选区示例
5163 * <b>xxx<i>[xxxxx]</i>xxx</b>
5164 *
5165 * <script>
5166 * //未调整前, 选区的开始容器和结束都是文本节点
5167 * //执行调整
5168 * range.trimBoundary( true );
5169 *
5170 * //调整之后, 开始容器节点变成了i节点
5171 * //但是, 结束容器没有发生变化
5172 * //<b>xxx[<i>xxxxx]</i>xxx</b>
5173 * </script>
5174 * ```
5175 */
5176 trimBoundary:function (ignoreEnd) {
5177 this.txtToElmBoundary();
5178 var start = this.startContainer,
5179 offset = this.startOffset,
5180 collapsed = this.collapsed,
5181 end = this.endContainer;
5182 if (start.nodeType == 3) {
5183 if (offset == 0) {
5184 this.setStartBefore(start);
5185 } else {
5186 if (offset >= start.nodeValue.length) {
5187 this.setStartAfter(start);
5188 } else {
5189 var textNode = domUtils.split(start, offset);
5190 //跟新结束边界
5191 if (start === end) {
5192 this.setEnd(textNode, this.endOffset - offset);
5193 } else if (start.parentNode === end) {
5194 this.endOffset += 1;
5195 }
5196 this.setStartBefore(textNode);
5197 }
5198 }
5199 if (collapsed) {
5200 return this.collapse(true);
5201 }
5202 }
5203 if (!ignoreEnd) {
5204 offset = this.endOffset;
5205 end = this.endContainer;
5206 if (end.nodeType == 3) {
5207 if (offset == 0) {
5208 this.setEndBefore(end);
5209 } else {
5210 offset < end.nodeValue.length && domUtils.split(end, offset);
5211 this.setEndAfter(end);
5212 }
5213 }
5214 }
5215 return this;
5216 },
5217
5218 /**
5219 * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则什么也不做
5220 * @method txtToElmBoundary
5221 * @remind 该操作不会修改dom节点
5222 * @return { UE.dom.Range } 当前range对象
5223 */
5224
5225 /**
5226 * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则根据参数项
5227 * ignoreCollapsed 的值决定是否执行该调整
5228 * @method txtToElmBoundary
5229 * @param { Boolean } ignoreCollapsed 是否忽略选区的闭合状态, 如果该参数取值为true, 则
5230 * 不论选区是否闭合, 都会执行该操作, 反之, 则不会对闭合的选区执行该操作
5231 * @return { UE.dom.Range } 当前range对象
5232 */
5233 txtToElmBoundary:function (ignoreCollapsed) {
5234 function adjust(r, c) {
5235 var container = r[c + 'Container'],
5236 offset = r[c + 'Offset'];
5237 if (container.nodeType == 3) {
5238 if (!offset) {
5239 r['set' + c.replace(/(\w)/, function (a) {
5240 return a.toUpperCase();
5241 }) + 'Before'](container);
5242 } else if (offset >= container.nodeValue.length) {
5243 r['set' + c.replace(/(\w)/, function (a) {
5244 return a.toUpperCase();
5245 }) + 'After' ](container);
5246 }
5247 }
5248 }
5249
5250 if (ignoreCollapsed || !this.collapsed) {
5251 adjust(this, 'start');
5252 adjust(this, 'end');
5253 }
5254 return this;
5255 },
5256
5257 /**
5258 * 在当前选区的开始位置前插入节点,新插入的节点会被该range包含
5259 * @method insertNode
5260 * @param { Node } node 需要插入的节点
5261 * @remind 插入的节点可以是一个DocumentFragment依次插入多个节点
5262 * @return { UE.dom.Range } 当前range对象
5263 */
5264 insertNode:function (node) {
5265 var first = node, length = 1;
5266 if (node.nodeType == 11) {
5267 first = node.firstChild;
5268 length = node.childNodes.length;
5269 }
5270 this.trimBoundary(true);
5271 var start = this.startContainer,
5272 offset = this.startOffset;
5273 var nextNode = start.childNodes[ offset ];
5274 if (nextNode) {
5275 start.insertBefore(node, nextNode);
5276 } else {
5277 start.appendChild(node);
5278 }
5279 if (first.parentNode === this.endContainer) {
5280 this.endOffset = this.endOffset + length;
5281 }
5282 return this.setStartBefore(first);
5283 },
5284
5285 /**
5286 * 闭合选区到当前选区的开始位置, 并且定位光标到闭合后的位置
5287 * @method setCursor
5288 * @return { UE.dom.Range } 当前range对象
5289 * @see UE.dom.Range:collapse()
5290 */
5291
5292 /**
5293 * 闭合选区,可以根据参数toEnd的值控制选区是向前闭合还是向后闭合, 并且定位光标到闭合后的位置。
5294 * @method setCursor
5295 * @param { Boolean } toEnd 是否向后闭合, 如果为true, 则闭合选区时, 将向结束容器方向闭合,
5296 * 反之,则向开始容器方向闭合
5297 * @return { UE.dom.Range } 当前range对象
5298 * @see UE.dom.Range:collapse(Boolean)
5299 */
5300 setCursor:function (toEnd, noFillData) {
5301 return this.collapse(!toEnd).select(noFillData);
5302 },
5303
5304 /**
5305 * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
5306 * @method createBookmark
5307 * @param { Boolean } serialize 控制返回的标记位置是对当前位置的引用还是ID,如果该值为true,则
5308 * 返回标记位置的ID, 反之则返回标记位置节点的引用
5309 * @return { Object } 返回一个书签记录键值对, 其包含的key有: start => 开始标记的ID或者引用,
5310 * end => 结束标记的ID或引用, id => 当前标记的类型, 如果为true,则表示
5311 * 返回的记录的类型为ID, 反之则为引用
5312 */
5313 createBookmark:function (serialize, same) {
5314 var endNode,
5315 startNode = this.document.createElement('span');
5316 startNode.style.cssText = 'display:none;line-height:0px;';
5317 startNode.appendChild(this.document.createTextNode('\u200D'));
5318 startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
5319
5320 if (!this.collapsed) {
5321 endNode = startNode.cloneNode(true);
5322 endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
5323 }
5324 this.insertNode(startNode);
5325 if (endNode) {
5326 this.collapse().insertNode(endNode).setEndBefore(endNode);
5327 }
5328 this.setStartAfter(startNode);
5329 return {
5330 start:serialize ? startNode.id : startNode,
5331 end:endNode ? serialize ? endNode.id : endNode : null,
5332 id:serialize
5333 }
5334 },
5335
5336 /**
5337 * 调整当前range的边界到书签位置,并删除该书签对象所标记的位置内的节点
5338 * @method moveToBookmark
5339 * @param { BookMark } bookmark createBookmark所创建的标签对象
5340 * @return { UE.dom.Range } 当前range对象
5341 * @see UE.dom.Range:createBookmark(Boolean)
5342 */
5343 moveToBookmark:function (bookmark) {
5344 var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
5345 end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
5346 this.setStartBefore(start);
5347 domUtils.remove(start);
5348 if (end) {
5349 this.setEndBefore(end);
5350 domUtils.remove(end);
5351 } else {
5352 this.collapse(true);
5353 }
5354 return this;
5355 },
5356
5357 /**
5358 * 调整range的边界,使其"放大"到最近的父节点
5359 * @method enlarge
5360 * @remind 会引起选区的变化
5361 * @return { UE.dom.Range } 当前range对象
5362 */
5363
5364 /**
5365 * 调整range的边界,使其"放大"到最近的父节点,根据参数 toBlock 的取值, 可以
5366 * 要求扩大之后的父节点是block节点
5367 * @method enlarge
5368 * @param { Boolean } toBlock 是否要求扩大之后的父节点必须是block节点
5369 * @return { UE.dom.Range } 当前range对象
5370 */
5371 enlarge:function (toBlock, stopFn) {
5372 var isBody = domUtils.isBody,
5373 pre, node, tmp = this.document.createTextNode('');
5374 if (toBlock) {
5375 node = this.startContainer;
5376 if (node.nodeType == 1) {
5377 if (node.childNodes[this.startOffset]) {
5378 pre = node = node.childNodes[this.startOffset]
5379 } else {
5380 node.appendChild(tmp);
5381 pre = node = tmp;
5382 }
5383 } else {
5384 pre = node;
5385 }
5386 while (1) {
5387 if (domUtils.isBlockElm(node)) {
5388 node = pre;
5389 while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) {
5390 node = pre;
5391 }
5392 this.setStartBefore(node);
5393 break;
5394 }
5395 pre = node;
5396 node = node.parentNode;
5397 }
5398 node = this.endContainer;
5399 if (node.nodeType == 1) {
5400 if (pre = node.childNodes[this.endOffset]) {
5401 node.insertBefore(tmp, pre);
5402 } else {
5403 node.appendChild(tmp);
5404 }
5405 pre = node = tmp;
5406 } else {
5407 pre = node;
5408 }
5409 while (1) {
5410 if (domUtils.isBlockElm(node)) {
5411 node = pre;
5412 while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) {
5413 node = pre;
5414 }
5415 this.setEndAfter(node);
5416 break;
5417 }
5418 pre = node;
5419 node = node.parentNode;
5420 }
5421 if (tmp.parentNode === this.endContainer) {
5422 this.endOffset--;
5423 }
5424 domUtils.remove(tmp);
5425 }
5426
5427 // 扩展边界到最大
5428 if (!this.collapsed) {
5429 while (this.startOffset == 0) {
5430 if (stopFn && stopFn(this.startContainer)) {
5431 break;
5432 }
5433 if (isBody(this.startContainer)) {
5434 break;
5435 }
5436 this.setStartBefore(this.startContainer);
5437 }
5438 while (this.endOffset == (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length)) {
5439 if (stopFn && stopFn(this.endContainer)) {
5440 break;
5441 }
5442 if (isBody(this.endContainer)) {
5443 break;
5444 }
5445 this.setEndAfter(this.endContainer);
5446 }
5447 }
5448 return this;
5449 },
5450 enlargeToBlockElm:function(ignoreEnd){
5451 while(!domUtils.isBlockElm(this.startContainer)){
5452 this.setStartBefore(this.startContainer);
5453 }
5454 if(!ignoreEnd){
5455 while(!domUtils.isBlockElm(this.endContainer)){
5456 this.setEndAfter(this.endContainer);
5457 }
5458 }
5459 return this;
5460 },
5461 /**
5462 * 调整Range的边界,使其"缩小"到最合适的位置
5463 * @method adjustmentBoundary
5464 * @return { UE.dom.Range } 当前range对象
5465 * @see UE.dom.Range:shrinkBoundary()
5466 */
5467 adjustmentBoundary:function () {
5468 if (!this.collapsed) {
5469 while (!domUtils.isBody(this.startContainer) &&
5470 this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
5471 this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
5472 ) {
5473
5474 this.setStartAfter(this.startContainer);
5475 }
5476 while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
5477 this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
5478 ) {
5479 this.setEndBefore(this.endContainer);
5480 }
5481 }
5482 return this;
5483 },
5484
5485 /**
5486 * 给range选区中的内容添加给定的inline标签
5487 * @method applyInlineStyle
5488 * @param { String } tagName 需要添加的标签名
5489 * @example
5490 * ```html
5491 * <p>xxxx[xxxx]x</p> ==> range.applyInlineStyle("strong") ==> <p>xxxx[<strong>xxxx</strong>]x</p>
5492 * ```
5493 */
5494
5495 /**
5496 * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。
5497 * @method applyInlineStyle
5498 * @param { String } tagName 需要添加的标签名
5499 * @param { Object } attrs 跟随新添加的标签的属性
5500 * @return { UE.dom.Range } 当前选区
5501 * @example
5502 * ```html
5503 * <p>xxxx[xxxx]x</p>
5504 *
5505 * ==>
5506 *
5507 * <!-- 执行操作 -->
5508 * range.applyInlineStyle("strong",{"style":"font-size:12px"})
5509 *
5510 * ==>
5511 *
5512 * <p>xxxx[<strong style="font-size:12px">xxxx</strong>]x</p>
5513 * ```
5514 */
5515 applyInlineStyle:function (tagName, attrs, list) {
5516 if (this.collapsed)return this;
5517 this.trimBoundary().enlarge(false,
5518 function (node) {
5519 return node.nodeType == 1 && domUtils.isBlockElm(node)
5520 }).adjustmentBoundary();
5521 var bookmark = this.createBookmark(),
5522 end = bookmark.end,
5523 filterFn = function (node) {
5524 return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
5525 },
5526 current = domUtils.getNextDomNode(bookmark.start, false, filterFn),
5527 node,
5528 pre,
5529 range = this.cloneRange();
5530 while (current && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
5531 if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
5532 range.setStartBefore(current);
5533 node = current;
5534 while (node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end) {
5535 pre = node;
5536 node = domUtils.getNextDomNode(node, node.nodeType == 1, null, function (parent) {
5537 return dtd[tagName][parent.tagName];
5538 });
5539 }
5540 var frag = range.setEndAfter(pre).extractContents(), elm;
5541 if (list && list.length > 0) {
5542 var level, top;
5543 top = level = list[0].cloneNode(false);
5544 for (var i = 1, ci; ci = list[i++];) {
5545 level.appendChild(ci.cloneNode(false));
5546 level = level.firstChild;
5547 }
5548 elm = level;
5549 } else {
5550 elm = range.document.createElement(tagName);
5551 }
5552 if (attrs) {
5553 domUtils.setAttributes(elm, attrs);
5554 }
5555 elm.appendChild(frag);
5556 range.insertNode(list ? top : elm);
5557 //处理下滑线在a上的情况
5558 var aNode;
5559 if (tagName == 'span' && attrs.style && /text\-decoration/.test(attrs.style) && (aNode = domUtils.findParentByTagName(elm, 'a', true))) {
5560 domUtils.setAttributes(aNode, attrs);
5561 domUtils.remove(elm, true);
5562 elm = aNode;
5563 } else {
5564 domUtils.mergeSibling(elm);
5565 domUtils.clearEmptySibling(elm);
5566 }
5567 //去除子节点相同的
5568 domUtils.mergeChild(elm, attrs);
5569 current = domUtils.getNextDomNode(elm, false, filterFn);
5570 domUtils.mergeToParent(elm);
5571 if (node === end) {
5572 break;
5573 }
5574 } else {
5575 current = domUtils.getNextDomNode(current, true, filterFn);
5576 }
5577 }
5578 return this.moveToBookmark(bookmark);
5579 },
5580
5581 /**
5582 * 移除当前选区内指定的inline标签,但保留其中的内容
5583 * @method removeInlineStyle
5584 * @param { String } tagName 需要移除的标签名
5585 * @return { UE.dom.Range } 当前的range对象
5586 * @example
5587 * ```html
5588 * xx[x<span>xxx<em>yyy</em>zz]z</span> => range.removeInlineStyle(["em"]) => xx[x<span>xxxyyyzz]z</span>
5589 * ```
5590 */
5591
5592 /**
5593 * 移除当前选区内指定的一组inline标签,但保留其中的内容
5594 * @method removeInlineStyle
5595 * @param { Array } tagNameArr 需要移除的标签名的数组
5596 * @return { UE.dom.Range } 当前的range对象
5597 * @see UE.dom.Range:removeInlineStyle(String)
5598 */
5599 removeInlineStyle:function (tagNames) {
5600 if (this.collapsed)return this;
5601 tagNames = utils.isArray(tagNames) ? tagNames : [tagNames];
5602 this.shrinkBoundary().adjustmentBoundary();
5603 var start = this.startContainer, end = this.endContainer;
5604 while (1) {
5605 if (start.nodeType == 1) {
5606 if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) {
5607 break;
5608 }
5609 if (start.tagName.toLowerCase() == 'body') {
5610 start = null;
5611 break;
5612 }
5613 }
5614 start = start.parentNode;
5615 }
5616 while (1) {
5617 if (end.nodeType == 1) {
5618 if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) {
5619 break;
5620 }
5621 if (end.tagName.toLowerCase() == 'body') {
5622 end = null;
5623 break;
5624 }
5625 }
5626 end = end.parentNode;
5627 }
5628 var bookmark = this.createBookmark(),
5629 frag,
5630 tmpRange;
5631 if (start) {
5632 tmpRange = this.cloneRange().setEndBefore(bookmark.start).setStartBefore(start);
5633 frag = tmpRange.extractContents();
5634 tmpRange.insertNode(frag);
5635 domUtils.clearEmptySibling(start, true);
5636 start.parentNode.insertBefore(bookmark.start, start);
5637 }
5638 if (end) {
5639 tmpRange = this.cloneRange().setStartAfter(bookmark.end).setEndAfter(end);
5640 frag = tmpRange.extractContents();
5641 tmpRange.insertNode(frag);
5642 domUtils.clearEmptySibling(end, false, true);
5643 end.parentNode.insertBefore(bookmark.end, end.nextSibling);
5644 }
5645 var current = domUtils.getNextDomNode(bookmark.start, false, function (node) {
5646 return node.nodeType == 1;
5647 }), next;
5648 while (current && current !== bookmark.end) {
5649 next = domUtils.getNextDomNode(current, true, function (node) {
5650 return node.nodeType == 1;
5651 });
5652 if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) {
5653 domUtils.remove(current, true);
5654 }
5655 current = next;
5656 }
5657 return this.moveToBookmark(bookmark);
5658 },
5659
5660 /**
5661 * 获取当前选中的自闭合的节点
5662 * @method getClosedNode
5663 * @return { Node | NULL } 如果当前选中的是自闭合节点, 则返回该节点, 否则返回NULL
5664 */
5665 getClosedNode:function () {
5666 var node;
5667 if (!this.collapsed) {
5668 var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
5669 if (selectOneNode(range)) {
5670 var child = range.startContainer.childNodes[range.startOffset];
5671 if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
5672 node = child;
5673 }
5674 }
5675 }
5676 return node;
5677 },
5678
5679 /**
5680 * 在页面上高亮range所表示的选区
5681 * @method select
5682 * @return { UE.dom.Range } 返回当前Range对象
5683 */
5684 //这里不区分ie9以上,trace:3824
5685 select:browser.ie ? function (noFillData, textRange) {
5686 var nativeRange;
5687 if (!this.collapsed)
5688 this.shrinkBoundary();
5689 var node = this.getClosedNode();
5690 if (node && !textRange) {
5691 try {
5692 nativeRange = this.document.body.createControlRange();
5693 nativeRange.addElement(node);
5694 nativeRange.select();
5695 } catch (e) {}
5696 return this;
5697 }
5698 var bookmark = this.createBookmark(),
5699 start = bookmark.start,
5700 end;
5701 nativeRange = this.document.body.createTextRange();
5702 nativeRange.moveToElementText(start);
5703 nativeRange.moveStart('character', 1);
5704 if (!this.collapsed) {
5705 var nativeRangeEnd = this.document.body.createTextRange();
5706 end = bookmark.end;
5707 nativeRangeEnd.moveToElementText(end);
5708 nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
5709 } else {
5710 if (!noFillData && this.startContainer.nodeType != 3) {
5711 //使用<span>|x<span>固定住光标
5712 var tmpText = this.document.createTextNode(fillChar),
5713 tmp = this.document.createElement('span');
5714 tmp.appendChild(this.document.createTextNode(fillChar));
5715 start.parentNode.insertBefore(tmp, start);
5716 start.parentNode.insertBefore(tmpText, start);
5717 //当点b,i,u时,不能清除i上边的b
5718 removeFillData(this.document, tmpText);
5719 fillData = tmpText;
5720 mergeSibling(tmp, 'previousSibling');
5721 mergeSibling(start, 'nextSibling');
5722 nativeRange.moveStart('character', -1);
5723 nativeRange.collapse(true);
5724 }
5725 }
5726 this.moveToBookmark(bookmark);
5727 tmp && domUtils.remove(tmp);
5728 //IE在隐藏状态下不支持range操作,catch一下
5729 try {
5730 nativeRange.select();
5731 } catch (e) {
5732 }
5733 return this;
5734 } : function (notInsertFillData) {
5735 function checkOffset(rng){
5736
5737 function check(node,offset,dir){
5738 if(node.nodeType == 3 && node.nodeValue.length < offset){
5739 rng[dir + 'Offset'] = node.nodeValue.length
5740 }
5741 }
5742 check(rng.startContainer,rng.startOffset,'start');
5743 check(rng.endContainer,rng.endOffset,'end');
5744 }
5745 var win = domUtils.getWindow(this.document),
5746 sel = win.getSelection(),
5747 txtNode;
5748 //FF下关闭自动长高时滚动条在关闭dialog时会跳
5749 //ff下如果不body.focus将不能定位闭合光标到编辑器内
5750 browser.gecko ? this.document.body.focus() : win.focus();
5751 if (sel) {
5752 sel.removeAllRanges();
5753 // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
5754 // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
5755 if (this.collapsed && !notInsertFillData) {
5756// //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
5757// if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
5758// var tmp = this.document.createTextNode('');
5759// this.insertNode(tmp).setStart(tmp, 0).collapse(true);
5760// }
5761//
5762 //处理光标落在文本节点的情况
5763 //处理以下的情况
5764 //<b>|xxxx</b>
5765 //<b>xxxx</b>|xxxx
5766 //xxxx<b>|</b>
5767 var start = this.startContainer,child = start;
5768 if(start.nodeType == 1){
5769 child = start.childNodes[this.startOffset];
5770
5771 }
5772 if( !(start.nodeType == 3 && this.startOffset) &&
5773 (child ?
5774 (!child.previousSibling || child.previousSibling.nodeType != 3)
5775 :
5776 (!start.lastChild || start.lastChild.nodeType != 3)
5777 )
5778 ){
5779 txtNode = this.document.createTextNode(fillChar);
5780 //跟着前边走
5781 this.insertNode(txtNode);
5782 removeFillData(this.document, txtNode);
5783 mergeSibling(txtNode, 'previousSibling');
5784 mergeSibling(txtNode, 'nextSibling');
5785 fillData = txtNode;
5786 this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
5787 }
5788 }
5789 var nativeRange = this.document.createRange();
5790 if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){
5791 var child = this.startContainer.childNodes[this.startOffset];
5792 if(!child){
5793 //往前靠拢
5794 child = this.startContainer.lastChild;
5795 if( child && domUtils.isBr(child)){
5796 this.setStartBefore(child).collapse(true);
5797 }
5798 }else{
5799 //向后靠拢
5800 while(child && domUtils.isBlockElm(child)){
5801 if(child.nodeType == 1 && child.childNodes[0]){
5802 child = child.childNodes[0]
5803 }else{
5804 break;
5805 }
5806 }
5807 child && this.setStartBefore(child).collapse(true)
5808 }
5809
5810 }
5811 //是createAddress最后一位算的不准,现在这里进行微调
5812 checkOffset(this);
5813 nativeRange.setStart(this.startContainer, this.startOffset);
5814 nativeRange.setEnd(this.endContainer, this.endOffset);
5815 sel.addRange(nativeRange);
5816 }
5817 return this;
5818 },
5819
5820 /**
5821 * 滚动到当前range开始的位置
5822 * @method scrollToView
5823 * @param { Window } win 当前range对象所属的window对象
5824 * @return { UE.dom.Range } 当前Range对象
5825 */
5826
5827 /**
5828 * 滚动到距离当前range开始位置 offset 的位置处
5829 * @method scrollToView
5830 * @param { Window } win 当前range对象所属的window对象
5831 * @param { Number } offset 距离range开始位置处的偏移量, 如果为正数, 则向下偏移, 反之, 则向上偏移
5832 * @return { UE.dom.Range } 当前Range对象
5833 */
5834 scrollToView:function (win, offset) {
5835 win = win ? window : domUtils.getWindow(this.document);
5836 var me = this,
5837 span = me.document.createElement('span');
5838 //trace:717
5839 span.innerHTML = '&nbsp;';
5840 me.cloneRange().insertNode(span);
5841 domUtils.scrollToView(span, win, offset);
5842 domUtils.remove(span);
5843 return me;
5844 },
5845
5846 /**
5847 * 判断当前选区内容是否占位符
5848 * @private
5849 * @method inFillChar
5850 * @return { Boolean } 如果是占位符返回true,否则返回false
5851 */
5852 inFillChar : function(){
5853 var start = this.startContainer;
5854 if(this.collapsed && start.nodeType == 3
5855 && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
5856 ){
5857 return true;
5858 }
5859 return false;
5860 },
5861
5862 /**
5863 * 保存
5864 * @method createAddress
5865 * @private
5866 * @return { Boolean } 返回开始和结束的位置
5867 * @example
5868 * ```html
5869 * <body>
5870 * <p>
5871 * aaaa
5872 * <em>
5873 * <!-- 选区开始 -->
5874 * bbbb
5875 * <!-- 选区结束 -->
5876 * </em>
5877 * </p>
5878 *
5879 * <script>
5880 * //output: {startAddress:[0,1,0,0],endAddress:[0,1,0,4]}
5881 * console.log( range.createAddress() );
5882 * </script>
5883 * </body>
5884 * ```
5885 */
5886 createAddress : function(ignoreEnd,ignoreTxt){
5887 var addr = {},me = this;
5888
5889 function getAddress(isStart){
5890 var node = isStart ? me.startContainer : me.endContainer;
5891 var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}),
5892 addrs = [];
5893 for(var i = 0,ci;ci = parents[i++];){
5894 addrs.push(domUtils.getNodeIndex(ci,ignoreTxt));
5895 }
5896 var firstIndex = 0;
5897
5898 if(ignoreTxt){
5899 if(node.nodeType == 3){
5900 var tmpNode = node.previousSibling;
5901 while(tmpNode && tmpNode.nodeType == 3){
5902 firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
5903 tmpNode = tmpNode.previousSibling;
5904 }
5905 firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
5906 }else{
5907 node = node.childNodes[ isStart ? me.startOffset : me.endOffset];
5908 if(node){
5909 firstIndex = domUtils.getNodeIndex(node,ignoreTxt);
5910 }else{
5911 node = isStart ? me.startContainer : me.endContainer;
5912 var first = node.firstChild;
5913 while(first){
5914 if(domUtils.isFillChar(first)){
5915 first = first.nextSibling;
5916 continue;
5917 }
5918 firstIndex++;
5919 if(first.nodeType == 3){
5920 while( first && first.nodeType == 3){
5921 first = first.nextSibling;
5922 }
5923 }else{
5924 first = first.nextSibling;
5925 }
5926 }
5927 }
5928 }
5929
5930 }else{
5931 firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
5932 }
5933 if(firstIndex < 0){
5934 firstIndex = 0;
5935 }
5936 addrs.push(firstIndex);
5937 return addrs;
5938 }
5939 addr.startAddress = getAddress(true);
5940 if(!ignoreEnd){
5941 addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
5942 }
5943 return addr;
5944 },
5945
5946 /**
5947 * 保存
5948 * @method createAddress
5949 * @private
5950 * @return { Boolean } 返回开始和结束的位置
5951 * @example
5952 * ```html
5953 * <body>
5954 * <p>
5955 * aaaa
5956 * <em>
5957 * <!-- 选区开始 -->
5958 * bbbb
5959 * <!-- 选区结束 -->
5960 * </em>
5961 * </p>
5962 *
5963 * <script>
5964 * var range = editor.selection.getRange();
5965 * range.moveToAddress({startAddress:[0,1,0,0],endAddress:[0,1,0,4]});
5966 * range.select();
5967 * //output: 'bbbb'
5968 * console.log(editor.selection.getText());
5969 * </script>
5970 * </body>
5971 * ```
5972 */
5973 moveToAddress : function(addr,ignoreEnd){
5974 var me = this;
5975 function getNode(address,isStart){
5976 var tmpNode = me.document.body,
5977 parentNode,offset;
5978 for(var i= 0,ci,l=address.length;i<l;i++){
5979 ci = address[i];
5980 parentNode = tmpNode;
5981 tmpNode = tmpNode.childNodes[ci];
5982 if(!tmpNode){
5983 offset = ci;
5984 break;
5985 }
5986 }
5987 if(isStart){
5988 if(tmpNode){
5989 me.setStartBefore(tmpNode)
5990 }else{
5991 me.setStart(parentNode,offset)
5992 }
5993 }else{
5994 if(tmpNode){
5995 me.setEndBefore(tmpNode)
5996 }else{
5997 me.setEnd(parentNode,offset)
5998 }
5999 }
6000 }
6001 getNode(addr.startAddress,true);
6002 !ignoreEnd && addr.endAddress && getNode(addr.endAddress);
6003 return me;
6004 },
6005
6006 /**
6007 * 判断给定的Range对象是否和当前Range对象表示的是同一个选区
6008 * @method equals
6009 * @param { UE.dom.Range } 需要判断的Range对象
6010 * @return { Boolean } 如果给定的Range对象与当前Range对象表示的是同一个选区, 则返回true, 否则返回false
6011 */
6012 equals : function(rng){
6013 for(var p in this){
6014 if(this.hasOwnProperty(p)){
6015 if(this[p] !== rng[p])
6016 return false
6017 }
6018 }
6019 return true;
6020
6021 },
6022
6023 /**
6024 * 遍历range内的节点。每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
6025 * 作为其参数。
6026 * @method traversal
6027 * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
6028 * @return { UE.dom.Range } 当前range对象
6029 * @example
6030 * ```html
6031 *
6032 * <body>
6033 *
6034 * <!-- 选区开始 -->
6035 * <span></span>
6036 * <a></a>
6037 * <!-- 选区结束 -->
6038 * </body>
6039 *
6040 * <script>
6041 *
6042 * //output: <span></span><a></a>
6043 * console.log( range.cloneContents() );
6044 *
6045 * range.traversal( function ( node ) {
6046 *
6047 * if ( node.nodeType === 1 ) {
6048 * node.className = "test";
6049 * }
6050 *
6051 * } );
6052 *
6053 * //output: <span class="test"></span><a class="test"></a>
6054 * console.log( range.cloneContents() );
6055 *
6056 * </script>
6057 * ```
6058 */
6059
6060 /**
6061 * 遍历range内的节点。
6062 * 每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
6063 * 作为其参数。
6064 * 可以通过参数项 filterFn 来指定一个过滤器, 只有符合该过滤器过滤规则的节点才会触
6065 * 发doFn函数的执行
6066 * @method traversal
6067 * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
6068 * @param { Function } filterFn 过滤器, 该函数接受当前遍历的节点作为参数, 如果该节点满足过滤
6069 * 规则, 请返回true, 该节点会触发doFn, 否则, 请返回false, 则该节点不
6070 * 会触发doFn。
6071 * @return { UE.dom.Range } 当前range对象
6072 * @see UE.dom.Range:traversal(Function)
6073 * @example
6074 * ```html
6075 *
6076 * <body>
6077 *
6078 * <!-- 选区开始 -->
6079 * <span></span>
6080 * <a></a>
6081 * <!-- 选区结束 -->
6082 * </body>
6083 *
6084 * <script>
6085 *
6086 * //output: <span></span><a></a>
6087 * console.log( range.cloneContents() );
6088 *
6089 * range.traversal( function ( node ) {
6090 *
6091 * node.className = "test";
6092 *
6093 * }, function ( node ) {
6094 * return node.nodeType === 1;
6095 * } );
6096 *
6097 * //output: <span class="test"></span><a class="test"></a>
6098 * console.log( range.cloneContents() );
6099 *
6100 * </script>
6101 * ```
6102 */
6103 traversal:function(doFn,filterFn){
6104 if (this.collapsed)
6105 return this;
6106 var bookmark = this.createBookmark(),
6107 end = bookmark.end,
6108 current = domUtils.getNextDomNode(bookmark.start, false, filterFn);
6109 while (current && current !== end && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
6110 var tmpNode = domUtils.getNextDomNode(current,false,filterFn);
6111 doFn(current);
6112 current = tmpNode;
6113 }
6114 return this.moveToBookmark(bookmark);
6115 }
6116 };
6117})();
6118
6119// core/Selection.js
6120/**
6121 * 选集
6122 * @file
6123 * @module UE.dom
6124 * @class Selection
6125 * @since 1.2.6.1
6126 */
6127
6128/**
6129 * 选区集合
6130 * @unfile
6131 * @module UE.dom
6132 * @class Selection
6133 */
6134(function () {
6135
6136 function getBoundaryInformation( range, start ) {
6137 var getIndex = domUtils.getNodeIndex;
6138 range = range.duplicate();
6139 range.collapse( start );
6140 var parent = range.parentElement();
6141 //如果节点里没有子节点,直接退出
6142 if ( !parent.hasChildNodes() ) {
6143 return {container:parent, offset:0};
6144 }
6145 var siblings = parent.children,
6146 child,
6147 testRange = range.duplicate(),
6148 startIndex = 0, endIndex = siblings.length - 1, index = -1,
6149 distance;
6150 while ( startIndex <= endIndex ) {
6151 index = Math.floor( (startIndex + endIndex) / 2 );
6152 child = siblings[index];
6153 testRange.moveToElementText( child );
6154 var position = testRange.compareEndPoints( 'StartToStart', range );
6155 if ( position > 0 ) {
6156 endIndex = index - 1;
6157 } else if ( position < 0 ) {
6158 startIndex = index + 1;
6159 } else {
6160 //trace:1043
6161 return {container:parent, offset:getIndex( child )};
6162 }
6163 }
6164 if ( index == -1 ) {
6165 testRange.moveToElementText( parent );
6166 testRange.setEndPoint( 'StartToStart', range );
6167 distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
6168 siblings = parent.childNodes;
6169 if ( !distance ) {
6170 child = siblings[siblings.length - 1];
6171 return {container:child, offset:child.nodeValue.length};
6172 }
6173
6174 var i = siblings.length;
6175 while ( distance > 0 ){
6176 distance -= siblings[ --i ].nodeValue.length;
6177 }
6178 return {container:siblings[i], offset:-distance};
6179 }
6180 testRange.collapse( position > 0 );
6181 testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
6182 distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
6183 if ( !distance ) {
6184 return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
6185 {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} :
6186 {container:child, offset:position > 0 ? 0 : child.childNodes.length}
6187 }
6188 while ( distance > 0 ) {
6189 try {
6190 var pre = child;
6191 child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
6192 distance -= child.nodeValue.length;
6193 } catch ( e ) {
6194 return {container:parent, offset:getIndex( pre )};
6195 }
6196 }
6197 return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance}
6198 }
6199
6200 /**
6201 * 将ieRange转换为Range对象
6202 * @param {Range} ieRange ieRange对象
6203 * @param {Range} range Range对象
6204 * @return {Range} range 返回转换后的Range对象
6205 */
6206 function transformIERangeToRange( ieRange, range ) {
6207 if ( ieRange.item ) {
6208 range.selectNode( ieRange.item( 0 ) );
6209 } else {
6210 var bi = getBoundaryInformation( ieRange, true );
6211 range.setStart( bi.container, bi.offset );
6212 if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) {
6213 bi = getBoundaryInformation( ieRange, false );
6214 range.setEnd( bi.container, bi.offset );
6215 }
6216 }
6217 return range;
6218 }
6219
6220 /**
6221 * 获得ieRange
6222 * @param {Selection} sel Selection对象
6223 * @return {ieRange} 得到ieRange
6224 */
6225 function _getIERange( sel ) {
6226 var ieRange;
6227 //ie下有可能报错
6228 try {
6229 ieRange = sel.getNative().createRange();
6230 } catch ( e ) {
6231 return null;
6232 }
6233 var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement();
6234 if ( ( el.ownerDocument || el ) === sel.document ) {
6235 return ieRange;
6236 }
6237 return null;
6238 }
6239
6240 var Selection = dom.Selection = function ( doc ) {
6241 var me = this, iframe;
6242 me.document = doc;
6243 if ( browser.ie9below ) {
6244 iframe = domUtils.getWindow( doc ).frameElement;
6245 domUtils.on( iframe, 'beforedeactivate', function () {
6246 me._bakIERange = me.getIERange();
6247 } );
6248 domUtils.on( iframe, 'activate', function () {
6249 try {
6250 if ( !_getIERange( me ) && me._bakIERange ) {
6251 me._bakIERange.select();
6252 }
6253 } catch ( ex ) {
6254 }
6255 me._bakIERange = null;
6256 } );
6257 }
6258 iframe = doc = null;
6259 };
6260
6261 Selection.prototype = {
6262
6263 rangeInBody : function(rng,txtRange){
6264 var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
6265
6266 return node === this.document.body || domUtils.inDoc(node,this.document);
6267 },
6268
6269 /**
6270 * 获取原生seleciton对象
6271 * @method getNative
6272 * @return { Object } 获得selection对象
6273 * @example
6274 * ```javascript
6275 * editor.selection.getNative();
6276 * ```
6277 */
6278 getNative:function () {
6279 var doc = this.document;
6280 try {
6281 return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow( doc ).getSelection();
6282 } catch ( e ) {
6283 return null;
6284 }
6285 },
6286
6287 /**
6288 * 获得ieRange
6289 * @method getIERange
6290 * @return { Object } 返回ie原生的Range
6291 * @example
6292 * ```javascript
6293 * editor.selection.getIERange();
6294 * ```
6295 */
6296 getIERange:function () {
6297 var ieRange = _getIERange( this );
6298 if ( !ieRange ) {
6299 if ( this._bakIERange ) {
6300 return this._bakIERange;
6301 }
6302 }
6303 return ieRange;
6304 },
6305
6306 /**
6307 * 缓存当前选区的range和选区的开始节点
6308 * @method cache
6309 */
6310 cache:function () {
6311 this.clear();
6312 this._cachedRange = this.getRange();
6313 this._cachedStartElement = this.getStart();
6314 this._cachedStartElementPath = this.getStartElementPath();
6315 },
6316
6317 /**
6318 * 获取选区开始位置的父节点到body
6319 * @method getStartElementPath
6320 * @return { Array } 返回父节点集合
6321 * @example
6322 * ```javascript
6323 * editor.selection.getStartElementPath();
6324 * ```
6325 */
6326 getStartElementPath:function () {
6327 if ( this._cachedStartElementPath ) {
6328 return this._cachedStartElementPath;
6329 }
6330 var start = this.getStart();
6331 if ( start ) {
6332 return domUtils.findParents( start, true, null, true )
6333 }
6334 return [];
6335 },
6336
6337 /**
6338 * 清空缓存
6339 * @method clear
6340 */
6341 clear:function () {
6342 this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
6343 },
6344
6345 /**
6346 * 编辑器是否得到了选区
6347 * @method isFocus
6348 */
6349 isFocus:function () {
6350 try {
6351 if(browser.ie9below){
6352
6353 var nativeRange = _getIERange(this);
6354 return !!(nativeRange && this.rangeInBody(nativeRange));
6355 }else{
6356 return !!this.getNative().rangeCount;
6357 }
6358 } catch ( e ) {
6359 return false;
6360 }
6361
6362 },
6363
6364 /**
6365 * 获取选区对应的Range
6366 * @method getRange
6367 * @return { Object } 得到Range对象
6368 * @example
6369 * ```javascript
6370 * editor.selection.getRange();
6371 * ```
6372 */
6373 getRange:function () {
6374 var me = this;
6375 function optimze( range ) {
6376 var child = me.document.body.firstChild,
6377 collapsed = range.collapsed;
6378 while ( child && child.firstChild ) {
6379 range.setStart( child, 0 );
6380 child = child.firstChild;
6381 }
6382 if ( !range.startContainer ) {
6383 range.setStart( me.document.body, 0 )
6384 }
6385 if ( collapsed ) {
6386 range.collapse( true );
6387 }
6388 }
6389
6390 if ( me._cachedRange != null ) {
6391 return this._cachedRange;
6392 }
6393 var range = new baidu.editor.dom.Range( me.document );
6394
6395 if ( browser.ie9below ) {
6396 var nativeRange = me.getIERange();
6397 if ( nativeRange ) {
6398 //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置
6399 try{
6400 transformIERangeToRange( nativeRange, range );
6401 }catch(e){
6402 optimze( range );
6403 }
6404
6405 } else {
6406 optimze( range );
6407 }
6408 } else {
6409 var sel = me.getNative();
6410 if ( sel && sel.rangeCount ) {
6411 var firstRange = sel.getRangeAt( 0 );
6412 var lastRange = sel.getRangeAt( sel.rangeCount - 1 );
6413 range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset );
6414 if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) {
6415 optimze( range );
6416 }
6417 } else {
6418 //trace:1734 有可能已经不在dom树上了,标识的节点
6419 if ( this._bakRange && domUtils.inDoc( this._bakRange.startContainer, this.document ) ){
6420 return this._bakRange;
6421 }
6422 optimze( range );
6423 }
6424 }
6425 return this._bakRange = range;
6426 },
6427
6428 /**
6429 * 获取开始元素,用于状态反射
6430 * @method getStart
6431 * @return { Element } 获得开始元素
6432 * @example
6433 * ```javascript
6434 * editor.selection.getStart();
6435 * ```
6436 */
6437 getStart:function () {
6438 if ( this._cachedStartElement ) {
6439 return this._cachedStartElement;
6440 }
6441 var range = browser.ie9below ? this.getIERange() : this.getRange(),
6442 tmpRange,
6443 start, tmp, parent;
6444 if ( browser.ie9below ) {
6445 if ( !range ) {
6446 //todo 给第一个值可能会有问题
6447 return this.document.body.firstChild;
6448 }
6449 //control元素
6450 if ( range.item ){
6451 return range.item( 0 );
6452 }
6453 tmpRange = range.duplicate();
6454 //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
6455 tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 );
6456 tmpRange.collapse( 1 );
6457 start = tmpRange.parentElement();
6458 parent = tmp = range.parentElement();
6459 while ( tmp = tmp.parentNode ) {
6460 if ( tmp == start ) {
6461 start = parent;
6462 break;
6463 }
6464 }
6465 } else {
6466 range.shrinkBoundary();
6467 start = range.startContainer;
6468 if ( start.nodeType == 1 && start.hasChildNodes() ){
6469 start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )];
6470 }
6471 if ( start.nodeType == 3 ){
6472 return start.parentNode;
6473 }
6474 }
6475 return start;
6476 },
6477
6478 /**
6479 * 得到选区中的文本
6480 * @method getText
6481 * @return { String } 选区中包含的文本
6482 * @example
6483 * ```javascript
6484 * editor.selection.getText();
6485 * ```
6486 */
6487 getText:function () {
6488 var nativeSel, nativeRange;
6489 if ( this.isFocus() && (nativeSel = this.getNative()) ) {
6490 nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
6491 return browser.ie9below ? nativeRange.text : nativeRange.toString();
6492 }
6493 return '';
6494 },
6495
6496 /**
6497 * 清除选区
6498 * @method clearRange
6499 * @example
6500 * ```javascript
6501 * editor.selection.clearRange();
6502 * ```
6503 */
6504 clearRange : function(){
6505 this.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
6506 }
6507 };
6508})();
6509
6510// core/Editor.js
6511/**
6512 * 编辑器主类,包含编辑器提供的大部分公用接口
6513 * @file
6514 * @module UE
6515 * @class Editor
6516 * @since 1.2.6.1
6517 */
6518
6519/**
6520 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
6521 * @unfile
6522 * @module UE
6523 */
6524
6525/**
6526 * UEditor的核心类,为用户提供与编辑器交互的接口。
6527 * @unfile
6528 * @module UE
6529 * @class Editor
6530 */
6531
6532(function () {
6533 var uid = 0, _selectionChangeTimer;
6534
6535 /**
6536 * 获取编辑器的html内容,赋值到编辑器所在表单的textarea文本域里面
6537 * @private
6538 * @method setValue
6539 * @param { UE.Editor } editor 编辑器事例
6540 */
6541 function setValue(form, editor) {
6542 var textarea;
6543 if (editor.textarea) {
6544 if (utils.isString(editor.textarea)) {
6545 for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
6546 if (ti.id == 'ueditor_textarea_' + editor.options.textarea) {
6547 textarea = ti;
6548 break;
6549 }
6550 }
6551 } else {
6552 textarea = editor.textarea;
6553 }
6554 }
6555 if (!textarea) {
6556 form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
6557 'name': editor.options.textarea,
6558 'id': 'ueditor_textarea_' + editor.options.textarea,
6559 'style': "display:none"
6560 }));
6561 //不要产生多个textarea
6562 editor.textarea = textarea;
6563 }
6564 !textarea.getAttribute('name') && textarea.setAttribute('name', editor.options.textarea );
6565 textarea.value = editor.hasContents() ?
6566 (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
6567 ''
6568 }
6569 function loadPlugins(me){
6570 //初始化插件
6571 for (var pi in UE.plugins) {
6572 UE.plugins[pi].call(me);
6573 }
6574
6575 }
6576 function checkCurLang(I18N){
6577 for(var lang in I18N){
6578 return lang
6579 }
6580 }
6581
6582 function langReadied(me){
6583 me.langIsReady = true;
6584
6585 me.fireEvent("langReady");
6586 }
6587
6588 /**
6589 * 编辑器准备就绪后会触发该事件
6590 * @module UE
6591 * @class Editor
6592 * @event ready
6593 * @remind render方法执行完成之后,会触发该事件
6594 * @remind
6595 * @example
6596 * ```javascript
6597 * editor.addListener( 'ready', function( editor ) {
6598 * editor.execCommand( 'focus' ); //编辑器家在完成后,让编辑器拿到焦点
6599 * } );
6600 * ```
6601 */
6602 /**
6603 * 执行destroy方法,会触发该事件
6604 * @module UE
6605 * @class Editor
6606 * @event destroy
6607 * @see UE.Editor:destroy()
6608 */
6609 /**
6610 * 执行reset方法,会触发该事件
6611 * @module UE
6612 * @class Editor
6613 * @event reset
6614 * @see UE.Editor:reset()
6615 */
6616 /**
6617 * 执行focus方法,会触发该事件
6618 * @module UE
6619 * @class Editor
6620 * @event focus
6621 * @see UE.Editor:focus(Boolean)
6622 */
6623 /**
6624 * 语言加载完成会触发该事件
6625 * @module UE
6626 * @class Editor
6627 * @event langReady
6628 */
6629 /**
6630 * 运行命令之后会触发该命令
6631 * @module UE
6632 * @class Editor
6633 * @event beforeExecCommand
6634 */
6635 /**
6636 * 运行命令之后会触发该命令
6637 * @module UE
6638 * @class Editor
6639 * @event afterExecCommand
6640 */
6641 /**
6642 * 运行命令之前会触发该命令
6643 * @module UE
6644 * @class Editor
6645 * @event firstBeforeExecCommand
6646 */
6647 /**
6648 * 在getContent方法执行之前会触发该事件
6649 * @module UE
6650 * @class Editor
6651 * @event beforeGetContent
6652 * @see UE.Editor:getContent()
6653 */
6654 /**
6655 * 在getContent方法执行之后会触发该事件
6656 * @module UE
6657 * @class Editor
6658 * @event afterGetContent
6659 * @see UE.Editor:getContent()
6660 */
6661 /**
6662 * 在getAllHtml方法执行时会触发该事件
6663 * @module UE
6664 * @class Editor
6665 * @event getAllHtml
6666 * @see UE.Editor:getAllHtml()
6667 */
6668 /**
6669 * 在setContent方法执行之前会触发该事件
6670 * @module UE
6671 * @class Editor
6672 * @event beforeSetContent
6673 * @see UE.Editor:setContent(String)
6674 */
6675 /**
6676 * 在setContent方法执行之后会触发该事件
6677 * @module UE
6678 * @class Editor
6679 * @event afterSetContent
6680 * @see UE.Editor:setContent(String)
6681 */
6682 /**
6683 * 每当编辑器内部选区发生改变时,将触发该事件
6684 * @event selectionchange
6685 * @warning 该事件的触发非常频繁,不建议在该事件的处理过程中做重量级的处理
6686 * @example
6687 * ```javascript
6688 * editor.addListener( 'selectionchange', function( editor ) {
6689 * console.log('选区发生改变');
6690 * }
6691 */
6692 /**
6693 * 在所有selectionchange的监听函数执行之前,会触发该事件
6694 * @module UE
6695 * @class Editor
6696 * @event beforeSelectionChange
6697 * @see UE.Editor:selectionchange
6698 */
6699 /**
6700 * 在所有selectionchange的监听函数执行完之后,会触发该事件
6701 * @module UE
6702 * @class Editor
6703 * @event afterSelectionChange
6704 * @see UE.Editor:selectionchange
6705 */
6706 /**
6707 * 编辑器内容发生改变时会触发该事件
6708 * @module UE
6709 * @class Editor
6710 * @event contentChange
6711 */
6712
6713
6714 /**
6715 * 以默认参数构建一个编辑器实例
6716 * @constructor
6717 * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
6718 * @example
6719 * ```javascript
6720 * var editor = new UE.Editor();
6721 * editor.execCommand('blod');
6722 * ```
6723 * @see UE.Config
6724 */
6725
6726 /**
6727 * 以给定的参数集合创建一个编辑器实例,对于未指定的参数,将应用默认参数。
6728 * @constructor
6729 * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
6730 * @param { Object } setting 创建编辑器的参数
6731 * @example
6732 * ```javascript
6733 * var editor = new UE.Editor();
6734 * editor.execCommand('blod');
6735 * ```
6736 * @see UE.Config
6737 */
6738 var Editor = UE.Editor = function (options) {
6739 var me = this;
6740 me.uid = uid++;
6741 EventBase.call(me);
6742 me.commands = {};
6743 me.options = utils.extend(utils.clone(options || {}), UEDITOR_CONFIG, true);
6744 me.shortcutkeys = {};
6745 me.inputRules = [];
6746 me.outputRules = [];
6747 //设置默认的常用属性
6748 me.setOpt(Editor.defaultOptions(me));
6749
6750 /* 尝试异步加载后台配置 */
6751 me.loadServerConfig();
6752
6753 if(!utils.isEmptyObject(UE.I18N)){
6754 //修改默认的语言类型
6755 me.options.lang = checkCurLang(UE.I18N);
6756 UE.plugin.load(me);
6757 langReadied(me);
6758
6759 }else{
6760 utils.loadFile(document, {
6761 src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
6762 tag: "script",
6763 type: "text/javascript",
6764 defer: "defer"
6765 }, function () {
6766 UE.plugin.load(me);
6767 langReadied(me);
6768 });
6769 }
6770
6771 UE.instants['ueditorInstant' + me.uid] = me;
6772 };
6773 Editor.prototype = {
6774 registerCommand : function(name,obj){
6775 this.commands[name] = obj;
6776 },
6777 /**
6778 * 编辑器对外提供的监听ready事件的接口, 通过调用该方法,达到的效果与监听ready事件是一致的
6779 * @method ready
6780 * @param { Function } fn 编辑器ready之后所执行的回调, 如果在注册事件之前编辑器已经ready,将会
6781 * 立即触发该回调。
6782 * @remind 需要等待编辑器加载完成后才能执行的代码,可以使用该方法传入
6783 * @example
6784 * ```javascript
6785 * editor.ready( function( editor ) {
6786 * editor.setContent('初始化完毕');
6787 * } );
6788 * ```
6789 * @see UE.Editor.event:ready
6790 */
6791 ready: function (fn) {
6792 var me = this;
6793 if (fn) {
6794 me.isReady ? fn.apply(me) : me.addListener('ready', fn);
6795 }
6796 },
6797
6798 /**
6799 * 该方法是提供给插件里面使用,设置配置项默认值
6800 * @method setOpt
6801 * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
6802 * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
6803 * @param { String } key 编辑器的可接受的选项名称
6804 * @param { * } val 该选项可接受的值
6805 * @example
6806 * ```javascript
6807 * editor.setOpt( 'initContent', '欢迎使用编辑器' );
6808 * ```
6809 */
6810
6811 /**
6812 * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值
6813 * @method setOpt
6814 * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
6815 * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
6816 * @param { Object } options 将要设置的选项的键值对对象
6817 * @example
6818 * ```javascript
6819 * editor.setOpt( {
6820 * 'initContent': '欢迎使用编辑器'
6821 * } );
6822 * ```
6823 */
6824 setOpt: function (key, val) {
6825 var obj = {};
6826 if (utils.isString(key)) {
6827 obj[key] = val
6828 } else {
6829 obj = key;
6830 }
6831 utils.extend(this.options, obj, true);
6832 },
6833 getOpt:function(key){
6834 return this.options[key]
6835 },
6836 /**
6837 * 销毁编辑器实例,使用textarea代替
6838 * @method destroy
6839 * @example
6840 * ```javascript
6841 * editor.destroy();
6842 * ```
6843 */
6844 destroy: function () {
6845
6846 var me = this;
6847 me.fireEvent('destroy');
6848 var container = me.container.parentNode;
6849 var textarea = me.textarea;
6850 if (!textarea) {
6851 textarea = document.createElement('textarea');
6852 container.parentNode.insertBefore(textarea, container);
6853 } else {
6854 textarea.style.display = ''
6855 }
6856
6857 textarea.style.width = me.iframe.offsetWidth + 'px';
6858 textarea.style.height = me.iframe.offsetHeight + 'px';
6859 textarea.value = me.getContent();
6860 textarea.id = me.key;
6861 container.innerHTML = '';
6862 domUtils.remove(container);
6863 var key = me.key;
6864 //trace:2004
6865 for (var p in me) {
6866 if (me.hasOwnProperty(p)) {
6867 delete this[p];
6868 }
6869 }
6870 UE.delEditor(key);
6871 },
6872
6873 /**
6874 * 渲染编辑器的DOM到指定容器
6875 * @method render
6876 * @param { String } containerId 指定一个容器ID
6877 * @remind 执行该方法,会触发ready事件
6878 * @warning 必须且只能调用一次
6879 */
6880
6881 /**
6882 * 渲染编辑器的DOM到指定容器
6883 * @method render
6884 * @param { Element } containerDom 直接指定容器对象
6885 * @remind 执行该方法,会触发ready事件
6886 * @warning 必须且只能调用一次
6887 */
6888 render: function (container) {
6889 var me = this,
6890 options = me.options,
6891 getStyleValue=function(attr){
6892 return parseInt(domUtils.getComputedStyle(container,attr));
6893 };
6894 if (utils.isString(container)) {
6895 container = document.getElementById(container);
6896 }
6897 if (container) {
6898 if(options.initialFrameWidth){
6899 options.minFrameWidth = options.initialFrameWidth
6900 }else{
6901 options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
6902 }
6903 if(options.initialFrameHeight){
6904 options.minFrameHeight = options.initialFrameHeight
6905 }else{
6906 options.initialFrameHeight = options.minFrameHeight = container.offsetHeight;
6907 }
6908
6909 container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth-
6910 getStyleValue("padding-left")- getStyleValue("padding-right") +'px';
6911 container.style.height = /%$/.test(options.initialFrameHeight) ? '100%' : options.initialFrameHeight -
6912 getStyleValue("padding-top")- getStyleValue("padding-bottom") +'px';
6913
6914 container.style.zIndex = options.zIndex;
6915
6916 var html = ( ie && browser.version < 9 ? '' : '<!DOCTYPE html>') +
6917 '<html xmlns=\'http://www.w3.org/1999/xhtml\' class=\'view\' ><head>' +
6918 '<style type=\'text/css\'>' +
6919 //设置四周的留边
6920 '.view{padding:0;word-wrap:break-word;cursor:text;height:90%;}\n' +
6921 //设置默认字体和字号
6922 //font-family不能呢随便改,在safari下fillchar会有解析问题
6923 'body{margin:8px;font-family:sans-serif;font-size:16px;}' +
6924 //设置段落间距
6925 'p{margin:5px 0;}</style>' +
6926 ( options.iframeCssUrl ? '<link rel=\'stylesheet\' type=\'text/css\' href=\'' + utils.unhtml(options.iframeCssUrl) + '\'/>' : '' ) +
6927 (options.initialStyle ? '<style>' + options.initialStyle + '</style>' : '') +
6928 '</head><body class=\'view\' ></body>' +
6929 '<script type=\'text/javascript\' ' + (ie ? 'defer=\'defer\'' : '' ) +' id=\'_initialScript\'>' +
6930 'setTimeout(function(){editor = window.parent.UE.instants[\'ueditorInstant' + me.uid + '\'];editor._setup(document);},0);' +
6931 'var _tmpScript = document.getElementById(\'_initialScript\');_tmpScript.parentNode.removeChild(_tmpScript);</script></html>';
6932 container.appendChild(domUtils.createElement(document, 'iframe', {
6933 id: 'ueditor_' + me.uid,
6934 width: "100%",
6935 height: "100%",
6936 frameborder: "0",
6937 //先注释掉了,加的原因忘记了,但开启会直接导致全屏模式下内容多时不会出现滚动条
6938// scrolling :'no',
6939 src: 'javascript:void(function(){document.open();' + (options.customDomain && document.domain != location.hostname ? 'document.domain="' + document.domain + '";' : '') +
6940 'document.write("' + html + '");document.close();}())'
6941 }));
6942 container.style.overflow = 'hidden';
6943 //解决如果是给定的百分比,会导致高度算不对的问题
6944 setTimeout(function(){
6945 if( /%$/.test(options.initialFrameWidth)){
6946 options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
6947 //如果这里给定宽度,会导致ie在拖动窗口大小时,编辑区域不随着变化
6948// container.style.width = options.initialFrameWidth + 'px';
6949 }
6950 if(/%$/.test(options.initialFrameHeight)){
6951 options.minFrameHeight = options.initialFrameHeight = container.offsetHeight;
6952 container.style.height = options.initialFrameHeight + 'px';
6953 }
6954 })
6955 }
6956 },
6957
6958 /**
6959 * 编辑器初始化
6960 * @method _setup
6961 * @private
6962 * @param { Element } doc 编辑器Iframe中的文档对象
6963 */
6964 _setup: function (doc) {
6965
6966 var me = this,
6967 options = me.options;
6968 if (ie) {
6969 doc.body.disabled = true;
6970 doc.body.contentEditable = true;
6971 doc.body.disabled = false;
6972 } else {
6973 doc.body.contentEditable = true;
6974 }
6975 doc.body.spellcheck = false;
6976 me.document = doc;
6977 me.window = doc.defaultView || doc.parentWindow;
6978 me.iframe = me.window.frameElement;
6979 me.body = doc.body;
6980 me.selection = new dom.Selection(doc);
6981 //gecko初始化就能得到range,无法判断isFocus了
6982 var geckoSel;
6983 if (browser.gecko && (geckoSel = this.selection.getNative())) {
6984 geckoSel.removeAllRanges();
6985 }
6986 this._initEvents();
6987 //为form提交提供一个隐藏的textarea
6988 for (var form = this.iframe.parentNode; !domUtils.isBody(form); form = form.parentNode) {
6989 if (form.tagName == 'FORM') {
6990 me.form = form;
6991 if(me.options.autoSyncData){
6992 domUtils.on(me.window,'blur',function(){
6993 setValue(form,me);
6994 });
6995 }else{
6996 domUtils.on(form, 'submit', function () {
6997 setValue(this, me);
6998 });
6999 }
7000 break;
7001 }
7002 }
7003 if (options.initialContent) {
7004 if (options.autoClearinitialContent) {
7005 var oldExecCommand = me.execCommand;
7006 me.execCommand = function () {
7007 me.fireEvent('firstBeforeExecCommand');
7008 return oldExecCommand.apply(me, arguments);
7009 };
7010 this._setDefaultContent(options.initialContent);
7011 } else
7012 this.setContent(options.initialContent, false, true);
7013 }
7014
7015 //编辑器不能为空内容
7016
7017 if (domUtils.isEmptyNode(me.body)) {
7018 me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
7019 }
7020 //如果要求focus, 就把光标定位到内容开始
7021 if (options.focus) {
7022 setTimeout(function () {
7023 me.focus(me.options.focusInEnd);
7024 //如果自动清除开着,就不需要做selectionchange;
7025 !me.options.autoClearinitialContent && me._selectionChange();
7026 }, 0);
7027 }
7028 if (!me.container) {
7029 me.container = this.iframe.parentNode;
7030 }
7031 if (options.fullscreen && me.ui) {
7032 me.ui.setFullScreen(true);
7033 }
7034
7035 try {
7036 me.document.execCommand('2D-position', false, false);
7037 } catch (e) {
7038 }
7039 try {
7040 me.document.execCommand('enableInlineTableEditing', false, false);
7041 } catch (e) {
7042 }
7043 try {
7044 me.document.execCommand('enableObjectResizing', false, false);
7045 } catch (e) {
7046 }
7047
7048 //挂接快捷键
7049 me._bindshortcutKeys();
7050 me.isReady = 1;
7051 me.fireEvent('ready');
7052 options.onready && options.onready.call(me);
7053 if (!browser.ie9below) {
7054 domUtils.on(me.window, ['blur', 'focus'], function (e) {
7055 //chrome下会出现alt+tab切换时,导致选区位置不对
7056 if (e.type == 'blur') {
7057 me._bakRange = me.selection.getRange();
7058 try {
7059 me._bakNativeRange = me.selection.getNative().getRangeAt(0);
7060 me.selection.getNative().removeAllRanges();
7061 } catch (e) {
7062 me._bakNativeRange = null;
7063 }
7064
7065 } else {
7066 try {
7067 me._bakRange && me._bakRange.select();
7068 } catch (e) {
7069 }
7070 }
7071 });
7072 }
7073 //trace:1518 ff3.6body不够寛,会导致点击空白处无法获得焦点
7074 if (browser.gecko && browser.version <= 10902) {
7075 //修复ff3.6初始化进来,不能点击获得焦点
7076 me.body.contentEditable = false;
7077 setTimeout(function () {
7078 me.body.contentEditable = true;
7079 }, 100);
7080 setInterval(function () {
7081 me.body.style.height = me.iframe.offsetHeight - 20 + 'px'
7082 }, 100)
7083 }
7084
7085 !options.isShow && me.setHide();
7086 options.readonly && me.setDisabled();
7087 },
7088
7089 /**
7090 * 同步数据到编辑器所在的form
7091 * 从编辑器的容器节点向上查找form元素,若找到,就同步编辑内容到找到的form里,为提交数据做准备,主要用于是手动提交的情况
7092 * 后台取得数据的键值,使用你容器上的name属性,如果没有就使用参数里的textarea项
7093 * @method sync
7094 * @example
7095 * ```javascript
7096 * editor.sync();
7097 * form.sumbit(); //form变量已经指向了form元素
7098 * ```
7099 */
7100
7101 /**
7102 * 根据传入的formId,在页面上查找要同步数据的表单,若找到,就同步编辑内容到找到的form里,为提交数据做准备
7103 * 后台取得数据的键值,该键值默认使用给定的编辑器容器的name属性,如果没有name属性则使用参数项里给定的“textarea”项
7104 * @method sync
7105 * @param { String } formID 指定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
7106 */
7107 sync: function (formId) {
7108 var me = this,
7109 form = formId ? document.getElementById(formId) :
7110 domUtils.findParent(me.iframe.parentNode, function (node) {
7111 return node.tagName == 'FORM'
7112 }, true);
7113 form && setValue(form, me);
7114 },
7115
7116 /**
7117 * 设置编辑器高度
7118 * @method setHeight
7119 * @remind 当配置项autoHeightEnabled为真时,该方法无效
7120 * @param { Number } number 设置的高度值,纯数值,不带单位
7121 * @example
7122 * ```javascript
7123 * editor.setHeight(number);
7124 * ```
7125 */
7126 setHeight: function (height,notSetHeight) {
7127 if (height !== parseInt(this.iframe.parentNode.style.height)) {
7128 this.iframe.parentNode.style.height = height + 'px';
7129 }
7130 !notSetHeight && (this.options.minFrameHeight = this.options.initialFrameHeight = height);
7131 this.body.style.height = height + 'px';
7132 !notSetHeight && this.trigger('setHeight')
7133 },
7134
7135 /**
7136 * 为编辑器的编辑命令提供快捷键
7137 * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
7138 * @method addshortcutkey
7139 * @param { Object } keyset 命令名和快捷键键值对对象,多个按钮的快捷键用“+”分隔
7140 * @example
7141 * ```javascript
7142 * editor.addshortcutkey({
7143 * "Bold" : "ctrl+66",//^B
7144 * "Italic" : "ctrl+73", //^I
7145 * });
7146 * ```
7147 */
7148 /**
7149 * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
7150 * @method addshortcutkey
7151 * @param { String } cmd 触发快捷键时,响应的命令
7152 * @param { String } keys 快捷键的字符串,多个按钮用“+”分隔
7153 * @example
7154 * ```javascript
7155 * editor.addshortcutkey("Underline", "ctrl+85"); //^U
7156 * ```
7157 */
7158 addshortcutkey: function (cmd, keys) {
7159 var obj = {};
7160 if (keys) {
7161 obj[cmd] = keys
7162 } else {
7163 obj = cmd;
7164 }
7165 utils.extend(this.shortcutkeys, obj)
7166 },
7167
7168 /**
7169 * 对编辑器设置keydown事件监听,绑定快捷键和命令,当快捷键组合触发成功,会响应对应的命令
7170 * @method _bindshortcutKeys
7171 * @private
7172 */
7173 _bindshortcutKeys: function () {
7174 var me = this, shortcutkeys = this.shortcutkeys;
7175 me.addListener('keydown', function (type, e) {
7176 var keyCode = e.keyCode || e.which;
7177 for (var i in shortcutkeys) {
7178 var tmp = shortcutkeys[i].split(',');
7179 for (var t = 0, ti; ti = tmp[t++];) {
7180 ti = ti.split(':');
7181 var key = ti[0], param = ti[1];
7182 if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
7183 if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
7184 && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
7185 && keyCode == RegExp.$3
7186 ) ||
7187 keyCode == RegExp.$1
7188 ) {
7189 if (me.queryCommandState(i,param) != -1)
7190 me.execCommand(i, param);
7191 domUtils.preventDefault(e);
7192 }
7193 }
7194 }
7195
7196 }
7197 });
7198 },
7199
7200 /**
7201 * 获取编辑器的内容
7202 * @method getContent
7203 * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
7204 * @return { String } 编辑器的内容字符串, 如果编辑器的内容为空,或者是空的标签内容(如:”&lt;p&gt;&lt;br/&gt;&lt;/p&gt;“), 则返回空字符串
7205 * @example
7206 * ```javascript
7207 * //编辑器html内容:<p>1<strong>2<em>34</em>5</strong>6</p>
7208 * var content = editor.getContent(); //返回值:<p>1<strong>2<em>34</em>5</strong>6</p>
7209 * ```
7210 */
7211
7212 /**
7213 * 获取编辑器的内容。 可以通过参数定义编辑器内置的判空规则
7214 * @method getContent
7215 * @param { Function } fn 自定的判空规则, 要求该方法返回一个boolean类型的值,
7216 * 代表当前编辑器的内容是否空,
7217 * 如果返回true, 则该方法将直接返回空字符串;如果返回false,则编辑器将返回
7218 * 经过内置过滤规则处理后的内容。
7219 * @remind 该方法在处理包含有初始化内容的时候能起到很好的作用。
7220 * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
7221 * @return { String } 编辑器的内容字符串
7222 * @example
7223 * ```javascript
7224 * // editor 是一个编辑器的实例
7225 * var content = editor.getContent( function ( editor ) {
7226 * return editor.body.innerHTML === '欢迎使用UEditor'; //返回空字符串
7227 * } );
7228 * ```
7229 */
7230 getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) {
7231 var me = this;
7232 if (cmd && utils.isFunction(cmd)) {
7233 fn = cmd;
7234 cmd = '';
7235 }
7236 if (fn ? !fn() : !this.hasContents()) {
7237 return '';
7238 }
7239 me.fireEvent('beforegetcontent');
7240 var root = UE.htmlparser(me.body.innerHTML,ignoreBlank);
7241 me.filterOutputRule(root);
7242 me.fireEvent('aftergetcontent', cmd,root);
7243 return root.toHtml(formatter);
7244 },
7245
7246 /**
7247 * 取得完整的html代码,可以直接显示成完整的html文档
7248 * @method getAllHtml
7249 * @return { String } 编辑器的内容html文档字符串
7250 * @eaxmple
7251 * ```javascript
7252 * editor.getAllHtml(); //返回格式大致是: <html><head>...</head><body>...</body></html>
7253 * ```
7254 */
7255 getAllHtml: function () {
7256 var me = this,
7257 headHtml = [],
7258 html = '';
7259 me.fireEvent('getAllHtml', headHtml);
7260 if (browser.ie && browser.version > 8) {
7261 var headHtmlForIE9 = '';
7262 utils.each(me.document.styleSheets, function (si) {
7263 headHtmlForIE9 += ( si.href ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />' : '<style>' + si.cssText + '</style>');
7264 });
7265 utils.each(me.document.getElementsByTagName('script'), function (si) {
7266 headHtmlForIE9 += si.outerHTML;
7267 });
7268
7269 }
7270 return '<html><head>' + (me.options.charset ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>' : '')
7271 + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '</head>'
7272 + '<body ' + (ie && browser.version < 9 ? 'class="view"' : '') + '>' + me.getContent(null, null, true) + '</body></html>';
7273 },
7274
7275 /**
7276 * 得到编辑器的纯文本内容,但会保留段落格式
7277 * @method getPlainTxt
7278 * @return { String } 编辑器带段落格式的纯文本内容字符串
7279 * @example
7280 * ```javascript
7281 * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
7282 * console.log(editor.getPlainTxt()); //输出:"1\n2\n
7283 * ```
7284 */
7285 getPlainTxt: function () {
7286 var reg = new RegExp(domUtils.fillChar, 'g'),
7287 html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
7288 html = html.replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
7289 .replace(/<br\/?>/gi, '\n')
7290 .replace(/<[^>/]+>/g, '')
7291 .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
7292 return dtd.$block[c] ? '\n' : b ? b : '';
7293 });
7294 //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
7295 return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/&nbsp;/g, ' ');
7296 },
7297
7298 /**
7299 * 获取编辑器中的纯文本内容,没有段落格式
7300 * @method getContentTxt
7301 * @return { String } 编辑器不带段落格式的纯文本内容字符串
7302 * @example
7303 * ```javascript
7304 * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
7305 * console.log(editor.getPlainTxt()); //输出:"12
7306 * ```
7307 */
7308 getContentTxt: function () {
7309 var reg = new RegExp(domUtils.fillChar, 'g');
7310 //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
7311 return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
7312 },
7313
7314 /**
7315 * 设置编辑器的内容,可修改编辑器当前的html内容
7316 * @method setContent
7317 * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
7318 * @warning 该方法会触发selectionchange事件
7319 * @param { String } html 要插入的html内容
7320 * @example
7321 * ```javascript
7322 * editor.getContent('<p>test</p>');
7323 * ```
7324 */
7325
7326 /**
7327 * 设置编辑器的内容,可修改编辑器当前的html内容
7328 * @method setContent
7329 * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
7330 * @warning 该方法会触发selectionchange事件
7331 * @param { String } html 要插入的html内容
7332 * @param { Boolean } isAppendTo 若传入true,不清空原来的内容,在最后插入内容,否则,清空内容再插入
7333 * @example
7334 * ```javascript
7335 * //假设设置前的编辑器内容是 <p>old text</p>
7336 * editor.setContent('<p>new text</p>', true); //插入的结果是<p>old text</p><p>new text</p>
7337 * ```
7338 */
7339 setContent: function (html, isAppendTo, notFireSelectionchange) {
7340 var me = this;
7341
7342 me.fireEvent('beforesetcontent', html);
7343 var root = UE.htmlparser(html);
7344 me.filterInputRule(root);
7345 html = root.toHtml();
7346
7347 me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
7348
7349
7350 function isCdataDiv(node){
7351 return node.tagName == 'DIV' && node.getAttribute('cdata_tag');
7352 }
7353 //给文本或者inline节点套p标签
7354 if (me.options.enterTag == 'p') {
7355
7356 var child = this.body.firstChild, tmpNode;
7357 if (!child || child.nodeType == 1 &&
7358 (dtd.$cdata[child.tagName] || isCdataDiv(child) ||
7359 domUtils.isCustomeNode(child)
7360 )
7361 && child === this.body.lastChild) {
7362 this.body.innerHTML = '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + this.body.innerHTML;
7363
7364 } else {
7365 var p = me.document.createElement('p');
7366 while (child) {
7367 while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
7368 tmpNode = child.nextSibling;
7369 p.appendChild(child);
7370 child = tmpNode;
7371 }
7372 if (p.firstChild) {
7373 if (!child) {
7374 me.body.appendChild(p);
7375 break;
7376 } else {
7377 child.parentNode.insertBefore(p, child);
7378 p = me.document.createElement('p');
7379 }
7380 }
7381 child = child.nextSibling;
7382 }
7383 }
7384 }
7385 me.fireEvent('aftersetcontent');
7386 me.fireEvent('contentchange');
7387
7388 !notFireSelectionchange && me._selectionChange();
7389 //清除保存的选区
7390 me._bakRange = me._bakIERange = me._bakNativeRange = null;
7391 //trace:1742 setContent后gecko能得到焦点问题
7392 var geckoSel;
7393 if (browser.gecko && (geckoSel = this.selection.getNative())) {
7394 geckoSel.removeAllRanges();
7395 }
7396 if(me.options.autoSyncData){
7397 me.form && setValue(me.form,me);
7398 }
7399 },
7400
7401 /**
7402 * 让编辑器获得焦点,默认focus到编辑器头部
7403 * @method focus
7404 * @example
7405 * ```javascript
7406 * editor.focus()
7407 * ```
7408 */
7409
7410 /**
7411 * 让编辑器获得焦点,toEnd确定focus位置
7412 * @method focus
7413 * @param { Boolean } toEnd 默认focus到编辑器头部,toEnd为true时focus到内容尾部
7414 * @example
7415 * ```javascript
7416 * editor.focus(true)
7417 * ```
7418 */
7419 focus: function (toEnd) {
7420 try {
7421 var me = this,
7422 rng = me.selection.getRange();
7423 if (toEnd) {
7424 var node = me.body.lastChild;
7425 if(node && node.nodeType == 1 && !dtd.$empty[node.tagName]){
7426 if(domUtils.isEmptyBlock(node)){
7427 rng.setStartAtFirst(node)
7428 }else{
7429 rng.setStartAtLast(node)
7430 }
7431 rng.collapse(true);
7432 }
7433 rng.setCursor(true);
7434 } else {
7435 if(!rng.collapsed && domUtils.isBody(rng.startContainer) && rng.startOffset == 0){
7436
7437 var node = me.body.firstChild;
7438 if(node && node.nodeType == 1 && !dtd.$empty[node.tagName]){
7439 rng.setStartAtFirst(node).collapse(true);
7440 }
7441 }
7442
7443 rng.select(true);
7444
7445 }
7446 this.fireEvent('focus selectionchange');
7447 } catch (e) {
7448 }
7449
7450 },
7451 isFocus:function(){
7452 return this.selection.isFocus();
7453 },
7454 blur:function(){
7455 var sel = this.selection.getNative();
7456 if(sel.empty && browser.ie){
7457 var nativeRng = document.body.createTextRange();
7458 nativeRng.moveToElementText(document.body);
7459 nativeRng.collapse(true);
7460 nativeRng.select();
7461 sel.empty()
7462 }else{
7463 sel.removeAllRanges()
7464 }
7465
7466 //this.fireEvent('blur selectionchange');
7467 },
7468 /**
7469 * 初始化UE事件及部分事件代理
7470 * @method _initEvents
7471 * @private
7472 */
7473 _initEvents: function () {
7474 var me = this,
7475 doc = me.document,
7476 win = me.window;
7477 me._proxyDomEvent = utils.bind(me._proxyDomEvent, me);
7478 domUtils.on(doc, ['click', 'contextmenu', 'mousedown', 'keydown', 'keyup', 'keypress', 'mouseup', 'mouseover', 'mouseout', 'selectstart'], me._proxyDomEvent);
7479 domUtils.on(win, ['focus', 'blur'], me._proxyDomEvent);
7480 domUtils.on(me.body,'drop',function(e){
7481 //阻止ff下默认的弹出新页面打开图片
7482 if(browser.gecko && e.stopPropagation) { e.stopPropagation(); }
7483 me.fireEvent('contentchange')
7484 });
7485 domUtils.on(doc, ['mouseup', 'keydown'], function (evt) {
7486 //特殊键不触发selectionchange
7487 if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
7488 return;
7489 }
7490 if (evt.button == 2)return;
7491 me._selectionChange(250, evt);
7492 });
7493 },
7494 /**
7495 * 触发事件代理
7496 * @method _proxyDomEvent
7497 * @private
7498 * @return { * } fireEvent的返回值
7499 * @see UE.EventBase:fireEvent(String)
7500 */
7501 _proxyDomEvent: function (evt) {
7502 if(this.fireEvent('before' + evt.type.replace(/^on/, '').toLowerCase()) === false){
7503 return false;
7504 }
7505 if(this.fireEvent(evt.type.replace(/^on/, ''), evt) === false){
7506 return false;
7507 }
7508 return this.fireEvent('after' + evt.type.replace(/^on/, '').toLowerCase())
7509 },
7510 /**
7511 * 变化选区
7512 * @method _selectionChange
7513 * @private
7514 */
7515 _selectionChange: function (delay, evt) {
7516 var me = this;
7517 //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
7518// if ( !me.selection.isFocus() ){
7519// return;
7520// }
7521
7522
7523 var hackForMouseUp = false;
7524 var mouseX, mouseY;
7525 if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
7526 var range = this.selection.getRange();
7527 if (!range.collapsed) {
7528 hackForMouseUp = true;
7529 mouseX = evt.clientX;
7530 mouseY = evt.clientY;
7531 }
7532 }
7533 clearTimeout(_selectionChangeTimer);
7534 _selectionChangeTimer = setTimeout(function () {
7535 if (!me.selection || !me.selection.getNative()) {
7536 return;
7537 }
7538 //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
7539 //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
7540 var ieRange;
7541 if (hackForMouseUp && me.selection.getNative().type == 'None') {
7542 ieRange = me.document.body.createTextRange();
7543 try {
7544 ieRange.moveToPoint(mouseX, mouseY);
7545 } catch (ex) {
7546 ieRange = null;
7547 }
7548 }
7549 var bakGetIERange;
7550 if (ieRange) {
7551 bakGetIERange = me.selection.getIERange;
7552 me.selection.getIERange = function () {
7553 return ieRange;
7554 };
7555 }
7556 me.selection.cache();
7557 if (bakGetIERange) {
7558 me.selection.getIERange = bakGetIERange;
7559 }
7560 if (me.selection._cachedRange && me.selection._cachedStartElement) {
7561 me.fireEvent('beforeselectionchange');
7562 // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
7563 me.fireEvent('selectionchange', !!evt);
7564 me.fireEvent('afterselectionchange');
7565 me.selection.clear();
7566 }
7567 }, delay || 50);
7568 },
7569
7570 /**
7571 * 执行编辑命令
7572 * @method _callCmdFn
7573 * @private
7574 * @param { String } fnName 函数名称
7575 * @param { * } args 传给命令函数的参数
7576 * @return { * } 返回命令函数运行的返回值
7577 */
7578 _callCmdFn: function (fnName, args) {
7579 var cmdName = args[0].toLowerCase(),
7580 cmd, cmdFn;
7581 cmd = this.commands[cmdName] || UE.commands[cmdName];
7582 cmdFn = cmd && cmd[fnName];
7583 //没有querycommandstate或者没有command的都默认返回0
7584 if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
7585 return 0;
7586 } else if (cmdFn) {
7587 return cmdFn.apply(this, args);
7588 }
7589 },
7590
7591 /**
7592 * 执行编辑命令cmdName,完成富文本编辑效果
7593 * @method execCommand
7594 * @param { String } cmdName 需要执行的命令
7595 * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
7596 * @return { * } 返回命令函数运行的返回值
7597 * @example
7598 * ```javascript
7599 * editor.execCommand(cmdName);
7600 * ```
7601 */
7602 execCommand: function (cmdName) {
7603 cmdName = cmdName.toLowerCase();
7604 var me = this,
7605 result,
7606 cmd = me.commands[cmdName] || UE.commands[cmdName];
7607 if (!cmd || !cmd.execCommand) {
7608 return null;
7609 }
7610 if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
7611 me.__hasEnterExecCommand = true;
7612 if (me.queryCommandState.apply(me,arguments) != -1) {
7613 me.fireEvent('saveScene');
7614 me.fireEvent.apply(me, ['beforeexeccommand', cmdName].concat(arguments));
7615 result = this._callCmdFn('execCommand', arguments);
7616 //保存场景时,做了内容对比,再看是否进行contentchange触发,这里多触发了一次,去掉
7617// (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
7618 me.fireEvent.apply(me, ['afterexeccommand', cmdName].concat(arguments));
7619 me.fireEvent('saveScene');
7620 }
7621 me.__hasEnterExecCommand = false;
7622 } else {
7623 result = this._callCmdFn('execCommand', arguments);
7624 (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
7625 }
7626 (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
7627 return result;
7628 },
7629
7630 /**
7631 * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
7632 * @method queryCommandState
7633 * @param { String } cmdName 需要查询的命令名称
7634 * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
7635 * @return { Number } number 返回放前命令的状态,返回值三种情况:(-1|0|1)
7636 * @example
7637 * ```javascript
7638 * editor.queryCommandState(cmdName) => (-1|0|1)
7639 * ```
7640 * @see COMMAND.LIST
7641 */
7642 queryCommandState: function (cmdName) {
7643 return this._callCmdFn('queryCommandState', arguments);
7644 },
7645
7646 /**
7647 * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
7648 * @method queryCommandValue
7649 * @param { String } cmdName 需要查询的命令名称
7650 * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
7651 * @remind 只有部分插件有此方法
7652 * @return { * } 返回每个命令特定的当前状态值
7653 * @grammar editor.queryCommandValue(cmdName) => {*}
7654 * @see COMMAND.LIST
7655 */
7656 queryCommandValue: function (cmdName) {
7657 return this._callCmdFn('queryCommandValue', arguments);
7658 },
7659
7660 /**
7661 * 检查编辑区域中是否有内容
7662 * @method hasContents
7663 * @remind 默认有文本内容,或者有以下节点都不认为是空
7664 * table,ul,ol,dl,iframe,area,base,col,hr,img,embed,input,link,meta,param
7665 * @return { Boolean } 检查有内容返回true,否则返回false
7666 * @example
7667 * ```javascript
7668 * editor.hasContents()
7669 * ```
7670 */
7671
7672 /**
7673 * 检查编辑区域中是否有内容,若包含参数tags中的节点类型,直接返回true
7674 * @method hasContents
7675 * @param { Array } tags 传入数组判断时用到的节点类型
7676 * @return { Boolean } 若文档中包含tags数组里对应的tag,返回true,否则返回false
7677 * @example
7678 * ```javascript
7679 * editor.hasContents(['span']);
7680 * ```
7681 */
7682 hasContents: function (tags) {
7683 if (tags) {
7684 for (var i = 0, ci; ci = tags[i++];) {
7685 if (this.document.getElementsByTagName(ci).length > 0) {
7686 return true;
7687 }
7688 }
7689 }
7690 if (!domUtils.isEmptyBlock(this.body)) {
7691 return true
7692 }
7693 //随时添加,定义的特殊标签如果存在,不能认为是空
7694 tags = ['div'];
7695 for (i = 0; ci = tags[i++];) {
7696 var nodes = domUtils.getElementsByTagName(this.document, ci);
7697 for (var n = 0, cn; cn = nodes[n++];) {
7698 if (domUtils.isCustomeNode(cn)) {
7699 return true;
7700 }
7701 }
7702 }
7703 return false;
7704 },
7705
7706 /**
7707 * 重置编辑器,可用来做多个tab使用同一个编辑器实例
7708 * @method reset
7709 * @remind 此方法会清空编辑器内容,清空回退列表,会触发reset事件
7710 * @example
7711 * ```javascript
7712 * editor.reset()
7713 * ```
7714 */
7715 reset: function () {
7716 this.fireEvent('reset');
7717 },
7718
7719 /**
7720 * 设置当前编辑区域可以编辑
7721 * @method setEnabled
7722 * @example
7723 * ```javascript
7724 * editor.setEnabled()
7725 * ```
7726 */
7727 setEnabled: function () {
7728 var me = this, range;
7729 if (me.body.contentEditable == 'false') {
7730 me.body.contentEditable = true;
7731 range = me.selection.getRange();
7732 //有可能内容丢失了
7733 try {
7734 range.moveToBookmark(me.lastBk);
7735 delete me.lastBk
7736 } catch (e) {
7737 range.setStartAtFirst(me.body).collapse(true)
7738 }
7739 range.select(true);
7740 if (me.bkqueryCommandState) {
7741 me.queryCommandState = me.bkqueryCommandState;
7742 delete me.bkqueryCommandState;
7743 }
7744 if (me.bkqueryCommandValue) {
7745 me.queryCommandValue = me.bkqueryCommandValue;
7746 delete me.bkqueryCommandValue;
7747 }
7748 me.fireEvent('selectionchange');
7749 }
7750 },
7751 enable: function () {
7752 return this.setEnabled();
7753 },
7754
7755 /** 设置当前编辑区域不可编辑
7756 * @method setDisabled
7757 */
7758
7759 /** 设置当前编辑区域不可编辑,except中的命令除外
7760 * @method setDisabled
7761 * @param { String } except 例外命令的字符串
7762 * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
7763 * @example
7764 * ```javascript
7765 * editor.setDisabled('bold'); //禁用工具栏中除加粗之外的所有功能
7766 * ```
7767 */
7768
7769 /** 设置当前编辑区域不可编辑,except中的命令除外
7770 * @method setDisabled
7771 * @param { Array } except 例外命令的字符串数组,数组中的命令仍然可以执行
7772 * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
7773 * @example
7774 * ```javascript
7775 * editor.setDisabled(['bold','insertimage']); //禁用工具栏中除加粗和插入图片之外的所有功能
7776 * ```
7777 */
7778 setDisabled: function (except) {
7779 var me = this;
7780 except = except ? utils.isArray(except) ? except : [except] : [];
7781 if (me.body.contentEditable == 'true') {
7782 if (!me.lastBk) {
7783 me.lastBk = me.selection.getRange().createBookmark(true);
7784 }
7785 me.body.contentEditable = false;
7786 me.bkqueryCommandState = me.queryCommandState;
7787 me.bkqueryCommandValue = me.queryCommandValue;
7788 me.queryCommandState = function (type) {
7789 if (utils.indexOf(except, type) != -1) {
7790 return me.bkqueryCommandState.apply(me, arguments);
7791 }
7792 return -1;
7793 };
7794 me.queryCommandValue = function (type) {
7795 if (utils.indexOf(except, type) != -1) {
7796 return me.bkqueryCommandValue.apply(me, arguments);
7797 }
7798 return null;
7799 };
7800 me.fireEvent('selectionchange');
7801 }
7802 },
7803 disable: function (except) {
7804 return this.setDisabled(except);
7805 },
7806
7807 /**
7808 * 设置默认内容
7809 * @method _setDefaultContent
7810 * @private
7811 * @param { String } cont 要存入的内容
7812 */
7813 _setDefaultContent: function () {
7814 function clear() {
7815 var me = this;
7816 if (me.document.getElementById('initContent')) {
7817 me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>';
7818 me.removeListener('firstBeforeExecCommand focus', clear);
7819 setTimeout(function () {
7820 me.focus();
7821 me._selectionChange();
7822 }, 0)
7823 }
7824 }
7825
7826 return function (cont) {
7827 var me = this;
7828 me.body.innerHTML = '<p id="initContent">' + cont + '</p>';
7829
7830 me.addListener('firstBeforeExecCommand focus', clear);
7831 }
7832 }(),
7833
7834 /**
7835 * 显示编辑器
7836 * @method setShow
7837 * @example
7838 * ```javascript
7839 * editor.setShow()
7840 * ```
7841 */
7842 setShow: function () {
7843 var me = this, range = me.selection.getRange();
7844 if (me.container.style.display == 'none') {
7845 //有可能内容丢失了
7846 try {
7847 range.moveToBookmark(me.lastBk);
7848 delete me.lastBk
7849 } catch (e) {
7850 range.setStartAtFirst(me.body).collapse(true)
7851 }
7852 //ie下focus实效,所以做了个延迟
7853 setTimeout(function () {
7854 range.select(true);
7855 }, 100);
7856 me.container.style.display = '';
7857 }
7858
7859 },
7860 show: function () {
7861 return this.setShow();
7862 },
7863 /**
7864 * 隐藏编辑器
7865 * @method setHide
7866 * @example
7867 * ```javascript
7868 * editor.setHide()
7869 * ```
7870 */
7871 setHide: function () {
7872 var me = this;
7873 if (!me.lastBk) {
7874 me.lastBk = me.selection.getRange().createBookmark(true);
7875 }
7876 me.container.style.display = 'none'
7877 },
7878 hide: function () {
7879 return this.setHide();
7880 },
7881
7882 /**
7883 * 根据指定的路径,获取对应的语言资源
7884 * @method getLang
7885 * @param { String } path 路径根据的是lang目录下的语言文件的路径结构
7886 * @return { Object | String } 根据路径返回语言资源的Json格式对象或者语言字符串
7887 * @example
7888 * ```javascript
7889 * editor.getLang('contextMenu.delete'); //如果当前是中文,那返回是的是'删除'
7890 * ```
7891 */
7892 getLang: function (path) {
7893 var lang = UE.I18N[this.options.lang];
7894 if (!lang) {
7895 throw Error("not import language file");
7896 }
7897 path = (path || "").split(".");
7898 for (var i = 0, ci; ci = path[i++];) {
7899 lang = lang[ci];
7900 if (!lang)break;
7901 }
7902 return lang;
7903 },
7904
7905 /**
7906 * 计算编辑器html内容字符串的长度
7907 * @method getContentLength
7908 * @return { Number } 返回计算的长度
7909 * @example
7910 * ```javascript
7911 * //编辑器html内容<p><strong>132</strong></p>
7912 * editor.getContentLength() //返回27
7913 * ```
7914 */
7915 /**
7916 * 计算编辑器当前纯文本内容的长度
7917 * @method getContentLength
7918 * @param { Boolean } ingoneHtml 传入true时,只按照纯文本来计算
7919 * @return { Number } 返回计算的长度,内容中有hr/img/iframe标签,长度加1
7920 * @example
7921 * ```javascript
7922 * //编辑器html内容<p><strong>132</strong></p>
7923 * editor.getContentLength() //返回3
7924 * ```
7925 */
7926 getContentLength: function (ingoneHtml, tagNames) {
7927 var count = this.getContent(false,false,true).length;
7928 if (ingoneHtml) {
7929 tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']);
7930 count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length;
7931 for (var i = 0, ci; ci = tagNames[i++];) {
7932 count += this.document.getElementsByTagName(ci).length;
7933 }
7934 }
7935 return count;
7936 },
7937
7938 /**
7939 * 注册输入过滤规则
7940 * @method addInputRule
7941 * @param { Function } rule 要添加的过滤规则
7942 * @example
7943 * ```javascript
7944 * editor.addInputRule(function(root){
7945 * $.each(root.getNodesByTagName('div'),function(i,node){
7946 * node.tagName="p";
7947 * });
7948 * });
7949 * ```
7950 */
7951 addInputRule: function (rule) {
7952 this.inputRules.push(rule);
7953 },
7954
7955 /**
7956 * 执行注册的过滤规则
7957 * @method filterInputRule
7958 * @param { UE.uNode } root 要过滤的uNode节点
7959 * @remind 执行editor.setContent方法和执行'inserthtml'命令后,会运行该过滤函数
7960 * @example
7961 * ```javascript
7962 * editor.filterInputRule(editor.body);
7963 * ```
7964 * @see UE.Editor:addInputRule
7965 */
7966 filterInputRule: function (root) {
7967 for (var i = 0, ci; ci = this.inputRules[i++];) {
7968 ci.call(this, root)
7969 }
7970 },
7971
7972 /**
7973 * 注册输出过滤规则
7974 * @method addOutputRule
7975 * @param { Function } rule 要添加的过滤规则
7976 * @example
7977 * ```javascript
7978 * editor.addOutputRule(function(root){
7979 * $.each(root.getNodesByTagName('p'),function(i,node){
7980 * node.tagName="div";
7981 * });
7982 * });
7983 * ```
7984 */
7985 addOutputRule: function (rule) {
7986 this.outputRules.push(rule)
7987 },
7988
7989 /**
7990 * 根据输出过滤规则,过滤编辑器内容
7991 * @method filterOutputRule
7992 * @remind 执行editor.getContent方法的时候,会先运行该过滤函数
7993 * @param { UE.uNode } root 要过滤的uNode节点
7994 * @example
7995 * ```javascript
7996 * editor.filterOutputRule(editor.body);
7997 * ```
7998 * @see UE.Editor:addOutputRule
7999 */
8000 filterOutputRule: function (root) {
8001 for (var i = 0, ci; ci = this.outputRules[i++];) {
8002 ci.call(this, root)
8003 }
8004 },
8005
8006 /**
8007 * 根据action名称获取请求的路径
8008 * @method getActionUrl
8009 * @remind 假如没有设置serverUrl,会根据imageUrl设置默认的controller路径
8010 * @param { String } action action名称
8011 * @example
8012 * ```javascript
8013 * editor.getActionUrl('config'); //返回 "/ueditor/php/controller.php?action=config"
8014 * editor.getActionUrl('image'); //返回 "/ueditor/php/controller.php?action=uplaodimage"
8015 * editor.getActionUrl('scrawl'); //返回 "/ueditor/php/controller.php?action=uplaodscrawl"
8016 * editor.getActionUrl('imageManager'); //返回 "/ueditor/php/controller.php?action=listimage"
8017 * ```
8018 */
8019 getActionUrl: function(action){
8020 var actionName = this.getOpt(action) || action,
8021 imageUrl = this.getOpt('imageUrl'),
8022 serverUrl = this.getOpt('serverUrl');
8023
8024 if(!serverUrl && imageUrl) {
8025 serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2');
8026 }
8027
8028 if(serverUrl) {
8029 serverUrl = serverUrl + (serverUrl.indexOf('?') == -1 ? '?':'&') + 'action=' + (actionName || '');
8030 return utils.formatUrl(serverUrl);
8031 } else {
8032 return '';
8033 }
8034 }
8035 };
8036 utils.inherits(Editor, EventBase);
8037})();
8038
8039
8040// core/Editor.defaultoptions.js
8041//维护编辑器一下默认的不在插件中的配置项
8042UE.Editor.defaultOptions = function(editor){
8043
8044 var _url = editor.options.UEDITOR_HOME_URL;
8045 return {
8046 isShow: true,
8047 initialContent: '',
8048 initialStyle:'',
8049 autoClearinitialContent: false,
8050 iframeCssUrl: _url + 'themes/iframe.css',
8051 textarea: 'editorValue',
8052 focus: false,
8053 focusInEnd: true,
8054 autoClearEmptyNode: true,
8055 fullscreen: false,
8056 readonly: false,
8057 zIndex: 999,
8058 imagePopup: true,
8059 enterTag: 'p',
8060 customDomain: false,
8061 lang: 'zh-cn',
8062 langPath: _url + 'lang/',
8063 theme: 'default',
8064 themePath: _url + 'themes/',
8065 allHtmlEnabled: false,
8066 scaleEnabled: false,
8067 tableNativeEditInFF: false,
8068 autoSyncData : true,
8069 fileNameFormat: '{time}{rand:6}'
8070 }
8071};
8072
8073// core/loadconfig.js
8074(function(){
8075
8076 UE.Editor.prototype.loadServerConfig = function(){
8077 var me = this;
8078 setTimeout(function(){
8079 try{
8080 me.options.imageUrl && me.setOpt('serverUrl', me.options.imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2'));
8081
8082 var configUrl = me.getActionUrl('config'),
8083 isJsonp = utils.isCrossDomainUrl(configUrl);
8084
8085 /* 发出ajax请求 */
8086 me._serverConfigLoaded = false;
8087
8088 configUrl && UE.ajax.request(configUrl,{
8089 'method': 'GET',
8090 'dataType': isJsonp ? 'jsonp':'',
8091 'onsuccess':function(r){
8092 try {
8093 var config = isJsonp ? r:eval("("+r.responseText+")");
8094 utils.extend(me.options, config);
8095 me.fireEvent('serverConfigLoaded');
8096 me._serverConfigLoaded = true;
8097 } catch (e) {
8098 showErrorMsg(me.getLang('loadconfigFormatError'));
8099 }
8100 },
8101 'onerror':function(){
8102 showErrorMsg(me.getLang('loadconfigHttpError'));
8103 }
8104 });
8105 } catch(e){
8106 showErrorMsg(me.getLang('loadconfigError'));
8107 }
8108 });
8109
8110 function showErrorMsg(msg) {
8111 console && console.error(msg);
8112 //me.fireEvent('showMessage', {
8113 // 'title': msg,
8114 // 'type': 'error'
8115 //});
8116 }
8117 };
8118
8119 UE.Editor.prototype.isServerConfigLoaded = function(){
8120 var me = this;
8121 return me._serverConfigLoaded || false;
8122 };
8123
8124 UE.Editor.prototype.afterConfigReady = function(handler){
8125 if (!handler || !utils.isFunction(handler)) return;
8126 var me = this;
8127 var readyHandler = function(){
8128 handler.apply(me, arguments);
8129 me.removeListener('serverConfigLoaded', readyHandler);
8130 };
8131
8132 if (me.isServerConfigLoaded()) {
8133 handler.call(me, 'serverConfigLoaded');
8134 } else {
8135 me.addListener('serverConfigLoaded', readyHandler);
8136 }
8137 };
8138
8139})();
8140
8141
8142// core/ajax.js
8143/**
8144 * @file
8145 * @module UE.ajax
8146 * @since 1.2.6.1
8147 */
8148
8149/**
8150 * 提供对ajax请求的支持
8151 * @module UE.ajax
8152 */
8153UE.ajax = function() {
8154
8155 //创建一个ajaxRequest对象
8156 var fnStr = 'XMLHttpRequest()';
8157 try {
8158 new ActiveXObject("Msxml2.XMLHTTP");
8159 fnStr = 'ActiveXObject(\'Msxml2.XMLHTTP\')';
8160 } catch (e) {
8161 try {
8162 new ActiveXObject("Microsoft.XMLHTTP");
8163 fnStr = 'ActiveXObject(\'Microsoft.XMLHTTP\')'
8164 } catch (e) {
8165 }
8166 }
8167 var creatAjaxRequest = new Function('return new ' + fnStr);
8168
8169
8170 /**
8171 * 将json参数转化成适合ajax提交的参数列表
8172 * @param json
8173 */
8174 function json2str(json) {
8175 var strArr = [];
8176 for (var i in json) {
8177 //忽略默认的几个参数
8178 if(i=="method" || i=="timeout" || i=="async" || i=="dataType" || i=="callback") continue;
8179 //忽略控制
8180 if(json[i] == undefined || json[i] == null) continue;
8181 //传递过来的对象和函数不在提交之列
8182 if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
8183 strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) );
8184 } else if (utils.isArray(json[i])) {
8185 //支持传数组内容
8186 for(var j = 0; j < json[i].length; j++) {
8187 strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) );
8188 }
8189 }
8190 }
8191 return strArr.join("&");
8192 }
8193
8194 function doAjax(url, ajaxOptions) {
8195 var xhr = creatAjaxRequest(),
8196 //是否超时
8197 timeIsOut = false,
8198 //默认参数
8199 defaultAjaxOptions = {
8200 method:"POST",
8201 timeout:5000,
8202 async:true,
8203 data:{},//需要传递对象的话只能覆盖
8204 onsuccess:function() {
8205 },
8206 onerror:function() {
8207 }
8208 };
8209
8210 if (typeof url === "object") {
8211 ajaxOptions = url;
8212 url = ajaxOptions.url;
8213 }
8214 if (!xhr || !url) return;
8215 var ajaxOpts = ajaxOptions ? utils.extend(defaultAjaxOptions,ajaxOptions) : defaultAjaxOptions;
8216
8217 var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
8218 //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
8219 if (!utils.isEmptyObject(ajaxOpts.data)){
8220 submitStr += (submitStr? "&":"") + json2str(ajaxOpts.data);
8221 }
8222 //超时检测
8223 var timerID = setTimeout(function() {
8224 if (xhr.readyState != 4) {
8225 timeIsOut = true;
8226 xhr.abort();
8227 clearTimeout(timerID);
8228 }
8229 }, ajaxOpts.timeout);
8230
8231 var method = ajaxOpts.method.toUpperCase();
8232 var str = url + (url.indexOf("?")==-1?"?":"&") + (method=="POST"?"":submitStr+ "&noCache=" + +new Date);
8233 xhr.open(method, str, ajaxOpts.async);
8234 xhr.onreadystatechange = function() {
8235 if (xhr.readyState == 4) {
8236 if (!timeIsOut && xhr.status == 200) {
8237 ajaxOpts.onsuccess(xhr);
8238 } else {
8239 ajaxOpts.onerror(xhr);
8240 }
8241 }
8242 };
8243 if (method == "POST") {
8244 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
8245 xhr.send(submitStr);
8246 } else {
8247 xhr.send(null);
8248 }
8249 }
8250
8251 function doJsonp(url, opts) {
8252
8253 var successhandler = opts.onsuccess || function(){},
8254 scr = document.createElement('SCRIPT'),
8255 options = opts || {},
8256 charset = options['charset'],
8257 callbackField = options['jsonp'] || 'callback',
8258 callbackFnName,
8259 timeOut = options['timeOut'] || 0,
8260 timer,
8261 reg = new RegExp('(\\?|&)' + callbackField + '=([^&]*)'),
8262 matches;
8263
8264 if (utils.isFunction(successhandler)) {
8265 callbackFnName = 'bd__editor__' + Math.floor(Math.random() * 2147483648).toString(36);
8266 window[callbackFnName] = getCallBack(0);
8267 } else if(utils.isString(successhandler)){
8268 callbackFnName = successhandler;
8269 } else {
8270 if (matches = reg.exec(url)) {
8271 callbackFnName = matches[2];
8272 }
8273 }
8274
8275 url = url.replace(reg, '\x241' + callbackField + '=' + callbackFnName);
8276
8277 if (url.search(reg) < 0) {
8278 url += (url.indexOf('?') < 0 ? '?' : '&') + callbackField + '=' + callbackFnName;
8279 }
8280
8281 var queryStr = json2str(opts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
8282 //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
8283 if (!utils.isEmptyObject(opts.data)){
8284 queryStr += (queryStr? "&":"") + json2str(opts.data);
8285 }
8286 if (queryStr) {
8287 url = url.replace(/\?/, '?' + queryStr + '&');
8288 }
8289
8290 scr.onerror = getCallBack(1);
8291 if( timeOut ){
8292 timer = setTimeout(getCallBack(1), timeOut);
8293 }
8294 createScriptTag(scr, url, charset);
8295
8296 function createScriptTag(scr, url, charset) {
8297 scr.setAttribute('type', 'text/javascript');
8298 scr.setAttribute('defer', 'defer');
8299 charset && scr.setAttribute('charset', charset);
8300 scr.setAttribute('src', url);
8301 document.getElementsByTagName('head')[0].appendChild(scr);
8302 }
8303
8304 function getCallBack(onTimeOut){
8305 return function(){
8306 try {
8307 if(onTimeOut){
8308 options.onerror && options.onerror();
8309 }else{
8310 try{
8311 clearTimeout(timer);
8312 successhandler.apply(window, arguments);
8313 } catch (e){}
8314 }
8315 } catch (exception) {
8316 options.onerror && options.onerror.call(window, exception);
8317 } finally {
8318 options.oncomplete && options.oncomplete.apply(window, arguments);
8319 scr.parentNode && scr.parentNode.removeChild(scr);
8320 window[callbackFnName] = null;
8321 try {
8322 delete window[callbackFnName];
8323 }catch(e){}
8324 }
8325 }
8326 }
8327 }
8328
8329 return {
8330 /**
8331 * 根据给定的参数项,向指定的url发起一个ajax请求。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
8332 * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调
8333 * @method request
8334 * @param { URLString } url ajax请求的url地址
8335 * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
8336 * @example
8337 * ```javascript
8338 * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s, 请求完成后执行相应的回调。
8339 * UE.ajax.requeset( 'sayhello.php', {
8340 *
8341 * //请求方法。可选值: 'GET', 'POST',默认值是'POST'
8342 * method: 'GET',
8343 *
8344 * //超时时间。 默认为5000, 单位是ms
8345 * timeout: 10000,
8346 *
8347 * //是否是异步请求。 true为异步请求, false为同步请求
8348 * async: true,
8349 *
8350 * //请求携带的数据。如果请求为GET请求, data会经过stringify后附加到请求url之后。
8351 * data: {
8352 * name: 'ueditor'
8353 * },
8354 *
8355 * //请求成功后的回调, 该回调接受当前的XMLHttpRequest对象作为参数。
8356 * onsuccess: function ( xhr ) {
8357 * console.log( xhr.responseText );
8358 * },
8359 *
8360 * //请求失败或者超时后的回调。
8361 * onerror: function ( xhr ) {
8362 * alert( 'Ajax请求失败' );
8363 * }
8364 *
8365 * } );
8366 * ```
8367 */
8368
8369 /**
8370 * 根据给定的参数项发起一个ajax请求, 参数项里必须包含一个url地址。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
8371 * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调。
8372 * @method request
8373 * @warning 如果在参数项里未提供一个key为“url”的地址值,则该请求将直接退出。
8374 * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
8375 * @example
8376 * ```javascript
8377 *
8378 * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s, 请求完成后不执行任何回调。
8379 * UE.ajax.requeset( 'sayhello.php', {
8380 *
8381 * //请求的地址, 该项是必须的。
8382 * url: 'sayhello.php'
8383 *
8384 * } );
8385 * ```
8386 */
8387 request:function(url, opts) {
8388 if (opts && opts.dataType == 'jsonp') {
8389 doJsonp(url, opts);
8390 } else {
8391 doAjax(url, opts);
8392 }
8393 },
8394 getJSONP:function(url, data, fn) {
8395 var opts = {
8396 'data': data,
8397 'oncomplete': fn
8398 };
8399 doJsonp(url, opts);
8400 }
8401 };
8402
8403
8404}();
8405
8406
8407// core/filterword.js
8408/**
8409 * UE过滤word的静态方法
8410 * @file
8411 */
8412
8413/**
8414 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
8415 * @module UE
8416 */
8417
8418
8419/**
8420 * 根据传入html字符串过滤word
8421 * @module UE
8422 * @since 1.2.6.1
8423 * @method filterWord
8424 * @param { String } html html字符串
8425 * @return { String } 已过滤后的结果字符串
8426 * @example
8427 * ```javascript
8428 * UE.filterWord(html);
8429 * ```
8430 */
8431var filterWord = UE.filterWord = function () {
8432
8433 //是否是word过来的内容
8434 function isWordDocument( str ) {
8435 return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str );
8436 }
8437 //去掉小数
8438 function transUnit( v ) {
8439 v = v.replace( /[\d.]+\w+/g, function ( m ) {
8440 return utils.transUnitToPx(m);
8441 } );
8442 return v;
8443 }
8444
8445 function filterPasteWord( str ) {
8446 return str.replace(/[\t\r\n]+/g,' ')
8447 .replace( /<!--[\s\S]*?-->/ig, "" )
8448 //转换图片
8449 .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi,function(str){
8450 //opera能自己解析出image所这里直接返回空
8451 if(browser.opera){
8452 return '';
8453 }
8454 try{
8455 //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
8456 if(/Bitmap/i.test(str)){
8457 return '';
8458 }
8459 var width = str.match(/width:([ \d.]*p[tx])/i)[1],
8460 height = str.match(/height:([ \d.]*p[tx])/i)[1],
8461 src = str.match(/src=\s*"([^"]*)"/i)[1];
8462 return '<img width="'+ transUnit(width) +'" height="'+transUnit(height) +'" src="' + src + '" />';
8463 } catch(e){
8464 return '';
8465 }
8466 })
8467 //针对wps添加的多余标签处理
8468 .replace(/<\/?div[^>]*>/g,'')
8469 //去掉多余的属性
8470 .replace( /v:\w+=(["']?)[^'"]+\1/g, '' )
8471 .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" )
8472 .replace( /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>" )
8473 //去掉多余的属性
8474 .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){
8475 //保留list的标示
8476 return name == 'class' && val == 'MsoListParagraph' ? str : ''
8477 })
8478 //清除多余的font/span不能匹配&nbsp;有可能是空格
8479 .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){
8480 return c.replace(/[\t\r\n ]+/g,' ')
8481 })
8482 //处理style的问题
8483 .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) {
8484 var n = [],
8485 s = style.replace( /^\s+|\s+$/, '' )
8486 .replace(/&#39;/g,'\'')
8487 .replace( /&quot;/gi, "'" )
8488 .replace(/[\d.]+(cm|pt)/g,function(str){
8489 return utils.transUnitToPx(str)
8490 })
8491 .split( /;\s*/g );
8492
8493 for ( var i = 0,v; v = s[i];i++ ) {
8494
8495 var name, value,
8496 parts = v.split( ":" );
8497
8498 if ( parts.length == 2 ) {
8499 name = parts[0].toLowerCase();
8500 value = parts[1].toLowerCase();
8501 if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0
8502 ||
8503 /^(margin)\w*/.test(name) && /^0\w+$/.test(value)
8504 ){
8505 continue;
8506 }
8507
8508 switch ( name ) {
8509 case "mso-padding-alt":
8510 case "mso-padding-top-alt":
8511 case "mso-padding-right-alt":
8512 case "mso-padding-bottom-alt":
8513 case "mso-padding-left-alt":
8514 case "mso-margin-alt":
8515 case "mso-margin-top-alt":
8516 case "mso-margin-right-alt":
8517 case "mso-margin-bottom-alt":
8518 case "mso-margin-left-alt":
8519 //ie下会出现挤到一起的情况
8520 //case "mso-table-layout-alt":
8521 case "mso-height":
8522 case "mso-width":
8523 case "mso-vertical-align-alt":
8524 //trace:1819 ff下会解析出padding在table上
8525 if(!/<table/.test(tag))
8526 n[i] = name.replace( /^mso-|-alt$/g, "" ) + ":" + transUnit( value );
8527 continue;
8528 case "horiz-align":
8529 n[i] = "text-align:" + value;
8530 continue;
8531
8532 case "vert-align":
8533 n[i] = "vertical-align:" + value;
8534 continue;
8535
8536 case "font-color":
8537 case "mso-foreground":
8538 n[i] = "color:" + value;
8539 continue;
8540
8541 case "mso-background":
8542 case "mso-highlight":
8543 n[i] = "background:" + value;
8544 continue;
8545
8546 case "mso-default-height":
8547 n[i] = "min-height:" + transUnit( value );
8548 continue;
8549
8550 case "mso-default-width":
8551 n[i] = "min-width:" + transUnit( value );
8552 continue;
8553
8554 case "mso-padding-between-alt":
8555 n[i] = "border-collapse:separate;border-spacing:" + transUnit( value );
8556 continue;
8557
8558 case "text-line-through":
8559 if ( (value == "single") || (value == "double") ) {
8560 n[i] = "text-decoration:line-through";
8561 }
8562 continue;
8563 case "mso-zero-height":
8564 if ( value == "yes" ) {
8565 n[i] = "display:none";
8566 }
8567 continue;
8568// case 'background':
8569// break;
8570 case 'margin':
8571 if ( !/[1-9]/.test( value ) ) {
8572 continue;
8573 }
8574
8575 }
8576
8577 if ( /^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:decor|trans)|top-bar|version|vnd|word-break)/.test( name )
8578 ||
8579 /text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)
8580 ) {
8581 continue;
8582 }
8583
8584 n[i] = name + ":" + parts[1];
8585 }
8586 }
8587 return tag + (n.length ? ' style="' + n.join( ';').replace(/;{2,}/g,';') + '"' : '');
8588 })
8589
8590
8591 }
8592
8593 return function ( html ) {
8594 return (isWordDocument( html ) ? filterPasteWord( html ) : html);
8595 };
8596}();
8597
8598// core/node.js
8599/**
8600 * 编辑器模拟的节点类
8601 * @file
8602 * @module UE
8603 * @class uNode
8604 * @since 1.2.6.1
8605 */
8606
8607/**
8608 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
8609 * @unfile
8610 * @module UE
8611 */
8612
8613(function () {
8614
8615 /**
8616 * 编辑器模拟的节点类
8617 * @unfile
8618 * @module UE
8619 * @class uNode
8620 */
8621
8622 /**
8623 * 通过一个键值对,创建一个uNode对象
8624 * @constructor
8625 * @param { Object } attr 传入要创建的uNode的初始属性
8626 * @example
8627 * ```javascript
8628 * var node = new uNode({
8629 * type:'element',
8630 * tagName:'span',
8631 * attrs:{style:'font-size:14px;'}
8632 * }
8633 * ```
8634 */
8635 var uNode = UE.uNode = function (obj) {
8636 this.type = obj.type;
8637 this.data = obj.data;
8638 this.tagName = obj.tagName;
8639 this.parentNode = obj.parentNode;
8640 this.attrs = obj.attrs || {};
8641 this.children = obj.children;
8642 };
8643
8644 var notTransAttrs = {
8645 'href':1,
8646 'src':1,
8647 '_src':1,
8648 '_href':1,
8649 'cdata_data':1
8650 };
8651
8652 var notTransTagName = {
8653 style:1,
8654 script:1
8655 };
8656
8657 var indentChar = ' ',
8658 breakChar = '\n';
8659
8660 function insertLine(arr, current, begin) {
8661 arr.push(breakChar);
8662 return current + (begin ? 1 : -1);
8663 }
8664
8665 function insertIndent(arr, current) {
8666 //插入缩进
8667 for (var i = 0; i < current; i++) {
8668 arr.push(indentChar);
8669 }
8670 }
8671
8672 //创建uNode的静态方法
8673 //支持标签和html
8674 uNode.createElement = function (html) {
8675 if (/[<>]/.test(html)) {
8676 return UE.htmlparser(html).children[0]
8677 } else {
8678 return new uNode({
8679 type:'element',
8680 children:[],
8681 tagName:html
8682 })
8683 }
8684 };
8685 uNode.createText = function (data,noTrans) {
8686 return new UE.uNode({
8687 type:'text',
8688 'data':noTrans ? data : utils.unhtml(data || '')
8689 })
8690 };
8691 function nodeToHtml(node, arr, formatter, current) {
8692 switch (node.type) {
8693 case 'root':
8694 for (var i = 0, ci; ci = node.children[i++];) {
8695 //插入新行
8696 if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
8697 insertLine(arr, current, true);
8698 insertIndent(arr, current)
8699 }
8700 nodeToHtml(ci, arr, formatter, current)
8701 }
8702 break;
8703 case 'text':
8704 isText(node, arr);
8705 break;
8706 case 'element':
8707 isElement(node, arr, formatter, current);
8708 break;
8709 case 'comment':
8710 isComment(node, arr, formatter);
8711 }
8712 return arr;
8713 }
8714
8715 function isText(node, arr) {
8716 if(node.parentNode.tagName == 'pre'){
8717 //源码模式下输入html标签,不能做转换处理,直接输出
8718 arr.push(node.data)
8719 }else{
8720 arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,' &nbsp;'))
8721 }
8722
8723 }
8724
8725 function isElement(node, arr, formatter, current) {
8726 var attrhtml = '';
8727 if (node.attrs) {
8728 attrhtml = [];
8729 var attrs = node.attrs;
8730 for (var a in attrs) {
8731 //这里就针对
8732 //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
8733 //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
8734 //有可能做的不够
8735 attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
8736 return '&quot;'
8737 }) : utils.unhtml(attrs[a])) + '"' : ''))
8738 }
8739 attrhtml = attrhtml.join(' ');
8740 }
8741 arr.push('<' + node.tagName +
8742 (attrhtml ? ' ' + attrhtml : '') +
8743 (dtd.$empty[node.tagName] ? '\/' : '' ) + '>'
8744 );
8745 //插入新行
8746 if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
8747 if(node.children && node.children.length){
8748 current = insertLine(arr, current, true);
8749 insertIndent(arr, current)
8750 }
8751
8752 }
8753 if (node.children && node.children.length) {
8754 for (var i = 0, ci; ci = node.children[i++];) {
8755 if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
8756 insertLine(arr, current);
8757 insertIndent(arr, current)
8758 }
8759 nodeToHtml(ci, arr, formatter, current)
8760 }
8761 }
8762 if (!dtd.$empty[node.tagName]) {
8763 if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
8764
8765 if(node.children && node.children.length){
8766 current = insertLine(arr, current);
8767 insertIndent(arr, current)
8768 }
8769 }
8770 arr.push('<\/' + node.tagName + '>');
8771 }
8772
8773 }
8774
8775 function isComment(node, arr) {
8776 arr.push('<!--' + node.data + '-->');
8777 }
8778
8779 function getNodeById(root, id) {
8780 var node;
8781 if (root.type == 'element' && root.getAttr('id') == id) {
8782 return root;
8783 }
8784 if (root.children && root.children.length) {
8785 for (var i = 0, ci; ci = root.children[i++];) {
8786 if (node = getNodeById(ci, id)) {
8787 return node;
8788 }
8789 }
8790 }
8791 }
8792
8793 function getNodesByTagName(node, tagName, arr) {
8794 if (node.type == 'element' && node.tagName == tagName) {
8795 arr.push(node);
8796 }
8797 if (node.children && node.children.length) {
8798 for (var i = 0, ci; ci = node.children[i++];) {
8799 getNodesByTagName(ci, tagName, arr)
8800 }
8801 }
8802 }
8803 function nodeTraversal(root,fn){
8804 if(root.children && root.children.length){
8805 for(var i= 0,ci;ci=root.children[i];){
8806 nodeTraversal(ci,fn);
8807 //ci被替换的情况,这里就不再走 fn了
8808 if(ci.parentNode ){
8809 if(ci.children && ci.children.length){
8810 fn(ci)
8811 }
8812 if(ci.parentNode) i++
8813 }
8814 }
8815 }else{
8816 fn(root)
8817 }
8818
8819 }
8820 uNode.prototype = {
8821
8822 /**
8823 * 当前节点对象,转换成html文本
8824 * @method toHtml
8825 * @return { String } 返回转换后的html字符串
8826 * @example
8827 * ```javascript
8828 * node.toHtml();
8829 * ```
8830 */
8831
8832 /**
8833 * 当前节点对象,转换成html文本
8834 * @method toHtml
8835 * @param { Boolean } formatter 是否格式化返回值
8836 * @return { String } 返回转换后的html字符串
8837 * @example
8838 * ```javascript
8839 * node.toHtml( true );
8840 * ```
8841 */
8842 toHtml:function (formatter) {
8843 var arr = [];
8844 nodeToHtml(this, arr, formatter, 0);
8845 return arr.join('')
8846 },
8847
8848 /**
8849 * 获取节点的html内容
8850 * @method innerHTML
8851 * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
8852 * @return { String } 返回节点的html内容
8853 * @example
8854 * ```javascript
8855 * var htmlstr = node.innerHTML();
8856 * ```
8857 */
8858
8859 /**
8860 * 设置节点的html内容
8861 * @method innerHTML
8862 * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
8863 * @param { String } htmlstr 传入要设置的html内容
8864 * @return { UE.uNode } 返回节点本身
8865 * @example
8866 * ```javascript
8867 * node.innerHTML('<span>text</span>');
8868 * ```
8869 */
8870 innerHTML:function (htmlstr) {
8871 if (this.type != 'element' || dtd.$empty[this.tagName]) {
8872 return this;
8873 }
8874 if (utils.isString(htmlstr)) {
8875 if(this.children){
8876 for (var i = 0, ci; ci = this.children[i++];) {
8877 ci.parentNode = null;
8878 }
8879 }
8880 this.children = [];
8881 var tmpRoot = UE.htmlparser(htmlstr);
8882 for (var i = 0, ci; ci = tmpRoot.children[i++];) {
8883 this.children.push(ci);
8884 ci.parentNode = this;
8885 }
8886 return this;
8887 } else {
8888 var tmpRoot = new UE.uNode({
8889 type:'root',
8890 children:this.children
8891 });
8892 return tmpRoot.toHtml();
8893 }
8894 },
8895
8896 /**
8897 * 获取节点的纯文本内容
8898 * @method innerText
8899 * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
8900 * @return { String } 返回节点的存文本内容
8901 * @example
8902 * ```javascript
8903 * var textStr = node.innerText();
8904 * ```
8905 */
8906
8907 /**
8908 * 设置节点的纯文本内容
8909 * @method innerText
8910 * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
8911 * @param { String } textStr 传入要设置的文本内容
8912 * @return { UE.uNode } 返回节点本身
8913 * @example
8914 * ```javascript
8915 * node.innerText('<span>text</span>');
8916 * ```
8917 */
8918 innerText:function (textStr,noTrans) {
8919 if (this.type != 'element' || dtd.$empty[this.tagName]) {
8920 return this;
8921 }
8922 if (textStr) {
8923 if(this.children){
8924 for (var i = 0, ci; ci = this.children[i++];) {
8925 ci.parentNode = null;
8926 }
8927 }
8928 this.children = [];
8929 this.appendChild(uNode.createText(textStr,noTrans));
8930 return this;
8931 } else {
8932 return this.toHtml().replace(/<[^>]+>/g, '');
8933 }
8934 },
8935
8936 /**
8937 * 获取当前对象的data属性
8938 * @method getData
8939 * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
8940 * @example
8941 * ```javascript
8942 * node.getData();
8943 * ```
8944 */
8945 getData:function () {
8946 if (this.type == 'element')
8947 return '';
8948 return this.data
8949 },
8950
8951 /**
8952 * 获取当前节点下的第一个子节点
8953 * @method firstChild
8954 * @return { UE.uNode } 返回第一个子节点
8955 * @example
8956 * ```javascript
8957 * node.firstChild(); //返回第一个子节点
8958 * ```
8959 */
8960 firstChild:function () {
8961// if (this.type != 'element' || dtd.$empty[this.tagName]) {
8962// return this;
8963// }
8964 return this.children ? this.children[0] : null;
8965 },
8966
8967 /**
8968 * 获取当前节点下的最后一个子节点
8969 * @method lastChild
8970 * @return { UE.uNode } 返回最后一个子节点
8971 * @example
8972 * ```javascript
8973 * node.lastChild(); //返回最后一个子节点
8974 * ```
8975 */
8976 lastChild:function () {
8977// if (this.type != 'element' || dtd.$empty[this.tagName] ) {
8978// return this;
8979// }
8980 return this.children ? this.children[this.children.length - 1] : null;
8981 },
8982
8983 /**
8984 * 获取和当前节点有相同父亲节点的前一个节点
8985 * @method previousSibling
8986 * @return { UE.uNode } 返回前一个节点
8987 * @example
8988 * ```javascript
8989 * node.children[2].previousSibling(); //返回子节点node.children[1]
8990 * ```
8991 */
8992 previousSibling : function(){
8993 var parent = this.parentNode;
8994 for (var i = 0, ci; ci = parent.children[i]; i++) {
8995 if (ci === this) {
8996 return i == 0 ? null : parent.children[i-1];
8997 }
8998 }
8999
9000 },
9001
9002 /**
9003 * 获取和当前节点有相同父亲节点的后一个节点
9004 * @method nextSibling
9005 * @return { UE.uNode } 返回后一个节点,找不到返回null
9006 * @example
9007 * ```javascript
9008 * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
9009 * ```
9010 */
9011 nextSibling : function(){
9012 var parent = this.parentNode;
9013 for (var i = 0, ci; ci = parent.children[i++];) {
9014 if (ci === this) {
9015 return parent.children[i];
9016 }
9017 }
9018 },
9019
9020 /**
9021 * 用新的节点替换当前节点
9022 * @method replaceChild
9023 * @param { UE.uNode } target 要替换成该节点参数
9024 * @param { UE.uNode } source 要被替换掉的节点
9025 * @return { UE.uNode } 返回替换之后的节点对象
9026 * @example
9027 * ```javascript
9028 * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
9029 * ```
9030 */
9031 replaceChild:function (target, source) {
9032 if (this.children) {
9033 if(target.parentNode){
9034 target.parentNode.removeChild(target);
9035 }
9036 for (var i = 0, ci; ci = this.children[i]; i++) {
9037 if (ci === source) {
9038 this.children.splice(i, 1, target);
9039 source.parentNode = null;
9040 target.parentNode = this;
9041 return target;
9042 }
9043 }
9044 }
9045 },
9046
9047 /**
9048 * 在节点的子节点列表最后位置插入一个节点
9049 * @method appendChild
9050 * @param { UE.uNode } node 要插入的节点
9051 * @return { UE.uNode } 返回刚插入的子节点
9052 * @example
9053 * ```javascript
9054 * node.appendChild( newNode ); //在node内插入子节点newNode
9055 * ```
9056 */
9057 appendChild:function (node) {
9058 if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
9059 if (!this.children) {
9060 this.children = []
9061 }
9062 if(node.parentNode){
9063 node.parentNode.removeChild(node);
9064 }
9065 for (var i = 0, ci; ci = this.children[i]; i++) {
9066 if (ci === node) {
9067 this.children.splice(i, 1);
9068 break;
9069 }
9070 }
9071 this.children.push(node);
9072 node.parentNode = this;
9073 return node;
9074 }
9075
9076
9077 },
9078
9079 /**
9080 * 在传入节点的前面插入一个节点
9081 * @method insertBefore
9082 * @param { UE.uNode } target 要插入的节点
9083 * @param { UE.uNode } source 在该参数节点前面插入
9084 * @return { UE.uNode } 返回刚插入的子节点
9085 * @example
9086 * ```javascript
9087 * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
9088 * ```
9089 */
9090 insertBefore:function (target, source) {
9091 if (this.children) {
9092 if(target.parentNode){
9093 target.parentNode.removeChild(target);
9094 }
9095 for (var i = 0, ci; ci = this.children[i]; i++) {
9096 if (ci === source) {
9097 this.children.splice(i, 0, target);
9098 target.parentNode = this;
9099 return target;
9100 }
9101 }
9102
9103 }
9104 },
9105
9106 /**
9107 * 在传入节点的后面插入一个节点
9108 * @method insertAfter
9109 * @param { UE.uNode } target 要插入的节点
9110 * @param { UE.uNode } source 在该参数节点后面插入
9111 * @return { UE.uNode } 返回刚插入的子节点
9112 * @example
9113 * ```javascript
9114 * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
9115 * ```
9116 */
9117 insertAfter:function (target, source) {
9118 if (this.children) {
9119 if(target.parentNode){
9120 target.parentNode.removeChild(target);
9121 }
9122 for (var i = 0, ci; ci = this.children[i]; i++) {
9123 if (ci === source) {
9124 this.children.splice(i + 1, 0, target);
9125 target.parentNode = this;
9126 return target;
9127 }
9128
9129 }
9130 }
9131 },
9132
9133 /**
9134 * 从当前节点的子节点列表中,移除节点
9135 * @method removeChild
9136 * @param { UE.uNode } node 要移除的节点引用
9137 * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
9138 * @return { * } 返回刚移除的子节点
9139 * @example
9140 * ```javascript
9141 * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
9142 * ```
9143 */
9144 removeChild:function (node,keepChildren) {
9145 if (this.children) {
9146 for (var i = 0, ci; ci = this.children[i]; i++) {
9147 if (ci === node) {
9148 this.children.splice(i, 1);
9149 ci.parentNode = null;
9150 if(keepChildren && ci.children && ci.children.length){
9151 for(var j= 0,cj;cj=ci.children[j];j++){
9152 this.children.splice(i+j,0,cj);
9153 cj.parentNode = this;
9154
9155 }
9156 }
9157 return ci;
9158 }
9159 }
9160 }
9161 },
9162
9163 /**
9164 * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
9165 * @method getAttr
9166 * @param { String } attrName 要获取的属性名称
9167 * @return { * } 返回attrs对象下的属性值
9168 * @example
9169 * ```javascript
9170 * node.getAttr('title');
9171 * ```
9172 */
9173 getAttr:function (attrName) {
9174 return this.attrs && this.attrs[attrName.toLowerCase()]
9175 },
9176
9177 /**
9178 * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
9179 * @method setAttr
9180 * @param { String } attrName 要设置的属性名称
9181 * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
9182 * @return { * } 返回attrs对象下的属性值
9183 * @example
9184 * ```javascript
9185 * node.setAttr('title','标题');
9186 * ```
9187 */
9188 setAttr:function (attrName, attrVal) {
9189 if (!attrName) {
9190 delete this.attrs;
9191 return;
9192 }
9193 if(!this.attrs){
9194 this.attrs = {};
9195 }
9196 if (utils.isObject(attrName)) {
9197 for (var a in attrName) {
9198 if (!attrName[a]) {
9199 delete this.attrs[a]
9200 } else {
9201 this.attrs[a.toLowerCase()] = attrName[a];
9202 }
9203 }
9204 } else {
9205 if (!attrVal) {
9206 delete this.attrs[attrName]
9207 } else {
9208 this.attrs[attrName.toLowerCase()] = attrVal;
9209 }
9210
9211 }
9212 },
9213
9214 /**
9215 * 获取当前节点在父节点下的位置索引
9216 * @method getIndex
9217 * @return { Number } 返回索引数值,如果没有父节点,返回-1
9218 * @example
9219 * ```javascript
9220 * node.getIndex();
9221 * ```
9222 */
9223 getIndex:function(){
9224 var parent = this.parentNode;
9225 for(var i= 0,ci;ci=parent.children[i];i++){
9226 if(ci === this){
9227 return i;
9228 }
9229 }
9230 return -1;
9231 },
9232
9233 /**
9234 * 在当前节点下,根据id查找节点
9235 * @method getNodeById
9236 * @param { String } id 要查找的id
9237 * @return { UE.uNode } 返回找到的节点
9238 * @example
9239 * ```javascript
9240 * node.getNodeById('textId');
9241 * ```
9242 */
9243 getNodeById:function (id) {
9244 var node;
9245 if (this.children && this.children.length) {
9246 for (var i = 0, ci; ci = this.children[i++];) {
9247 if (node = getNodeById(ci, id)) {
9248 return node;
9249 }
9250 }
9251 }
9252 },
9253
9254 /**
9255 * 在当前节点下,根据元素名称查找节点列表
9256 * @method getNodesByTagName
9257 * @param { String } tagNames 要查找的元素名称
9258 * @return { Array } 返回找到的节点列表
9259 * @example
9260 * ```javascript
9261 * node.getNodesByTagName('span');
9262 * ```
9263 */
9264 getNodesByTagName:function (tagNames) {
9265 tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
9266 var arr = [], me = this;
9267 utils.each(tagNames, function (tagName) {
9268 if (me.children && me.children.length) {
9269 for (var i = 0, ci; ci = me.children[i++];) {
9270 getNodesByTagName(ci, tagName, arr)
9271 }
9272 }
9273 });
9274 return arr;
9275 },
9276
9277 /**
9278 * 根据样式名称,获取节点的样式值
9279 * @method getStyle
9280 * @param { String } name 要获取的样式名称
9281 * @return { String } 返回样式值
9282 * @example
9283 * ```javascript
9284 * node.getStyle('font-size');
9285 * ```
9286 */
9287 getStyle:function (name) {
9288 var cssStyle = this.getAttr('style');
9289 if (!cssStyle) {
9290 return ''
9291 }
9292 var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i');
9293 var match = cssStyle.match(reg);
9294 if (match && match[0]) {
9295 return match[2]
9296 }
9297 return '';
9298 },
9299
9300 /**
9301 * 给节点设置样式
9302 * @method setStyle
9303 * @param { String } name 要设置的的样式名称
9304 * @param { String } val 要设置的的样值
9305 * @example
9306 * ```javascript
9307 * node.setStyle('font-size', '12px');
9308 * ```
9309 */
9310 setStyle:function (name, val) {
9311 function exec(name, val) {
9312 var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
9313 cssStyle = cssStyle.replace(reg, '$1');
9314 if (val) {
9315 cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
9316 }
9317
9318 }
9319
9320 var cssStyle = this.getAttr('style');
9321 if (!cssStyle) {
9322 cssStyle = '';
9323 }
9324 if (utils.isObject(name)) {
9325 for (var a in name) {
9326 exec(a, name[a])
9327 }
9328 } else {
9329 exec(name, val)
9330 }
9331 this.setAttr('style', utils.trim(cssStyle))
9332 },
9333
9334 /**
9335 * 传入一个函数,递归遍历当前节点下的所有节点
9336 * @method traversal
9337 * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
9338 * @example
9339 * ```javascript
9340 * traversal(node, function(){
9341 * console.log(node.type);
9342 * });
9343 * ```
9344 */
9345 traversal:function(fn){
9346 if(this.children && this.children.length){
9347 nodeTraversal(this,fn);
9348 }
9349 return this;
9350 }
9351 }
9352})();
9353
9354
9355// core/htmlparser.js
9356/**
9357 * html字符串转换成uNode节点
9358 * @file
9359 * @module UE
9360 * @since 1.2.6.1
9361 */
9362
9363/**
9364 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
9365 * @unfile
9366 * @module UE
9367 */
9368
9369/**
9370 * html字符串转换成uNode节点的静态方法
9371 * @method htmlparser
9372 * @param { String } htmlstr 要转换的html代码
9373 * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符
9374 * @return { uNode } 给定的html片段转换形成的uNode对象
9375 * @example
9376 * ```javascript
9377 * var root = UE.htmlparser('<p><b>htmlparser</b></p>', true);
9378 * ```
9379 */
9380
9381var htmlparser = UE.htmlparser = function (htmlstr,ignoreBlank) {
9382 //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
9383 //先去掉了,加上的原因忘了,这里先记录
9384 var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
9385 re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
9386
9387 //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
9388 var allowEmptyTags = {
9389 b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1,
9390 sub:1,img:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1
9391 };
9392 htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
9393 if(!ignoreBlank){
9394 htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){
9395 //br暂时单独处理
9396 if(b && allowEmptyTags[b.toLowerCase()]){
9397 return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,'');
9398 }
9399 return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),'');
9400 });
9401 }
9402
9403 var notTransAttrs = {
9404 'href':1,
9405 'src':1
9406 };
9407
9408 var uNode = UE.uNode,
9409 needParentNode = {
9410 'td':'tr',
9411 'tr':['tbody','thead','tfoot'],
9412 'tbody':'table',
9413 'th':'tr',
9414 'thead':'table',
9415 'tfoot':'table',
9416 'caption':'table',
9417 'li':['ul', 'ol'],
9418 'dt':'dl',
9419 'dd':'dl',
9420 'option':'select'
9421 },
9422 needChild = {
9423 'ol':'li',
9424 'ul':'li'
9425 };
9426
9427 function text(parent, data) {
9428
9429 if(needChild[parent.tagName]){
9430 var tmpNode = uNode.createElement(needChild[parent.tagName]);
9431 parent.appendChild(tmpNode);
9432 tmpNode.appendChild(uNode.createText(data));
9433 parent = tmpNode;
9434 }else{
9435
9436 parent.appendChild(uNode.createText(data));
9437 }
9438 }
9439
9440 function element(parent, tagName, htmlattr) {
9441 var needParentTag;
9442 if (needParentTag = needParentNode[tagName]) {
9443 var tmpParent = parent,hasParent;
9444 while(tmpParent.type != 'root'){
9445 if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){
9446 parent = tmpParent;
9447 hasParent = true;
9448 break;
9449 }
9450 tmpParent = tmpParent.parentNode;
9451 }
9452 if(!hasParent){
9453 parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
9454 }
9455 }
9456 //按dtd处理嵌套
9457// if(parent.type != 'root' && !dtd[parent.tagName][tagName])
9458// parent = parent.parentNode;
9459 var elm = new uNode({
9460 parentNode:parent,
9461 type:'element',
9462 tagName:tagName.toLowerCase(),
9463 //是自闭合的处理一下
9464 children:dtd.$empty[tagName] ? null : []
9465 });
9466 //如果属性存在,处理属性
9467 if (htmlattr) {
9468 var attrs = {}, match;
9469 while (match = re_attr.exec(htmlattr)) {
9470 attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
9471 }
9472 elm.attrs = attrs;
9473 }
9474 //trace:3970
9475// //如果parent下不能放elm
9476// if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){
9477// parent = parent.parentNode;
9478// elm.parentNode = parent;
9479// }
9480 parent.children.push(elm);
9481 //如果是自闭合节点返回父亲节点
9482 return dtd.$empty[tagName] ? parent : elm
9483 }
9484
9485 function comment(parent, data) {
9486 parent.children.push(new uNode({
9487 type:'comment',
9488 data:data,
9489 parentNode:parent
9490 }));
9491 }
9492
9493 var match, currentIndex = 0, nextIndex = 0;
9494 //设置根节点
9495 var root = new uNode({
9496 type:'root',
9497 children:[]
9498 });
9499 var currentParent = root;
9500
9501 while (match = re_tag.exec(htmlstr)) {
9502 currentIndex = match.index;
9503 try{
9504 if (currentIndex > nextIndex) {
9505 //text node
9506 text(currentParent, htmlstr.slice(nextIndex, currentIndex));
9507 }
9508 if (match[3]) {
9509
9510 if(dtd.$cdata[currentParent.tagName]){
9511 text(currentParent, match[0]);
9512 }else{
9513 //start tag
9514 currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
9515 }
9516
9517
9518 } else if (match[1]) {
9519 if(currentParent.type != 'root'){
9520 if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){
9521 text(currentParent, match[0]);
9522 }else{
9523 var tmpParent = currentParent;
9524 while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){
9525 currentParent = currentParent.parentNode;
9526 if(currentParent.type == 'root'){
9527 currentParent = tmpParent;
9528 throw 'break'
9529 }
9530 }
9531 //end tag
9532 currentParent = currentParent.parentNode;
9533 }
9534
9535 }
9536
9537 } else if (match[2]) {
9538 //comment
9539 comment(currentParent, match[2])
9540 }
9541 }catch(e){}
9542
9543 nextIndex = re_tag.lastIndex;
9544
9545 }
9546 //如果结束是文本,就有可能丢掉,所以这里手动判断一下
9547 //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
9548 if (nextIndex < htmlstr.length) {
9549 text(currentParent, htmlstr.slice(nextIndex));
9550 }
9551 return root;
9552};
9553
9554
9555// core/filternode.js
9556/**
9557 * UE过滤节点的静态方法
9558 * @file
9559 */
9560
9561/**
9562 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
9563 * @module UE
9564 */
9565
9566
9567/**
9568 * 根据传入节点和过滤规则过滤相应节点
9569 * @module UE
9570 * @since 1.2.6.1
9571 * @method filterNode
9572 * @param { Object } root 指定root节点
9573 * @param { Object } rules 过滤规则json对象
9574 * @example
9575 * ```javascript
9576 * UE.filterNode(root,editor.options.filterRules);
9577 * ```
9578 */
9579var filterNode = UE.filterNode = function () {
9580 function filterNode(node,rules){
9581 switch (node.type) {
9582 case 'text':
9583 break;
9584 case 'element':
9585 var val;
9586 if(val = rules[node.tagName]){
9587 if(val === '-'){
9588 node.parentNode.removeChild(node)
9589 }else if(utils.isFunction(val)){
9590 var parentNode = node.parentNode,
9591 index = node.getIndex();
9592 val(node);
9593 if(node.parentNode){
9594 if(node.children){
9595 for(var i = 0,ci;ci=node.children[i];){
9596 filterNode(ci,rules);
9597 if(ci.parentNode){
9598 i++;
9599 }
9600 }
9601 }
9602 }else{
9603 for(var i = index,ci;ci=parentNode.children[i];){
9604 filterNode(ci,rules);
9605 if(ci.parentNode){
9606 i++;
9607 }
9608 }
9609 }
9610
9611
9612 }else{
9613 var attrs = val['$'];
9614 if(attrs && node.attrs){
9615 var tmpAttrs = {},tmpVal;
9616 for(var a in attrs){
9617 tmpVal = node.getAttr(a);
9618 //todo 只先对style单独处理
9619 if(a == 'style' && utils.isArray(attrs[a])){
9620 var tmpCssStyle = [];
9621 utils.each(attrs[a],function(v){
9622 var tmp;
9623 if(tmp = node.getStyle(v)){
9624 tmpCssStyle.push(v + ':' + tmp);
9625 }
9626 });
9627 tmpVal = tmpCssStyle.join(';')
9628 }
9629 if(tmpVal){
9630 tmpAttrs[a] = tmpVal;
9631 }
9632
9633 }
9634 node.attrs = tmpAttrs;
9635 }
9636 if(node.children){
9637 for(var i = 0,ci;ci=node.children[i];){
9638 filterNode(ci,rules);
9639 if(ci.parentNode){
9640 i++;
9641 }
9642 }
9643 }
9644 }
9645 }else{
9646 //如果不在名单里扣出子节点并删除该节点,cdata除外
9647 if(dtd.$cdata[node.tagName]){
9648 node.parentNode.removeChild(node)
9649 }else{
9650 var parentNode = node.parentNode,
9651 index = node.getIndex();
9652 node.parentNode.removeChild(node,true);
9653 for(var i = index,ci;ci=parentNode.children[i];){
9654 filterNode(ci,rules);
9655 if(ci.parentNode){
9656 i++;
9657 }
9658 }
9659 }
9660 }
9661 break;
9662 case 'comment':
9663 node.parentNode.removeChild(node)
9664 }
9665
9666 }
9667 return function(root,rules){
9668 if(utils.isEmptyObject(rules)){
9669 return root;
9670 }
9671 var val;
9672 if(val = rules['-']){
9673 utils.each(val.split(' '),function(k){
9674 rules[k] = '-'
9675 })
9676 }
9677 for(var i= 0,ci;ci=root.children[i];){
9678 filterNode(ci,rules);
9679 if(ci.parentNode){
9680 i++;
9681 }
9682 }
9683 return root;
9684 }
9685}();
9686
9687// core/plugin.js
9688/**
9689 * Created with JetBrains PhpStorm.
9690 * User: campaign
9691 * Date: 10/8/13
9692 * Time: 6:15 PM
9693 * To change this template use File | Settings | File Templates.
9694 */
9695UE.plugin = function(){
9696 var _plugins = {};
9697 return {
9698 register : function(pluginName,fn,oldOptionName,afterDisabled){
9699 if(oldOptionName && utils.isFunction(oldOptionName)){
9700 afterDisabled = oldOptionName;
9701 oldOptionName = null
9702 }
9703 _plugins[pluginName] = {
9704 optionName : oldOptionName || pluginName,
9705 execFn : fn,
9706 //当插件被禁用时执行
9707 afterDisabled : afterDisabled
9708 }
9709 },
9710 load : function(editor){
9711 utils.each(_plugins,function(plugin){
9712 var _export = plugin.execFn.call(editor);
9713 if(editor.options[plugin.optionName] !== false){
9714 if(_export){
9715 //后边需要再做扩展
9716 utils.each(_export,function(v,k){
9717 switch(k.toLowerCase()){
9718 case 'shortcutkey':
9719 editor.addshortcutkey(v);
9720 break;
9721 case 'bindevents':
9722 utils.each(v,function(fn,eventName){
9723 editor.addListener(eventName,fn);
9724 });
9725 break;
9726 case 'bindmultievents':
9727 utils.each(utils.isArray(v) ? v:[v],function(event){
9728 var types = utils.trim(event.type).split(/\s+/);
9729 utils.each(types,function(eventName){
9730 editor.addListener(eventName, event.handler);
9731 });
9732 });
9733 break;
9734 case 'commands':
9735 utils.each(v,function(execFn,execName){
9736 editor.commands[execName] = execFn
9737 });
9738 break;
9739 case 'outputrule':
9740 editor.addOutputRule(v);
9741 break;
9742 case 'inputrule':
9743 editor.addInputRule(v);
9744 break;
9745 case 'defaultoptions':
9746 editor.setOpt(v)
9747 }
9748 })
9749 }
9750
9751 }else if(plugin.afterDisabled){
9752 plugin.afterDisabled.call(editor)
9753 }
9754
9755 });
9756 //向下兼容
9757 utils.each(UE.plugins,function(plugin){
9758 plugin.call(editor);
9759 });
9760 },
9761 run : function(pluginName,editor){
9762 var plugin = _plugins[pluginName];
9763 if(plugin){
9764 plugin.exeFn.call(editor)
9765 }
9766 }
9767 }
9768}();
9769
9770// core/keymap.js
9771var keymap = UE.keymap = {
9772 'Backspace' : 8,
9773 'Tab' : 9,
9774 'Enter' : 13,
9775
9776 'Shift':16,
9777 'Control':17,
9778 'Alt':18,
9779 'CapsLock':20,
9780
9781 'Esc':27,
9782
9783 'Spacebar':32,
9784
9785 'PageUp':33,
9786 'PageDown':34,
9787 'End':35,
9788 'Home':36,
9789
9790 'Left':37,
9791 'Up':38,
9792 'Right':39,
9793 'Down':40,
9794
9795 'Insert':45,
9796
9797 'Del':46,
9798
9799 'NumLock':144,
9800
9801 'Cmd':91,
9802
9803 '=':187,
9804 '-':189,
9805
9806 "b":66,
9807 'i':73,
9808 //回退
9809 'z':90,
9810 'y':89,
9811 //粘贴
9812 'v' : 86,
9813 'x' : 88,
9814
9815 's' : 83,
9816
9817 'n' : 78
9818};
9819
9820// core/localstorage.js
9821//存储媒介封装
9822var LocalStorage = UE.LocalStorage = (function () {
9823
9824 var storage = window.localStorage || getUserData() || null,
9825 LOCAL_FILE = 'localStorage';
9826
9827 return {
9828
9829 saveLocalData: function (key, data) {
9830
9831 if (storage && data) {
9832 storage.setItem(key, data);
9833 return true;
9834 }
9835
9836 return false;
9837
9838 },
9839
9840 getLocalData: function (key) {
9841
9842 if (storage) {
9843 return storage.getItem(key);
9844 }
9845
9846 return null;
9847
9848 },
9849
9850 removeItem: function (key) {
9851
9852 storage && storage.removeItem(key);
9853
9854 }
9855
9856 };
9857
9858 function getUserData() {
9859
9860 var container = document.createElement("div");
9861 container.style.display = "none";
9862
9863 if (!container.addBehavior) {
9864 return null;
9865 }
9866
9867 container.addBehavior("#default#userdata");
9868
9869 return {
9870
9871 getItem: function (key) {
9872
9873 var result = null;
9874
9875 try {
9876 document.body.appendChild(container);
9877 container.load(LOCAL_FILE);
9878 result = container.getAttribute(key);
9879 document.body.removeChild(container);
9880 } catch (e) {
9881 }
9882
9883 return result;
9884
9885 },
9886
9887 setItem: function (key, value) {
9888
9889 document.body.appendChild(container);
9890 container.setAttribute(key, value);
9891 container.save(LOCAL_FILE);
9892 document.body.removeChild(container);
9893
9894 },
9895
9896 //// 暂时没有用到
9897 //clear: function () {
9898 //
9899 // var expiresTime = new Date();
9900 // expiresTime.setFullYear(expiresTime.getFullYear() - 1);
9901 // document.body.appendChild(container);
9902 // container.expires = expiresTime.toUTCString();
9903 // container.save(LOCAL_FILE);
9904 // document.body.removeChild(container);
9905 //
9906 //},
9907
9908 removeItem: function (key) {
9909
9910 document.body.appendChild(container);
9911 container.removeAttribute(key);
9912 container.save(LOCAL_FILE);
9913 document.body.removeChild(container);
9914
9915 }
9916
9917 };
9918
9919 }
9920
9921})();
9922
9923(function () {
9924
9925 var ROOTKEY = 'ueditor_preference';
9926
9927 UE.Editor.prototype.setPreferences = function(key,value){
9928 var obj = {};
9929 if (utils.isString(key)) {
9930 obj[ key ] = value;
9931 } else {
9932 obj = key;
9933 }
9934 var data = LocalStorage.getLocalData(ROOTKEY);
9935 if (data && (data = utils.str2json(data))) {
9936 utils.extend(data, obj);
9937 } else {
9938 data = obj;
9939 }
9940 data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
9941 };
9942
9943 UE.Editor.prototype.getPreferences = function(key){
9944 var data = LocalStorage.getLocalData(ROOTKEY);
9945 if (data && (data = utils.str2json(data))) {
9946 return key ? data[key] : data
9947 }
9948 return null;
9949 };
9950
9951 UE.Editor.prototype.removePreferences = function (key) {
9952 var data = LocalStorage.getLocalData(ROOTKEY);
9953 if (data && (data = utils.str2json(data))) {
9954 data[key] = undefined;
9955 delete data[key]
9956 }
9957 data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
9958 };
9959
9960})();
9961
9962
9963// plugins/defaultfilter.js
9964///import core
9965///plugin 编辑器默认的过滤转换机制
9966
9967UE.plugins['defaultfilter'] = function () {
9968 var me = this;
9969 me.setOpt({
9970 'allowDivTransToP':true,
9971 'disabledTableInTable':true
9972 });
9973 //默认的过滤处理
9974 //进入编辑器的内容处理
9975 me.addInputRule(function (root) {
9976 var allowDivTransToP = this.options.allowDivTransToP;
9977 var val;
9978 function tdParent(node){
9979 while(node && node.type == 'element'){
9980 if(node.tagName == 'td'){
9981 return true;
9982 }
9983 node = node.parentNode;
9984 }
9985 return false;
9986 }
9987 //进行默认的处理
9988 root.traversal(function (node) {
9989 if (node.type == 'element') {
9990 if (!dtd.$cdata[node.tagName] && me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) {
9991 if (!node.firstChild()) node.parentNode.removeChild(node);
9992 else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
9993 node.parentNode.removeChild(node, true)
9994 }
9995 return;
9996 }
9997 switch (node.tagName) {
9998 case 'style':
9999 case 'script':
10000 node.setAttr({
10001 cdata_tag: node.tagName,
10002 cdata_data: (node.innerHTML() || ''),
10003 '_ue_custom_node_':'true'
10004 });
10005 node.tagName = 'div';
10006 node.innerHTML('');
10007 break;
10008 case 'a':
10009 if (val = node.getAttr('href')) {
10010 node.setAttr('_href', val)
10011 }
10012 break;
10013 case 'img':
10014 //todo base64暂时去掉,后边做远程图片上传后,干掉这个
10015 if (val = node.getAttr('src')) {
10016 if (/^data:/.test(val)) {
10017 node.parentNode.removeChild(node);
10018 break;
10019 }
10020 }
10021 node.setAttr('_src', node.getAttr('src'));
10022 break;
10023 case 'span':
10024 if (browser.webkit && (val = node.getStyle('white-space'))) {
10025 if (/nowrap|normal/.test(val)) {
10026 node.setStyle('white-space', '');
10027 if (me.options.autoClearEmptyNode && utils.isEmptyObject(node.attrs)) {
10028 node.parentNode.removeChild(node, true)
10029 }
10030 }
10031 }
10032 val = node.getAttr('id');
10033 if(val && /^_baidu_bookmark_/i.test(val)){
10034 node.parentNode.removeChild(node)
10035 }
10036 break;
10037 case 'p':
10038 if (val = node.getAttr('align')) {
10039 node.setAttr('align');
10040 node.setStyle('text-align', val)
10041 }
10042 //trace:3431
10043// var cssStyle = node.getAttr('style');
10044// if (cssStyle) {
10045// cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, '');
10046// node.setAttr('style', cssStyle)
10047//
10048// }
10049 //p标签不允许嵌套
10050 utils.each(node.children,function(n){
10051 if(n.type == 'element' && n.tagName == 'p'){
10052 var next = n.nextSibling();
10053 node.parentNode.insertAfter(n,node);
10054 var last = n;
10055 while(next){
10056 var tmp = next.nextSibling();
10057 node.parentNode.insertAfter(next,last);
10058 last = next;
10059 next = tmp;
10060 }
10061 return false;
10062 }
10063 });
10064 if (!node.firstChild()) {
10065 node.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
10066 }
10067 break;
10068 case 'div':
10069 if(node.getAttr('cdata_tag')){
10070 break;
10071 }
10072 //针对代码这里不处理插入代码的div
10073 val = node.getAttr('class');
10074 if(val && /^line number\d+/.test(val)){
10075 break;
10076 }
10077 if(!allowDivTransToP){
10078 break;
10079 }
10080 var tmpNode, p = UE.uNode.createElement('p');
10081 while (tmpNode = node.firstChild()) {
10082 if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
10083 p.appendChild(tmpNode);
10084 } else {
10085 if (p.firstChild()) {
10086 node.parentNode.insertBefore(p, node);
10087 p = UE.uNode.createElement('p');
10088 } else {
10089 node.parentNode.insertBefore(tmpNode, node);
10090 }
10091 }
10092 }
10093 if (p.firstChild()) {
10094 node.parentNode.insertBefore(p, node);
10095 }
10096 node.parentNode.removeChild(node);
10097 break;
10098 case 'dl':
10099 node.tagName = 'ul';
10100 break;
10101 case 'dt':
10102 case 'dd':
10103 node.tagName = 'li';
10104 break;
10105 case 'li':
10106 var className = node.getAttr('class');
10107 if (!className || !/list\-/.test(className)) {
10108 node.setAttr()
10109 }
10110 var tmpNodes = node.getNodesByTagName('ol ul');
10111 UE.utils.each(tmpNodes, function (n) {
10112 node.parentNode.insertAfter(n, node);
10113 });
10114 break;
10115 case 'td':
10116 case 'th':
10117 case 'caption':
10118 if(!node.children || !node.children.length){
10119 node.appendChild(browser.ie11below ? UE.uNode.createText(' ') : UE.uNode.createElement('br'))
10120 }
10121 break;
10122 case 'table':
10123 if(me.options.disabledTableInTable && tdParent(node)){
10124 node.parentNode.insertBefore(UE.uNode.createText(node.innerText()),node);
10125 node.parentNode.removeChild(node)
10126 }
10127 }
10128
10129 }
10130// if(node.type == 'comment'){
10131// node.parentNode.removeChild(node);
10132// }
10133 })
10134
10135 });
10136
10137 //从编辑器出去的内容处理
10138 me.addOutputRule(function (root) {
10139
10140 var val;
10141 root.traversal(function (node) {
10142 if (node.type == 'element') {
10143
10144 if (me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) {
10145
10146 if (!node.firstChild()) node.parentNode.removeChild(node);
10147 else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
10148 node.parentNode.removeChild(node, true)
10149 }
10150 return;
10151 }
10152 switch (node.tagName) {
10153 case 'div':
10154 if (val = node.getAttr('cdata_tag')) {
10155 node.tagName = val;
10156 node.appendChild(UE.uNode.createText(node.getAttr('cdata_data')));
10157 node.setAttr({cdata_tag: '', cdata_data: '','_ue_custom_node_':''});
10158 }
10159 break;
10160 case 'a':
10161 if (val = node.getAttr('_href')) {
10162 node.setAttr({
10163 'href': utils.html(val),
10164 '_href': ''
10165 })
10166 }
10167 break;
10168 break;
10169 case 'span':
10170 val = node.getAttr('id');
10171 if(val && /^_baidu_bookmark_/i.test(val)){
10172 node.parentNode.removeChild(node)
10173 }
10174 break;
10175 case 'img':
10176 if (val = node.getAttr('_src')) {
10177 node.setAttr({
10178 'src': node.getAttr('_src'),
10179 '_src': ''
10180 })
10181 }
10182
10183
10184 }
10185 }
10186
10187 })
10188
10189
10190 });
10191};
10192
10193
10194// plugins/inserthtml.js
10195/**
10196 * 插入html字符串插件
10197 * @file
10198 * @since 1.2.6.1
10199 */
10200
10201/**
10202 * 插入html代码
10203 * @command inserthtml
10204 * @method execCommand
10205 * @param { String } cmd 命令字符串
10206 * @param { String } html 插入的html字符串
10207 * @remaind 插入的标签内容是在当前的选区位置上插入,如果当前是闭合状态,那直接插入内容, 如果当前是选中状态,将先清除当前选中内容后,再做插入
10208 * @warning 注意:该命令会对当前选区的位置,对插入的内容进行过滤转换处理。 过滤的规则遵循html语意化的原则。
10209 * @example
10210 * ```javascript
10211 * //xxx[BB]xxx 当前选区为非闭合选区,选中BB这两个文本
10212 * //执行命令,插入<b>CC</b>
10213 * //插入后的效果 xxx<b>CC</b>xxx
10214 * //<p>xx|xxx</p> 当前选区为闭合状态
10215 * //插入<p>CC</p>
10216 * //结果 <p>xx</p><p>CC</p><p>xxx</p>
10217 * //<p>xxxx</p>|</p>xxx</p> 当前选区在两个p标签之间
10218 * //插入 xxxx
10219 * //结果 <p>xxxx</p><p>xxxx</p></p>xxx</p>
10220 * ```
10221 */
10222
10223UE.commands['inserthtml'] = {
10224 execCommand: function (command,html,notNeedFilter){
10225 var me = this,
10226 range,
10227 div;
10228 if(!html){
10229 return;
10230 }
10231 if(me.fireEvent('beforeinserthtml',html) === true){
10232 return;
10233 }
10234 range = me.selection.getRange();
10235 div = range.document.createElement( 'div' );
10236 div.style.display = 'inline';
10237
10238 if (!notNeedFilter) {
10239 var root = UE.htmlparser(html);
10240 //如果给了过滤规则就先进行过滤
10241 if(me.options.filterRules){
10242 UE.filterNode(root,me.options.filterRules);
10243 }
10244 //执行默认的处理
10245 me.filterInputRule(root);
10246 html = root.toHtml()
10247 }
10248 div.innerHTML = utils.trim( html );
10249
10250 if ( !range.collapsed ) {
10251 var tmpNode = range.startContainer;
10252 if(domUtils.isFillChar(tmpNode)){
10253 range.setStartBefore(tmpNode)
10254 }
10255 tmpNode = range.endContainer;
10256 if(domUtils.isFillChar(tmpNode)){
10257 range.setEndAfter(tmpNode)
10258 }
10259 range.txtToElmBoundary();
10260 //结束边界可能放到了br的前边,要把br包含进来
10261 // x[xxx]<br/>
10262 if(range.endContainer && range.endContainer.nodeType == 1){
10263 tmpNode = range.endContainer.childNodes[range.endOffset];
10264 if(tmpNode && domUtils.isBr(tmpNode)){
10265 range.setEndAfter(tmpNode);
10266 }
10267 }
10268 if(range.startOffset == 0){
10269 tmpNode = range.startContainer;
10270 if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
10271 tmpNode = range.endContainer;
10272 if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
10273 me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
10274 range.setStart(me.body.firstChild,0).collapse(true)
10275
10276 }
10277 }
10278 }
10279 !range.collapsed && range.deleteContents();
10280 if(range.startContainer.nodeType == 1){
10281 var child = range.startContainer.childNodes[range.startOffset],pre;
10282 if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){
10283 range.setEnd(pre,pre.childNodes.length).collapse();
10284 while(child.firstChild){
10285 pre.appendChild(child.firstChild);
10286 }
10287 domUtils.remove(child);
10288 }
10289 }
10290
10291 }
10292
10293
10294 var child,parent,pre,tmp,hadBreak = 0, nextNode;
10295 //如果当前位置选中了fillchar要干掉,要不会产生空行
10296 if(range.inFillChar()){
10297 child = range.startContainer;
10298 if(domUtils.isFillChar(child)){
10299 range.setStartBefore(child).collapse(true);
10300 domUtils.remove(child);
10301 }else if(domUtils.isFillChar(child,true)){
10302 child.nodeValue = child.nodeValue.replace(fillCharReg,'');
10303 range.startOffset--;
10304 range.collapsed && range.collapse(true)
10305 }
10306 }
10307 //列表单独处理
10308 var li = domUtils.findParentByTagName(range.startContainer,'li',true);
10309 if(li){
10310 var next,last;
10311 while(child = div.firstChild){
10312 //针对hr单独处理一下先
10313 while(child && (child.nodeType == 3 || !domUtils.isBlockElm(child) || child.tagName=='HR' )){
10314 next = child.nextSibling;
10315 range.insertNode( child).collapse();
10316 last = child;
10317 child = next;
10318
10319 }
10320 if(child){
10321 if(/^(ol|ul)$/i.test(child.tagName)){
10322 while(child.firstChild){
10323 last = child.firstChild;
10324 domUtils.insertAfter(li,child.firstChild);
10325 li = li.nextSibling;
10326 }
10327 domUtils.remove(child)
10328 }else{
10329 var tmpLi;
10330 next = child.nextSibling;
10331 tmpLi = me.document.createElement('li');
10332 domUtils.insertAfter(li,tmpLi);
10333 tmpLi.appendChild(child);
10334 last = child;
10335 child = next;
10336 li = tmpLi;
10337 }
10338 }
10339 }
10340 li = domUtils.findParentByTagName(range.startContainer,'li',true);
10341 if(domUtils.isEmptyBlock(li)){
10342 domUtils.remove(li)
10343 }
10344 if(last){
10345
10346 range.setStartAfter(last).collapse(true).select(true)
10347 }
10348 }else{
10349 while ( child = div.firstChild ) {
10350 if(hadBreak){
10351 var p = me.document.createElement('p');
10352 while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){
10353 nextNode = child.nextSibling;
10354 p.appendChild(child);
10355 child = nextNode;
10356 }
10357 if(p.firstChild){
10358
10359 child = p
10360 }
10361 }
10362 range.insertNode( child );
10363 nextNode = child.nextSibling;
10364 if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){
10365
10366 parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } );
10367 if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){
10368 if(!dtd[parent.tagName][child.nodeName]){
10369 pre = parent;
10370 }else{
10371 tmp = child.parentNode;
10372 while (tmp !== parent){
10373 pre = tmp;
10374 tmp = tmp.parentNode;
10375
10376 }
10377 }
10378
10379
10380 domUtils.breakParent( child, pre || tmp );
10381 //去掉break后前一个多余的节点 <p>|<[p> ==> <p></p><div></div><p>|</p>
10382 var pre = child.previousSibling;
10383 domUtils.trimWhiteTextNode(pre);
10384 if(!pre.childNodes.length){
10385 domUtils.remove(pre);
10386 }
10387 //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位
10388
10389 if(!browser.ie &&
10390 (next = child.nextSibling) &&
10391 domUtils.isBlockElm(next) &&
10392 next.lastChild &&
10393 !domUtils.isBr(next.lastChild)){
10394 next.appendChild(me.document.createElement('br'));
10395 }
10396 hadBreak = 1;
10397 }
10398 }
10399 var next = child.nextSibling;
10400 if(!div.firstChild && next && domUtils.isBlockElm(next)){
10401
10402 range.setStart(next,0).collapse(true);
10403 break;
10404 }
10405 range.setEndAfter( child ).collapse();
10406
10407 }
10408
10409 child = range.startContainer;
10410
10411 if(nextNode && domUtils.isBr(nextNode)){
10412 domUtils.remove(nextNode)
10413 }
10414 //用chrome可能有空白展位符
10415 if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){
10416 if(nextNode = child.nextSibling){
10417 domUtils.remove(child);
10418 if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){
10419
10420 range.setStart(nextNode,0).collapse(true).shrinkBoundary()
10421 }
10422 }else{
10423
10424 try{
10425 child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
10426 }catch(e){
10427 range.setStartBefore(child);
10428 domUtils.remove(child)
10429 }
10430
10431 }
10432
10433 }
10434 //加上true因为在删除表情等时会删两次,第一次是删的fillData
10435 try{
10436 range.select(true);
10437 }catch(e){}
10438
10439 }
10440
10441
10442
10443 setTimeout(function(){
10444 range = me.selection.getRange();
10445 range.scrollToView(me.autoHeightEnabled,me.autoHeightEnabled ? domUtils.getXY(me.iframe).y:0);
10446 me.fireEvent('afterinserthtml', html);
10447 },200);
10448 }
10449};
10450
10451
10452// plugins/autotypeset.js
10453/**
10454 * 自动排版
10455 * @file
10456 * @since 1.2.6.1
10457 */
10458
10459/**
10460 * 对当前编辑器的内容执行自动排版, 排版的行为根据config配置文件里的“autotypeset”选项进行控制。
10461 * @command autotypeset
10462 * @method execCommand
10463 * @param { String } cmd 命令字符串
10464 * @example
10465 * ```javascript
10466 * editor.execCommand( 'autotypeset' );
10467 * ```
10468 */
10469
10470UE.plugins['autotypeset'] = function(){
10471
10472 this.setOpt({'autotypeset': {
10473 mergeEmptyline: true, //合并空行
10474 removeClass: true, //去掉冗余的class
10475 removeEmptyline: false, //去掉空行
10476 textAlign:"left", //段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版
10477 imageBlockLine: 'center', //图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版
10478 pasteFilter: false, //根据规则过滤没事粘贴进来的内容
10479 clearFontSize: false, //去掉所有的内嵌字号,使用编辑器默认的字号
10480 clearFontFamily: false, //去掉所有的内嵌字体,使用编辑器默认的字体
10481 removeEmptyNode: false, // 去掉空节点
10482 //可以去掉的标签
10483 removeTagNames: utils.extend({div:1},dtd.$removeEmpty),
10484 indent: false, // 行首缩进
10485 indentValue : '2em', //行首缩进的大小
10486 bdc2sb: false,
10487 tobdc: false
10488 }});
10489
10490 var me = this,
10491 opt = me.options.autotypeset,
10492 remainClass = {
10493 'selectTdClass':1,
10494 'pagebreak':1,
10495 'anchorclass':1
10496 },
10497 remainTag = {
10498 'li':1
10499 },
10500 tags = {
10501 div:1,
10502 p:1,
10503 //trace:2183 这些也认为是行
10504 blockquote:1,center:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,
10505 span:1
10506 },
10507 highlightCont;
10508 //升级了版本,但配置项目里没有autotypeset
10509 if(!opt){
10510 return;
10511 }
10512
10513 readLocalOpts();
10514
10515 function isLine(node,notEmpty){
10516 if(!node || node.nodeType == 3)
10517 return 0;
10518 if(domUtils.isBr(node))
10519 return 1;
10520 if(node && node.parentNode && tags[node.tagName.toLowerCase()]){
10521 if(highlightCont && highlightCont.contains(node)
10522 ||
10523 node.getAttribute('pagebreak')
10524 ){
10525 return 0;
10526 }
10527
10528 return notEmpty ? !domUtils.isEmptyBlock(node) : domUtils.isEmptyBlock(node,new RegExp('[\\s'+domUtils.fillChar
10529 +']','g'));
10530 }
10531 }
10532
10533 function removeNotAttributeSpan(node){
10534 if(!node.style.cssText){
10535 domUtils.removeAttributes(node,['style']);
10536 if(node.tagName.toLowerCase() == 'span' && domUtils.hasNoAttributes(node)){
10537 domUtils.remove(node,true);
10538 }
10539 }
10540 }
10541 function autotype(type,html){
10542
10543 var me = this,cont;
10544 if(html){
10545 if(!opt.pasteFilter){
10546 return;
10547 }
10548 cont = me.document.createElement('div');
10549 cont.innerHTML = html.html;
10550 }else{
10551 cont = me.document.body;
10552 }
10553 var nodes = domUtils.getElementsByTagName(cont,'*');
10554
10555 // 行首缩进,段落方向,段间距,段内间距
10556 for(var i=0,ci;ci=nodes[i++];){
10557
10558 if(me.fireEvent('excludeNodeinautotype',ci) === true){
10559 continue;
10560 }
10561 //font-size
10562 if(opt.clearFontSize && ci.style.fontSize){
10563 domUtils.removeStyle(ci,'font-size');
10564
10565 removeNotAttributeSpan(ci);
10566
10567 }
10568 //font-family
10569 if(opt.clearFontFamily && ci.style.fontFamily){
10570 domUtils.removeStyle(ci,'font-family');
10571 removeNotAttributeSpan(ci);
10572 }
10573
10574 if(isLine(ci)){
10575 //合并空行
10576 if(opt.mergeEmptyline ){
10577 var next = ci.nextSibling,tmpNode,isBr = domUtils.isBr(ci);
10578 while(isLine(next)){
10579 tmpNode = next;
10580 next = tmpNode.nextSibling;
10581 if(isBr && (!next || next && !domUtils.isBr(next))){
10582 break;
10583 }
10584 domUtils.remove(tmpNode);
10585 }
10586
10587 }
10588 //去掉空行,保留占位的空行
10589 if(opt.removeEmptyline && domUtils.inDoc(ci,cont) && !remainTag[ci.parentNode.tagName.toLowerCase()] ){
10590 if(domUtils.isBr(ci)){
10591 next = ci.nextSibling;
10592 if(next && !domUtils.isBr(next)){
10593 continue;
10594 }
10595 }
10596 domUtils.remove(ci);
10597 continue;
10598
10599 }
10600
10601 }
10602 if(isLine(ci,true) && ci.tagName != 'SPAN'){
10603 if(opt.indent){
10604 ci.style.textIndent = opt.indentValue;
10605 }
10606 if(opt.textAlign){
10607 ci.style.textAlign = opt.textAlign;
10608 }
10609 // if(opt.lineHeight)
10610 // ci.style.lineHeight = opt.lineHeight + 'cm';
10611
10612 }
10613
10614 //去掉class,保留的class不去掉
10615 if(opt.removeClass && ci.className && !remainClass[ci.className.toLowerCase()]){
10616
10617 if(highlightCont && highlightCont.contains(ci)){
10618 continue;
10619 }
10620 domUtils.removeAttributes(ci,['class']);
10621 }
10622
10623 //表情不处理
10624 if(opt.imageBlockLine && ci.tagName.toLowerCase() == 'img' && !ci.getAttribute('emotion')){
10625 if(html){
10626 var img = ci;
10627 switch (opt.imageBlockLine){
10628 case 'left':
10629 case 'right':
10630 case 'none':
10631 var pN = img.parentNode,tmpNode,pre,next;
10632 while(dtd.$inline[pN.tagName] || pN.tagName == 'A'){
10633 pN = pN.parentNode;
10634 }
10635 tmpNode = pN;
10636 if(tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode,'text-align') == 'center'){
10637 if(!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode,function(node){return !domUtils.isBr(node) && !domUtils.isWhitespace(node)}) == 1){
10638 pre = tmpNode.previousSibling;
10639 next = tmpNode.nextSibling;
10640 if(pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)){
10641 pre.appendChild(tmpNode.firstChild);
10642 while(next.firstChild){
10643 pre.appendChild(next.firstChild);
10644 }
10645 domUtils.remove(tmpNode);
10646 domUtils.remove(next);
10647 }else{
10648 domUtils.setStyle(tmpNode,'text-align','');
10649 }
10650
10651
10652 }
10653
10654
10655 }
10656 domUtils.setStyle(img,'float', opt.imageBlockLine);
10657 break;
10658 case 'center':
10659 if(me.queryCommandValue('imagefloat') != 'center'){
10660 pN = img.parentNode;
10661 domUtils.setStyle(img,'float','none');
10662 tmpNode = img;
10663 while(pN && domUtils.getChildCount(pN,function(node){return !domUtils.isBr(node) && !domUtils.isWhitespace(node)}) == 1
10664 && (dtd.$inline[pN.tagName] || pN.tagName == 'A')){
10665 tmpNode = pN;
10666 pN = pN.parentNode;
10667 }
10668 var pNode = me.document.createElement('p');
10669 domUtils.setAttributes(pNode,{
10670
10671 style:'text-align:center'
10672 });
10673 tmpNode.parentNode.insertBefore(pNode,tmpNode);
10674 pNode.appendChild(tmpNode);
10675 domUtils.setStyle(tmpNode,'float','');
10676
10677 }
10678
10679
10680 }
10681 } else {
10682 var range = me.selection.getRange();
10683 range.selectNode(ci).select();
10684 me.execCommand('imagefloat', opt.imageBlockLine);
10685 }
10686
10687 }
10688
10689 //去掉冗余的标签
10690 if(opt.removeEmptyNode){
10691 if(opt.removeTagNames[ci.tagName.toLowerCase()] && domUtils.hasNoAttributes(ci) && domUtils.isEmptyBlock(ci)){
10692 domUtils.remove(ci);
10693 }
10694 }
10695 }
10696 if(opt.tobdc){
10697 var root = UE.htmlparser(cont.innerHTML);
10698 root.traversal(function(node){
10699 if(node.type == 'text'){
10700 node.data = ToDBC(node.data)
10701 }
10702 });
10703 cont.innerHTML = root.toHtml()
10704 }
10705 if(opt.bdc2sb){
10706 var root = UE.htmlparser(cont.innerHTML);
10707 root.traversal(function(node){
10708 if(node.type == 'text'){
10709 node.data = DBC2SB(node.data)
10710 }
10711 });
10712 cont.innerHTML = root.toHtml()
10713 }
10714 if(html){
10715 html.html = cont.innerHTML;
10716 }
10717 }
10718 if(opt.pasteFilter){
10719 me.addListener('beforepaste',autotype);
10720 }
10721
10722 function DBC2SB(str) {
10723 var result = '';
10724 for (var i = 0; i < str.length; i++) {
10725 var code = str.charCodeAt(i); //获取当前字符的unicode编码
10726 if (code >= 65281 && code <= 65373)//在这个unicode编码范围中的是所有的英文字母已经各种字符
10727 {
10728 result += String.fromCharCode(str.charCodeAt(i) - 65248); //把全角字符的unicode编码转换为对应半角字符的unicode码
10729 } else if (code == 12288)//空格
10730 {
10731 result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32);
10732 } else {
10733 result += str.charAt(i);
10734 }
10735 }
10736 return result;
10737 }
10738 function ToDBC(txtstring) {
10739 txtstring = utils.html(txtstring);
10740 var tmp = "";
10741 var mark = "";/*用于判断,如果是html尖括里的标记,则不进行全角的转换*/
10742 for (var i = 0; i < txtstring.length; i++) {
10743 if (txtstring.charCodeAt(i) == 32) {
10744 tmp = tmp + String.fromCharCode(12288);
10745 }
10746 else if (txtstring.charCodeAt(i) < 127) {
10747 tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248);
10748 }
10749 else {
10750 tmp += txtstring.charAt(i);
10751 }
10752 }
10753 return tmp;
10754 }
10755
10756 function readLocalOpts() {
10757 var cookieOpt = me.getPreferences('autotypeset');
10758 utils.extend(me.options.autotypeset, cookieOpt);
10759 }
10760
10761 me.commands['autotypeset'] = {
10762 execCommand:function () {
10763 me.removeListener('beforepaste',autotype);
10764 if(opt.pasteFilter){
10765 me.addListener('beforepaste',autotype);
10766 }
10767 autotype.call(me)
10768 }
10769
10770 };
10771
10772};
10773
10774
10775
10776// plugins/autosubmit.js
10777/**
10778 * 快捷键提交
10779 * @file
10780 * @since 1.2.6.1
10781 */
10782
10783/**
10784 * 提交表单
10785 * @command autosubmit
10786 * @method execCommand
10787 * @param { String } cmd 命令字符串
10788 * @example
10789 * ```javascript
10790 * editor.execCommand( 'autosubmit' );
10791 * ```
10792 */
10793
10794UE.plugin.register('autosubmit',function(){
10795 return {
10796 shortcutkey:{
10797 "autosubmit":"ctrl+13" //手动提交
10798 },
10799 commands:{
10800 'autosubmit':{
10801 execCommand:function () {
10802 var me=this,
10803 form = domUtils.findParentByTagName(me.iframe,"form", false);
10804 if (form){
10805 if(me.fireEvent("beforesubmit")===false){
10806 return;
10807 }
10808 me.sync();
10809 form.submit();
10810 }
10811 }
10812 }
10813 }
10814 }
10815});
10816
10817// plugins/background.js
10818/**
10819 * 背景插件,为UEditor提供设置背景功能
10820 * @file
10821 * @since 1.2.6.1
10822 */
10823UE.plugin.register('background', function () {
10824 var me = this,
10825 cssRuleId = 'editor_background',
10826 isSetColored,
10827 reg = new RegExp('body[\\s]*\\{(.+)\\}', 'i');
10828
10829 function stringToObj(str) {
10830 var obj = {}, styles = str.split(';');
10831 utils.each(styles, function (v) {
10832 var index = v.indexOf(':'),
10833 key = utils.trim(v.substr(0, index)).toLowerCase();
10834 key && (obj[key] = utils.trim(v.substr(index + 1) || ''));
10835 });
10836 return obj;
10837 }
10838
10839 function setBackground(obj) {
10840 if (obj) {
10841 var styles = [];
10842 for (var name in obj) {
10843 if (obj.hasOwnProperty(name)) {
10844 styles.push(name + ":" + obj[name] + '; ');
10845 }
10846 }
10847 utils.cssRule(cssRuleId, styles.length ? ('body{' + styles.join("") + '}') : '', me.document);
10848 } else {
10849 utils.cssRule(cssRuleId, '', me.document)
10850 }
10851 }
10852 //重写editor.hasContent方法
10853
10854 var orgFn = me.hasContents;
10855 me.hasContents = function(){
10856 if(me.queryCommandValue('background')){
10857 return true
10858 }
10859 return orgFn.apply(me,arguments);
10860 };
10861 return {
10862 bindEvents: {
10863 'getAllHtml': function (type, headHtml) {
10864 var body = this.body,
10865 su = domUtils.getComputedStyle(body, "background-image"),
10866 url = "";
10867 if (su.indexOf(me.options.imagePath) > 0) {
10868 url = su.substring(su.indexOf(me.options.imagePath), su.length - 1).replace(/"|\(|\)/ig, "");
10869 } else {
10870 url = su != "none" ? su.replace(/url\("?|"?\)/ig, "") : "";
10871 }
10872 var html = '<style type="text/css">body{';
10873 var bgObj = {
10874 "background-color": domUtils.getComputedStyle(body, "background-color") || "#ffffff",
10875 'background-image': url ? 'url(' + url + ')' : '',
10876 'background-repeat': domUtils.getComputedStyle(body, "background-repeat") || "",
10877 'background-position': browser.ie ? (domUtils.getComputedStyle(body, "background-position-x") + " " + domUtils.getComputedStyle(body, "background-position-y")) : domUtils.getComputedStyle(body, "background-position"),
10878 'height': domUtils.getComputedStyle(body, "height")
10879 };
10880 for (var name in bgObj) {
10881 if (bgObj.hasOwnProperty(name)) {
10882 html += name + ":" + bgObj[name] + "; ";
10883 }
10884 }
10885 html += '}</style> ';
10886 headHtml.push(html);
10887 },
10888 'aftersetcontent': function () {
10889 if(isSetColored == false) setBackground();
10890 }
10891 },
10892 inputRule: function (root) {
10893 isSetColored = false;
10894 utils.each(root.getNodesByTagName('p'), function (p) {
10895 var styles = p.getAttr('data-background');
10896 if (styles) {
10897 isSetColored = true;
10898 setBackground(stringToObj(styles));
10899 p.parentNode.removeChild(p);
10900 }
10901 })
10902 },
10903 outputRule: function (root) {
10904 var me = this,
10905 styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg);
10906 if (styles) {
10907 root.appendChild(UE.uNode.createElement('<p style="display:none;" data-background="' + utils.trim(styles[1].replace(/"/g, '').replace(/[\s]+/g, ' ')) + '"><br/></p>'));
10908 }
10909 },
10910 commands: {
10911 'background': {
10912 execCommand: function (cmd, obj) {
10913 setBackground(obj);
10914 },
10915 queryCommandValue: function () {
10916 var me = this,
10917 styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg);
10918 return styles ? stringToObj(styles[1]) : null;
10919 },
10920 notNeedUndo: true
10921 }
10922 }
10923 }
10924});
10925
10926// plugins/image.js
10927/**
10928 * 图片插入、排版插件
10929 * @file
10930 * @since 1.2.6.1
10931 */
10932
10933/**
10934 * 图片对齐方式
10935 * @command imagefloat
10936 * @method execCommand
10937 * @remind 值center为独占一行居中
10938 * @param { String } cmd 命令字符串
10939 * @param { String } align 对齐方式,可传left、right、none、center
10940 * @remaind center表示图片独占一行
10941 * @example
10942 * ```javascript
10943 * editor.execCommand( 'imagefloat', 'center' );
10944 * ```
10945 */
10946
10947/**
10948 * 如果选区所在位置是图片区域
10949 * @command imagefloat
10950 * @method queryCommandValue
10951 * @param { String } cmd 命令字符串
10952 * @return { String } 返回图片对齐方式
10953 * @example
10954 * ```javascript
10955 * editor.queryCommandValue( 'imagefloat' );
10956 * ```
10957 */
10958
10959UE.commands['imagefloat'] = {
10960 execCommand:function (cmd, align) {
10961 var me = this,
10962 range = me.selection.getRange();
10963 if (!range.collapsed) {
10964 var img = range.getClosedNode();
10965 if (img && img.tagName == 'IMG') {
10966 switch (align) {
10967 case 'left':
10968 case 'right':
10969 case 'none':
10970 var pN = img.parentNode, tmpNode, pre, next;
10971 while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
10972 pN = pN.parentNode;
10973 }
10974 tmpNode = pN;
10975 if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') {
10976 if (!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode, function (node) {
10977 return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
10978 }) == 1) {
10979 pre = tmpNode.previousSibling;
10980 next = tmpNode.nextSibling;
10981 if (pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)) {
10982 pre.appendChild(tmpNode.firstChild);
10983 while (next.firstChild) {
10984 pre.appendChild(next.firstChild);
10985 }
10986 domUtils.remove(tmpNode);
10987 domUtils.remove(next);
10988 } else {
10989 domUtils.setStyle(tmpNode, 'text-align', '');
10990 }
10991
10992
10993 }
10994
10995 range.selectNode(img).select();
10996 }
10997 domUtils.setStyle(img, 'float', align == 'none' ? '' : align);
10998 if(align == 'none'){
10999 domUtils.removeAttributes(img,'align');
11000 }
11001
11002 break;
11003 case 'center':
11004 if (me.queryCommandValue('imagefloat') != 'center') {
11005 pN = img.parentNode;
11006 domUtils.setStyle(img, 'float', '');
11007 domUtils.removeAttributes(img,'align');
11008 tmpNode = img;
11009 while (pN && domUtils.getChildCount(pN, function (node) {
11010 return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
11011 }) == 1
11012 && (dtd.$inline[pN.tagName] || pN.tagName == 'A')) {
11013 tmpNode = pN;
11014 pN = pN.parentNode;
11015 }
11016 range.setStartBefore(tmpNode).setCursor(false);
11017 pN = me.document.createElement('div');
11018 pN.appendChild(tmpNode);
11019 domUtils.setStyle(tmpNode, 'float', '');
11020
11021 me.execCommand('insertHtml', '<p id="_img_parent_tmp" style="text-align:center">' + pN.innerHTML + '</p>');
11022
11023 tmpNode = me.document.getElementById('_img_parent_tmp');
11024 tmpNode.removeAttribute('id');
11025 tmpNode = tmpNode.firstChild;
11026 range.selectNode(tmpNode).select();
11027 //去掉后边多余的元素
11028 next = tmpNode.parentNode.nextSibling;
11029 if (next && domUtils.isEmptyNode(next)) {
11030 domUtils.remove(next);
11031 }
11032
11033 }
11034
11035 break;
11036 }
11037
11038 }
11039 }
11040 },
11041 queryCommandValue:function () {
11042 var range = this.selection.getRange(),
11043 startNode, floatStyle;
11044 if (range.collapsed) {
11045 return 'none';
11046 }
11047 startNode = range.getClosedNode();
11048 if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
11049 floatStyle = domUtils.getComputedStyle(startNode, 'float') || startNode.getAttribute('align');
11050
11051 if (floatStyle == 'none') {
11052 floatStyle = domUtils.getComputedStyle(startNode.parentNode, 'text-align') == 'center' ? 'center' : floatStyle;
11053 }
11054 return {
11055 left:1,
11056 right:1,
11057 center:1
11058 }[floatStyle] ? floatStyle : 'none';
11059 }
11060 return 'none';
11061
11062
11063 },
11064 queryCommandState:function () {
11065 var range = this.selection.getRange(),
11066 startNode;
11067
11068 if (range.collapsed) return -1;
11069
11070 startNode = range.getClosedNode();
11071 if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
11072 return 0;
11073 }
11074 return -1;
11075 }
11076};
11077
11078
11079/**
11080 * 插入图片
11081 * @command insertimage
11082 * @method execCommand
11083 * @param { String } cmd 命令字符串
11084 * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片
11085 * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片,
11086 * 此时数组的每一个元素都是一个Object类型的图片属性集合。
11087 * @example
11088 * ```javascript
11089 * editor.execCommand( 'insertimage', {
11090 * src:'a/b/c.jpg',
11091 * width:'100',
11092 * height:'100'
11093 * } );
11094 * ```
11095 * @example
11096 * ```javascript
11097 * editor.execCommand( 'insertimage', [{
11098 * src:'a/b/c.jpg',
11099 * width:'100',
11100 * height:'100'
11101 * },{
11102 * src:'a/b/d.jpg',
11103 * width:'100',
11104 * height:'100'
11105 * }] );
11106 * ```
11107 */
11108
11109UE.commands['insertimage'] = {
11110 execCommand:function (cmd, opt) {
11111
11112 opt = utils.isArray(opt) ? opt : [opt];
11113 if (!opt.length) {
11114 return;
11115 }
11116 var me = this,
11117 range = me.selection.getRange(),
11118 img = range.getClosedNode();
11119
11120 if(me.fireEvent('beforeinsertimage', opt) === true){
11121 return;
11122 }
11123
11124 function unhtmlData(imgCi) {
11125
11126 utils.each('width,height,border,hspace,vspace'.split(','), function (item) {
11127
11128 if (imgCi[item]) {
11129 imgCi[item] = parseInt(imgCi[item], 10) || 0;
11130 }
11131 });
11132
11133 utils.each('src,_src'.split(','), function (item) {
11134
11135 if (imgCi[item]) {
11136 imgCi[item] = utils.unhtmlForUrl(imgCi[item]);
11137 }
11138 });
11139 utils.each('title,alt'.split(','), function (item) {
11140
11141 if (imgCi[item]) {
11142 imgCi[item] = utils.unhtml(imgCi[item]);
11143 }
11144 });
11145 }
11146
11147 if (img && /img/i.test(img.tagName) && (img.className != "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1) && !img.getAttribute("word_img")) {
11148 var first = opt.shift();
11149 var floatStyle = first['floatStyle'];
11150 delete first['floatStyle'];
11151//// img.style.border = (first.border||0) +"px solid #000";
11152//// img.style.margin = (first.margin||0) +"px";
11153// img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000";
11154 domUtils.setAttributes(img, first);
11155 me.execCommand('imagefloat', floatStyle);
11156 if (opt.length > 0) {
11157 range.setStartAfter(img).setCursor(false, true);
11158 me.execCommand('insertimage', opt);
11159 }
11160
11161 } else {
11162 var html = [], str = '', ci;
11163 ci = opt[0];
11164 if (opt.length == 1) {
11165 unhtmlData(ci);
11166
11167 str = '<img src="' + ci.src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') +
11168 (ci.width ? 'width="' + ci.width + '" ' : '') +
11169 (ci.height ? ' height="' + ci.height + '" ' : '') +
11170 (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') +
11171 (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') +
11172 (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') +
11173 (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') +
11174 (ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') +
11175 (ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>';
11176 if (ci['floatStyle'] == 'center') {
11177 str = '<p style="text-align: center">' + str + '</p>';
11178 }
11179 html.push(str);
11180
11181 } else {
11182 for (var i = 0; ci = opt[i++];) {
11183 unhtmlData(ci);
11184 str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + ci.src + '" ' +
11185 (ci.width ? 'width="' + ci.width + '" ' : '') + (ci._src ? ' _src="' + ci._src + '" ' : '') +
11186 (ci.height ? ' height="' + ci.height + '" ' : '') +
11187 ' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
11188 (ci.border || '') + '" ' +
11189 (ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>';
11190 html.push(str);
11191 }
11192 }
11193
11194 me.execCommand('insertHtml', html.join(''));
11195 }
11196
11197 me.fireEvent('afterinsertimage', opt)
11198 }
11199};
11200
11201
11202// plugins/justify.js
11203/**
11204 * 段落格式
11205 * @file
11206 * @since 1.2.6.1
11207 */
11208
11209/**
11210 * 段落对齐方式
11211 * @command justify
11212 * @method execCommand
11213 * @param { String } cmd 命令字符串
11214 * @param { String } align 对齐方式:left => 居左,right => 居右,center => 居中,justify => 两端对齐
11215 * @example
11216 * ```javascript
11217 * editor.execCommand( 'justify', 'center' );
11218 * ```
11219 */
11220/**
11221 * 如果选区所在位置是段落区域,返回当前段落对齐方式
11222 * @command justify
11223 * @method queryCommandValue
11224 * @param { String } cmd 命令字符串
11225 * @return { String } 返回段落对齐方式
11226 * @example
11227 * ```javascript
11228 * editor.queryCommandValue( 'justify' );
11229 * ```
11230 */
11231
11232UE.plugins['justify']=function(){
11233 var me=this,
11234 block = domUtils.isBlockElm,
11235 defaultValue = {
11236 left:1,
11237 right:1,
11238 center:1,
11239 justify:1
11240 },
11241 doJustify = function (range, style) {
11242 var bookmark = range.createBookmark(),
11243 filterFn = function (node) {
11244 return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node);
11245 };
11246
11247 range.enlarge(true);
11248 var bookmark2 = range.createBookmark(),
11249 current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
11250 tmpRange = range.cloneRange(),
11251 tmpNode;
11252 while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
11253 if (current.nodeType == 3 || !block(current)) {
11254 tmpRange.setStartBefore(current);
11255 while (current && current !== bookmark2.end && !block(current)) {
11256 tmpNode = current;
11257 current = domUtils.getNextDomNode(current, false, null, function (node) {
11258 return !block(node);
11259 });
11260 }
11261 tmpRange.setEndAfter(tmpNode);
11262 var common = tmpRange.getCommonAncestor();
11263 if (!domUtils.isBody(common) && block(common)) {
11264 domUtils.setStyles(common, utils.isString(style) ? {'text-align':style} : style);
11265 current = common;
11266 } else {
11267 var p = range.document.createElement('p');
11268 domUtils.setStyles(p, utils.isString(style) ? {'text-align':style} : style);
11269 var frag = tmpRange.extractContents();
11270 p.appendChild(frag);
11271 tmpRange.insertNode(p);
11272 current = p;
11273 }
11274 current = domUtils.getNextDomNode(current, false, filterFn);
11275 } else {
11276 current = domUtils.getNextDomNode(current, true, filterFn);
11277 }
11278 }
11279 return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
11280 };
11281
11282 UE.commands['justify'] = {
11283 execCommand:function (cmdName, align) {
11284 var range = this.selection.getRange(),
11285 txt;
11286
11287 //闭合时单独处理
11288 if (range.collapsed) {
11289 txt = this.document.createTextNode('p');
11290 range.insertNode(txt);
11291 }
11292 doJustify(range, align);
11293 if (txt) {
11294 range.setStartBefore(txt).collapse(true);
11295 domUtils.remove(txt);
11296 }
11297
11298 range.select();
11299
11300
11301 return true;
11302 },
11303 queryCommandValue:function () {
11304 var startNode = this.selection.getStart(),
11305 value = domUtils.getComputedStyle(startNode, 'text-align');
11306 return defaultValue[value] ? value : 'left';
11307 },
11308 queryCommandState:function () {
11309 var start = this.selection.getStart(),
11310 cell = start && domUtils.findParentByTagName(start, ["td", "th","caption"], true);
11311
11312 return cell? -1:0;
11313 }
11314
11315 };
11316};
11317
11318
11319// plugins/font.js
11320/**
11321 * 字体颜色,背景色,字号,字体,下划线,删除线
11322 * @file
11323 * @since 1.2.6.1
11324 */
11325
11326/**
11327 * 字体颜色
11328 * @command forecolor
11329 * @method execCommand
11330 * @param { String } cmd 命令字符串
11331 * @param { String } value 色值(必须十六进制)
11332 * @example
11333 * ```javascript
11334 * editor.execCommand( 'forecolor', '#000' );
11335 * ```
11336 */
11337/**
11338 * 返回选区字体颜色
11339 * @command forecolor
11340 * @method queryCommandValue
11341 * @param { String } cmd 命令字符串
11342 * @return { String } 返回字体颜色
11343 * @example
11344 * ```javascript
11345 * editor.queryCommandValue( 'forecolor' );
11346 * ```
11347 */
11348
11349/**
11350 * 字体背景颜色
11351 * @command backcolor
11352 * @method execCommand
11353 * @param { String } cmd 命令字符串
11354 * @param { String } value 色值(必须十六进制)
11355 * @example
11356 * ```javascript
11357 * editor.execCommand( 'backcolor', '#000' );
11358 * ```
11359 */
11360/**
11361 * 返回选区字体颜色
11362 * @command backcolor
11363 * @method queryCommandValue
11364 * @param { String } cmd 命令字符串
11365 * @return { String } 返回字体背景颜色
11366 * @example
11367 * ```javascript
11368 * editor.queryCommandValue( 'backcolor' );
11369 * ```
11370 */
11371
11372/**
11373 * 字体大小
11374 * @command fontsize
11375 * @method execCommand
11376 * @param { String } cmd 命令字符串
11377 * @param { String } value 字体大小
11378 * @example
11379 * ```javascript
11380 * editor.execCommand( 'fontsize', '14px' );
11381 * ```
11382 */
11383/**
11384 * 返回选区字体大小
11385 * @command fontsize
11386 * @method queryCommandValue
11387 * @param { String } cmd 命令字符串
11388 * @return { String } 返回字体大小
11389 * @example
11390 * ```javascript
11391 * editor.queryCommandValue( 'fontsize' );
11392 * ```
11393 */
11394
11395/**
11396 * 字体样式
11397 * @command fontfamily
11398 * @method execCommand
11399 * @param { String } cmd 命令字符串
11400 * @param { String } value 字体样式
11401 * @example
11402 * ```javascript
11403 * editor.execCommand( 'fontfamily', '微软雅黑' );
11404 * ```
11405 */
11406/**
11407 * 返回选区字体样式
11408 * @command fontfamily
11409 * @method queryCommandValue
11410 * @param { String } cmd 命令字符串
11411 * @return { String } 返回字体样式
11412 * @example
11413 * ```javascript
11414 * editor.queryCommandValue( 'fontfamily' );
11415 * ```
11416 */
11417
11418/**
11419 * 字体下划线,与删除线互斥
11420 * @command underline
11421 * @method execCommand
11422 * @param { String } cmd 命令字符串
11423 * @example
11424 * ```javascript
11425 * editor.execCommand( 'underline' );
11426 * ```
11427 */
11428
11429/**
11430 * 字体删除线,与下划线互斥
11431 * @command strikethrough
11432 * @method execCommand
11433 * @param { String } cmd 命令字符串
11434 * @example
11435 * ```javascript
11436 * editor.execCommand( 'strikethrough' );
11437 * ```
11438 */
11439
11440/**
11441 * 字体边框
11442 * @command fontborder
11443 * @method execCommand
11444 * @param { String } cmd 命令字符串
11445 * @example
11446 * ```javascript
11447 * editor.execCommand( 'fontborder' );
11448 * ```
11449 */
11450
11451UE.plugins['font'] = function () {
11452 var me = this,
11453 fonts = {
11454 'forecolor': 'color',
11455 'backcolor': 'background-color',
11456 'fontsize': 'font-size',
11457 'fontfamily': 'font-family',
11458 'underline': 'text-decoration',
11459 'strikethrough': 'text-decoration',
11460 'fontborder': 'border'
11461 },
11462 needCmd = {'underline': 1, 'strikethrough': 1, 'fontborder': 1},
11463 needSetChild = {
11464 'forecolor': 'color',
11465 'backcolor': 'background-color',
11466 'fontsize': 'font-size',
11467 'fontfamily': 'font-family'
11468
11469 };
11470 me.setOpt({
11471 'fontfamily': [
11472 { name: 'songti', val: '宋体,SimSun'},
11473 { name: 'yahei', val: '微软雅黑,Microsoft YaHei'},
11474 { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'},
11475 { name: 'heiti', val: '黑体, SimHei'},
11476 { name: 'lishu', val: '隶书, SimLi'},
11477 { name: 'andaleMono', val: 'andale mono'},
11478 { name: 'arial', val: 'arial, helvetica,sans-serif'},
11479 { name: 'arialBlack', val: 'arial black,avant garde'},
11480 { name: 'comicSansMs', val: 'comic sans ms'},
11481 { name: 'impact', val: 'impact,chicago'},
11482 { name: 'timesNewRoman', val: 'times new roman'}
11483 ],
11484 'fontsize': [10, 11, 12, 14, 16, 18, 20, 24, 36]
11485 });
11486
11487 function mergeWithParent(node){
11488 var parent;
11489 while(parent = node.parentNode){
11490 if(parent.tagName == 'SPAN' && domUtils.getChildCount(parent,function(child){
11491 return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child)
11492 }) == 1) {
11493 parent.style.cssText += node.style.cssText;
11494 domUtils.remove(node,true);
11495 node = parent;
11496
11497 }else{
11498 break;
11499 }
11500 }
11501
11502 }
11503 function mergeChild(rng,cmdName,value){
11504 if(needSetChild[cmdName]){
11505 rng.adjustmentBoundary();
11506 if(!rng.collapsed && rng.startContainer.nodeType == 1){
11507 var start = rng.startContainer.childNodes[rng.startOffset];
11508 if(start && domUtils.isTagNode(start,'span')){
11509 var bk = rng.createBookmark();
11510 utils.each(domUtils.getElementsByTagName(start, 'span'), function (span) {
11511 if (!span.parentNode || domUtils.isBookmarkNode(span))return;
11512 if(cmdName == 'backcolor' && domUtils.getComputedStyle(span,'background-color').toLowerCase() === value){
11513 return;
11514 }
11515 domUtils.removeStyle(span,needSetChild[cmdName]);
11516 if(span.style.cssText.replace(/^\s+$/,'').length == 0){
11517 domUtils.remove(span,true)
11518 }
11519 });
11520 rng.moveToBookmark(bk)
11521 }
11522 }
11523 }
11524
11525 }
11526 function mergesibling(rng,cmdName,value) {
11527 var collapsed = rng.collapsed,
11528 bk = rng.createBookmark(), common;
11529 if (collapsed) {
11530 common = bk.start.parentNode;
11531 while (dtd.$inline[common.tagName]) {
11532 common = common.parentNode;
11533 }
11534 } else {
11535 common = domUtils.getCommonAncestor(bk.start, bk.end);
11536 }
11537 utils.each(domUtils.getElementsByTagName(common, 'span'), function (span) {
11538 if (!span.parentNode || domUtils.isBookmarkNode(span))return;
11539 if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) {
11540 if(/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)){
11541 domUtils.remove(span, true);
11542 }else{
11543 domUtils.removeStyle(span,'border');
11544 }
11545 return
11546 }
11547 if (/border/i.test(span.style.cssText) && span.parentNode.tagName == 'SPAN' && /border/i.test(span.parentNode.style.cssText)) {
11548 span.style.cssText = span.style.cssText.replace(/border[^:]*:[^;]+;?/gi, '');
11549 }
11550 if(!(cmdName=='fontborder' && value=='none')){
11551 var next = span.nextSibling;
11552 while (next && next.nodeType == 1 && next.tagName == 'SPAN' ) {
11553 if(domUtils.isBookmarkNode(next) && cmdName == 'fontborder') {
11554 span.appendChild(next);
11555 next = span.nextSibling;
11556 continue;
11557 }
11558 if (next.style.cssText == span.style.cssText) {
11559 domUtils.moveChild(next, span);
11560 domUtils.remove(next);
11561 }
11562 if (span.nextSibling === next)
11563 break;
11564 next = span.nextSibling;
11565 }
11566 }
11567
11568
11569 mergeWithParent(span);
11570 if(browser.ie && browser.version > 8 ){
11571 //拷贝父亲们的特别的属性,这里只做背景颜色的处理
11572 var parent = domUtils.findParent(span,function(n){return n.tagName == 'SPAN' && /background-color/.test(n.style.cssText)});
11573 if(parent && !/background-color/.test(span.style.cssText)){
11574 span.style.backgroundColor = parent.style.backgroundColor;
11575 }
11576 }
11577
11578 });
11579 rng.moveToBookmark(bk);
11580 mergeChild(rng,cmdName,value)
11581 }
11582
11583 me.addInputRule(function (root) {
11584 utils.each(root.getNodesByTagName('u s del font strike'), function (node) {
11585 if (node.tagName == 'font') {
11586 var cssStyle = [];
11587 for (var p in node.attrs) {
11588 switch (p) {
11589 case 'size':
11590 cssStyle.push('font-size:' +
11591 ({
11592 '1':'10',
11593 '2':'12',
11594 '3':'16',
11595 '4':'18',
11596 '5':'24',
11597 '6':'32',
11598 '7':'48'
11599 }[node.attrs[p]] || node.attrs[p]) + 'px');
11600 break;
11601 case 'color':
11602 cssStyle.push('color:' + node.attrs[p]);
11603 break;
11604 case 'face':
11605 cssStyle.push('font-family:' + node.attrs[p]);
11606 break;
11607 case 'style':
11608 cssStyle.push(node.attrs[p]);
11609 }
11610 }
11611 node.attrs = {
11612 'style': cssStyle.join(';')
11613 };
11614 } else {
11615 var val = node.tagName == 'u' ? 'underline' : 'line-through';
11616 node.attrs = {
11617 'style': (node.getAttr('style') || '') + 'text-decoration:' + val + ';'
11618 }
11619 }
11620 node.tagName = 'span';
11621 });
11622// utils.each(root.getNodesByTagName('span'), function (node) {
11623// var val;
11624// if(val = node.getAttr('class')){
11625// if(/fontstrikethrough/.test(val)){
11626// node.setStyle('text-decoration','line-through');
11627// if(node.attrs['class']){
11628// node.attrs['class'] = node.attrs['class'].replace(/fontstrikethrough/,'');
11629// }else{
11630// node.setAttr('class')
11631// }
11632// }
11633// if(/fontborder/.test(val)){
11634// node.setStyle('border','1px solid #000');
11635// if(node.attrs['class']){
11636// node.attrs['class'] = node.attrs['class'].replace(/fontborder/,'');
11637// }else{
11638// node.setAttr('class')
11639// }
11640// }
11641// }
11642// });
11643 });
11644// me.addOutputRule(function(root){
11645// utils.each(root.getNodesByTagName('span'), function (node) {
11646// var val;
11647// if(val = node.getStyle('text-decoration')){
11648// if(/line-through/.test(val)){
11649// if(node.attrs['class']){
11650// node.attrs['class'] += ' fontstrikethrough';
11651// }else{
11652// node.setAttr('class','fontstrikethrough')
11653// }
11654// }
11655//
11656// node.setStyle('text-decoration')
11657// }
11658// if(val = node.getStyle('border')){
11659// if(/1px/.test(val) && /solid/.test(val)){
11660// if(node.attrs['class']){
11661// node.attrs['class'] += ' fontborder';
11662//
11663// }else{
11664// node.setAttr('class','fontborder')
11665// }
11666// }
11667// node.setStyle('border')
11668//
11669// }
11670// });
11671// });
11672 for (var p in fonts) {
11673 (function (cmd, style) {
11674 UE.commands[cmd] = {
11675 execCommand: function (cmdName, value) {
11676 value = value || (this.queryCommandState(cmdName) ? 'none' : cmdName == 'underline' ? 'underline' :
11677 cmdName == 'fontborder' ? '1px solid #000' :
11678 'line-through');
11679 var me = this,
11680 range = this.selection.getRange(),
11681 text;
11682
11683 if (value == 'default') {
11684
11685 if (range.collapsed) {
11686 text = me.document.createTextNode('font');
11687 range.insertNode(text).select();
11688
11689 }
11690 me.execCommand('removeFormat', 'span,a', style);
11691 if (text) {
11692 range.setStartBefore(text).collapse(true);
11693 domUtils.remove(text);
11694 }
11695 mergesibling(range,cmdName,value);
11696 range.select()
11697 } else {
11698 if (!range.collapsed) {
11699 if (needCmd[cmd] && me.queryCommandValue(cmd)) {
11700 me.execCommand('removeFormat', 'span,a', style);
11701 }
11702 range = me.selection.getRange();
11703
11704 range.applyInlineStyle('span', {'style': style + ':' + value});
11705 mergesibling(range, cmdName,value);
11706 range.select();
11707 } else {
11708
11709 var span = domUtils.findParentByTagName(range.startContainer, 'span', true);
11710 text = me.document.createTextNode('font');
11711 if (span && !span.children.length && !span[browser.ie ? 'innerText' : 'textContent'].replace(fillCharReg, '').length) {
11712 //for ie hack when enter
11713 range.insertNode(text);
11714 if (needCmd[cmd]) {
11715 range.selectNode(text).select();
11716 me.execCommand('removeFormat', 'span,a', style, null);
11717
11718 span = domUtils.findParentByTagName(text, 'span', true);
11719 range.setStartBefore(text);
11720
11721 }
11722 span && (span.style.cssText += ';' + style + ':' + value);
11723 range.collapse(true).select();
11724
11725
11726 } else {
11727 range.insertNode(text);
11728 range.selectNode(text).select();
11729 span = range.document.createElement('span');
11730
11731 if (needCmd[cmd]) {
11732 //a标签内的不处理跳过
11733 if (domUtils.findParentByTagName(text, 'a', true)) {
11734 range.setStartBefore(text).setCursor();
11735 domUtils.remove(text);
11736 return;
11737 }
11738 me.execCommand('removeFormat', 'span,a', style);
11739 }
11740
11741 span.style.cssText = style + ':' + value;
11742
11743
11744 text.parentNode.insertBefore(span, text);
11745 //修复,span套span 但样式不继承的问题
11746 if (!browser.ie || browser.ie && browser.version == 9) {
11747 var spanParent = span.parentNode;
11748 while (!domUtils.isBlockElm(spanParent)) {
11749 if (spanParent.tagName == 'SPAN') {
11750 //opera合并style不会加入";"
11751 span.style.cssText = spanParent.style.cssText + ";" + span.style.cssText;
11752 }
11753 spanParent = spanParent.parentNode;
11754 }
11755 }
11756
11757
11758 if (opera) {
11759 setTimeout(function () {
11760 range.setStart(span, 0).collapse(true);
11761 mergesibling(range, cmdName,value);
11762 range.select();
11763 });
11764 } else {
11765 range.setStart(span, 0).collapse(true);
11766 mergesibling(range,cmdName,value);
11767 range.select();
11768 }
11769
11770 //trace:981
11771 //domUtils.mergeToParent(span)
11772 }
11773 domUtils.remove(text);
11774 }
11775
11776
11777 }
11778 return true;
11779 },
11780 queryCommandValue: function (cmdName) {
11781 var startNode = this.selection.getStart();
11782
11783 //trace:946
11784 if (cmdName == 'underline' || cmdName == 'strikethrough') {
11785 var tmpNode = startNode, value;
11786 while (tmpNode && !domUtils.isBlockElm(tmpNode) && !domUtils.isBody(tmpNode)) {
11787 if (tmpNode.nodeType == 1) {
11788 value = domUtils.getComputedStyle(tmpNode, style);
11789 if (value != 'none') {
11790 return value;
11791 }
11792 }
11793
11794 tmpNode = tmpNode.parentNode;
11795 }
11796 return 'none';
11797 }
11798 if (cmdName == 'fontborder') {
11799 var tmp = startNode, val;
11800 while (tmp && dtd.$inline[tmp.tagName]) {
11801 if (val = domUtils.getComputedStyle(tmp, 'border')) {
11802
11803 if (/1px/.test(val) && /solid/.test(val)) {
11804 return val;
11805 }
11806 }
11807 tmp = tmp.parentNode;
11808 }
11809 return ''
11810 }
11811
11812 if( cmdName == 'FontSize' ) {
11813 var styleVal = domUtils.getComputedStyle(startNode, style),
11814 tmp = /^([\d\.]+)(\w+)$/.exec( styleVal );
11815
11816 if( tmp ) {
11817
11818 return Math.floor( tmp[1] ) + tmp[2];
11819
11820 }
11821
11822 return styleVal;
11823
11824 }
11825
11826 return domUtils.getComputedStyle(startNode, style);
11827 },
11828 queryCommandState: function (cmdName) {
11829 if (!needCmd[cmdName])
11830 return 0;
11831 var val = this.queryCommandValue(cmdName);
11832 if (cmdName == 'fontborder') {
11833 return /1px/.test(val) && /solid/.test(val)
11834 } else {
11835 return cmdName == 'underline' ? /underline/.test(val) : /line\-through/.test(val);
11836
11837 }
11838
11839 }
11840 };
11841 })(p, fonts[p]);
11842 }
11843};
11844
11845// plugins/link.js
11846/**
11847 * 超链接
11848 * @file
11849 * @since 1.2.6.1
11850 */
11851
11852/**
11853 * 插入超链接
11854 * @command link
11855 * @method execCommand
11856 * @param { String } cmd 命令字符串
11857 * @param { Object } options 设置自定义属性,例如:url、title、target
11858 * @example
11859 * ```javascript
11860 * editor.execCommand( 'link', '{
11861 * url:'ueditor.baidu.com',
11862 * title:'ueditor',
11863 * target:'_blank'
11864 * }' );
11865 * ```
11866 */
11867/**
11868 * 返回当前选中的第一个超链接节点
11869 * @command link
11870 * @method queryCommandValue
11871 * @param { String } cmd 命令字符串
11872 * @return { Element } 超链接节点
11873 * @example
11874 * ```javascript
11875 * editor.queryCommandValue( 'link' );
11876 * ```
11877 */
11878
11879/**
11880 * 取消超链接
11881 * @command unlink
11882 * @method execCommand
11883 * @param { String } cmd 命令字符串
11884 * @example
11885 * ```javascript
11886 * editor.execCommand( 'unlink');
11887 * ```
11888 */
11889
11890UE.plugins['link'] = function(){
11891 function optimize( range ) {
11892 var start = range.startContainer,end = range.endContainer;
11893
11894 if ( start = domUtils.findParentByTagName( start, 'a', true ) ) {
11895 range.setStartBefore( start );
11896 }
11897 if ( end = domUtils.findParentByTagName( end, 'a', true ) ) {
11898 range.setEndAfter( end );
11899 }
11900 }
11901
11902
11903 UE.commands['unlink'] = {
11904 execCommand : function() {
11905 var range = this.selection.getRange(),
11906 bookmark;
11907 if(range.collapsed && !domUtils.findParentByTagName( range.startContainer, 'a', true )){
11908 return;
11909 }
11910 bookmark = range.createBookmark();
11911 optimize( range );
11912 range.removeInlineStyle( 'a' ).moveToBookmark( bookmark ).select();
11913 },
11914 queryCommandState : function(){
11915 return !this.highlight && this.queryCommandValue('link') ? 0 : -1;
11916 }
11917
11918 };
11919 function doLink(range,opt,me){
11920 var rngClone = range.cloneRange(),
11921 link = me.queryCommandValue('link');
11922 optimize( range = range.adjustmentBoundary() );
11923 var start = range.startContainer;
11924 if(start.nodeType == 1 && link){
11925 start = start.childNodes[range.startOffset];
11926 if(start && start.nodeType == 1 && start.tagName == 'A' && /^(?:https?|ftp|file)\s*:\s*\/\//.test(start[browser.ie?'innerText':'textContent'])){
11927 start[browser.ie ? 'innerText' : 'textContent'] = utils.html(opt.textValue||opt.href);
11928
11929 }
11930 }
11931 if( !rngClone.collapsed || link){
11932 range.removeInlineStyle( 'a' );
11933 rngClone = range.cloneRange();
11934 }
11935
11936 if ( rngClone.collapsed ) {
11937 var a = range.document.createElement( 'a'),
11938 text = '';
11939 if(opt.textValue){
11940
11941 text = utils.html(opt.textValue);
11942 delete opt.textValue;
11943 }else{
11944 text = utils.html(opt.href);
11945
11946 }
11947 domUtils.setAttributes( a, opt );
11948 start = domUtils.findParentByTagName( rngClone.startContainer, 'a', true );
11949 if(start && domUtils.isInNodeEndBoundary(rngClone,start)){
11950 range.setStartAfter(start).collapse(true);
11951
11952 }
11953 a[browser.ie ? 'innerText' : 'textContent'] = text;
11954 range.insertNode(a).selectNode( a );
11955 } else {
11956 range.applyInlineStyle( 'a', opt );
11957
11958 }
11959 }
11960 UE.commands['link'] = {
11961 execCommand : function( cmdName, opt ) {
11962 var range;
11963 opt._href && (opt._href = utils.unhtml(opt._href,/[<">]/g));
11964 opt.href && (opt.href = utils.unhtml(opt.href,/[<">]/g));
11965 opt.textValue && (opt.textValue = utils.unhtml(opt.textValue,/[<">]/g));
11966 doLink(range=this.selection.getRange(),opt,this);
11967 //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题
11968 range.collapse().select(true);
11969
11970 },
11971 queryCommandValue : function() {
11972 var range = this.selection.getRange(),
11973 node;
11974 if ( range.collapsed ) {
11975// node = this.selection.getStart();
11976 //在ie下getstart()取值偏上了
11977 node = range.startContainer;
11978 node = node.nodeType == 1 ? node : node.parentNode;
11979
11980 if ( node && (node = domUtils.findParentByTagName( node, 'a', true )) && ! domUtils.isInNodeEndBoundary(range,node)) {
11981
11982 return node;
11983 }
11984 } else {
11985 //trace:1111 如果是<p><a>xx</a></p> startContainer是p就会找不到a
11986 range.shrinkBoundary();
11987 var start = range.startContainer.nodeType == 3 || !range.startContainer.childNodes[range.startOffset] ? range.startContainer : range.startContainer.childNodes[range.startOffset],
11988 end = range.endContainer.nodeType == 3 || range.endOffset == 0 ? range.endContainer : range.endContainer.childNodes[range.endOffset-1],
11989 common = range.getCommonAncestor();
11990 node = domUtils.findParentByTagName( common, 'a', true );
11991 if ( !node && common.nodeType == 1){
11992
11993 var as = common.getElementsByTagName( 'a' ),
11994 ps,pe;
11995
11996 for ( var i = 0,ci; ci = as[i++]; ) {
11997 ps = domUtils.getPosition( ci, start ),pe = domUtils.getPosition( ci,end);
11998 if ( (ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS)
11999 &&
12000 (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
12001 ) {
12002 node = ci;
12003 break;
12004 }
12005 }
12006 }
12007 return node;
12008 }
12009
12010 },
12011 queryCommandState : function() {
12012 //判断如果是视频的话连接不可用
12013 //fix 853
12014 var img = this.selection.getRange().getClosedNode(),
12015 flag = img && (img.className == "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1);
12016 return flag ? -1 : 0;
12017 }
12018 };
12019};
12020
12021// plugins/iframe.js
12022///import core
12023///import plugins\inserthtml.js
12024///commands 插入框架
12025///commandsName InsertFrame
12026///commandsTitle 插入Iframe
12027///commandsDialog dialogs\insertframe
12028
12029UE.plugins['insertframe'] = function() {
12030 var me =this;
12031 function deleteIframe(){
12032 me._iframe && delete me._iframe;
12033 }
12034
12035 me.addListener("selectionchange",function(){
12036 deleteIframe();
12037 });
12038
12039};
12040
12041
12042
12043// plugins/scrawl.js
12044///import core
12045///commands 涂鸦
12046///commandsName Scrawl
12047///commandsTitle 涂鸦
12048///commandsDialog dialogs\scrawl
12049UE.commands['scrawl'] = {
12050 queryCommandState : function(){
12051 return ( browser.ie && browser.version <= 8 ) ? -1 :0;
12052 }
12053};
12054
12055
12056// plugins/removeformat.js
12057/**
12058 * 清除格式
12059 * @file
12060 * @since 1.2.6.1
12061 */
12062
12063/**
12064 * 清除文字样式
12065 * @command removeformat
12066 * @method execCommand
12067 * @param { String } cmd 命令字符串
12068 * @param {String} tags 以逗号隔开的标签。如:strong
12069 * @param {String} style 样式如:color
12070 * @param {String} attrs 属性如:width
12071 * @example
12072 * ```javascript
12073 * editor.execCommand( 'removeformat', 'strong','color','width' );
12074 * ```
12075 */
12076
12077UE.plugins['removeformat'] = function(){
12078 var me = this;
12079 me.setOpt({
12080 'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
12081 'removeFormatAttributes':'class,style,lang,width,height,align,hspace,valign'
12082 });
12083 me.commands['removeformat'] = {
12084 execCommand : function( cmdName, tags, style, attrs,notIncludeA ) {
12085
12086 var tagReg = new RegExp( '^(?:' + (tags || this.options.removeFormatTags).replace( /,/g, '|' ) + ')$', 'i' ) ,
12087 removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split( ',' ),
12088 range = new dom.Range( this.document ),
12089 bookmark,node,parent,
12090 filter = function( node ) {
12091 return node.nodeType == 1;
12092 };
12093
12094 function isRedundantSpan (node) {
12095 if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span'){
12096 return 0;
12097 }
12098 if (browser.ie) {
12099 //ie 下判断实效,所以只能简单用style来判断
12100 //return node.style.cssText == '' ? 1 : 0;
12101 var attrs = node.attributes;
12102 if ( attrs.length ) {
12103 for ( var i = 0,l = attrs.length; i<l; i++ ) {
12104 if ( attrs[i].specified ) {
12105 return 0;
12106 }
12107 }
12108 return 1;
12109 }
12110 }
12111 return !node.attributes.length;
12112 }
12113 function doRemove( range ) {
12114
12115 var bookmark1 = range.createBookmark();
12116 if ( range.collapsed ) {
12117 range.enlarge( true );
12118 }
12119
12120 //不能把a标签切了
12121 if(!notIncludeA){
12122 var aNode = domUtils.findParentByTagName(range.startContainer,'a',true);
12123 if(aNode){
12124 range.setStartBefore(aNode);
12125 }
12126
12127 aNode = domUtils.findParentByTagName(range.endContainer,'a',true);
12128 if(aNode){
12129 range.setEndAfter(aNode);
12130 }
12131
12132 }
12133
12134
12135 bookmark = range.createBookmark();
12136
12137 node = bookmark.start;
12138
12139 //切开始
12140 while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
12141 domUtils.breakParent( node, parent );
12142
12143 domUtils.clearEmptySibling( node );
12144 }
12145 if ( bookmark.end ) {
12146 //切结束
12147 node = bookmark.end;
12148 while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
12149 domUtils.breakParent( node, parent );
12150 domUtils.clearEmptySibling( node );
12151 }
12152
12153 //开始去除样式
12154 var current = domUtils.getNextDomNode( bookmark.start, false, filter ),
12155 next;
12156 while ( current ) {
12157 if ( current == bookmark.end ) {
12158 break;
12159 }
12160
12161 next = domUtils.getNextDomNode( current, true, filter );
12162
12163 if ( !dtd.$empty[current.tagName.toLowerCase()] && !domUtils.isBookmarkNode( current ) ) {
12164 if ( tagReg.test( current.tagName ) ) {
12165 if ( style ) {
12166 domUtils.removeStyle( current, style );
12167 if ( isRedundantSpan( current ) && style != 'text-decoration'){
12168 domUtils.remove( current, true );
12169 }
12170 } else {
12171 domUtils.remove( current, true );
12172 }
12173 } else {
12174 //trace:939 不能把list上的样式去掉
12175 if(!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]){
12176 domUtils.removeAttributes( current, removeFormatAttributes );
12177 if ( isRedundantSpan( current ) ){
12178 domUtils.remove( current, true );
12179 }
12180 }
12181
12182 }
12183 }
12184 current = next;
12185 }
12186 }
12187 //trace:1035
12188 //trace:1096 不能把td上的样式去掉,比如边框
12189 var pN = bookmark.start.parentNode;
12190 if(domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]){
12191 domUtils.removeAttributes( pN,removeFormatAttributes );
12192 }
12193 pN = bookmark.end.parentNode;
12194 if(bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName]&& !dtd.$list[pN.tagName]){
12195 domUtils.removeAttributes( pN,removeFormatAttributes );
12196 }
12197 range.moveToBookmark( bookmark ).moveToBookmark(bookmark1);
12198 //清除冗余的代码 <b><bookmark></b>
12199 var node = range.startContainer,
12200 tmp,
12201 collapsed = range.collapsed;
12202 while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
12203 tmp = node.parentNode;
12204 range.setStartBefore(node);
12205 //trace:937
12206 //更新结束边界
12207 if(range.startContainer === range.endContainer){
12208 range.endOffset--;
12209 }
12210 domUtils.remove(node);
12211 node = tmp;
12212 }
12213
12214 if(!collapsed){
12215 node = range.endContainer;
12216 while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
12217 tmp = node.parentNode;
12218 range.setEndBefore(node);
12219 domUtils.remove(node);
12220
12221 node = tmp;
12222 }
12223
12224
12225 }
12226 }
12227
12228
12229
12230 range = this.selection.getRange();
12231 doRemove( range );
12232 range.select();
12233
12234 }
12235
12236 };
12237
12238};
12239
12240
12241// plugins/blockquote.js
12242/**
12243 * 添加引用
12244 * @file
12245 * @since 1.2.6.1
12246 */
12247
12248/**
12249 * 添加引用
12250 * @command blockquote
12251 * @method execCommand
12252 * @param { String } cmd 命令字符串
12253 * @example
12254 * ```javascript
12255 * editor.execCommand( 'blockquote' );
12256 * ```
12257 */
12258
12259/**
12260 * 添加引用
12261 * @command blockquote
12262 * @method execCommand
12263 * @param { String } cmd 命令字符串
12264 * @param { Object } attrs 节点属性
12265 * @example
12266 * ```javascript
12267 * editor.execCommand( 'blockquote',{
12268 * style: "color: red;"
12269 * } );
12270 * ```
12271 */
12272
12273
12274UE.plugins['blockquote'] = function(){
12275 var me = this;
12276 function getObj(editor){
12277 return domUtils.filterNodeList(editor.selection.getStartElementPath(),'blockquote');
12278 }
12279 me.commands['blockquote'] = {
12280 execCommand : function( cmdName, attrs ) {
12281 var range = this.selection.getRange(),
12282 obj = getObj(this),
12283 blockquote = dtd.blockquote,
12284 bookmark = range.createBookmark();
12285
12286 if ( obj ) {
12287
12288 var start = range.startContainer,
12289 startBlock = domUtils.isBlockElm(start) ? start : domUtils.findParent(start,function(node){return domUtils.isBlockElm(node)}),
12290
12291 end = range.endContainer,
12292 endBlock = domUtils.isBlockElm(end) ? end : domUtils.findParent(end,function(node){return domUtils.isBlockElm(node)});
12293
12294 //处理一下li
12295 startBlock = domUtils.findParentByTagName(startBlock,'li',true) || startBlock;
12296 endBlock = domUtils.findParentByTagName(endBlock,'li',true) || endBlock;
12297
12298
12299 if(startBlock.tagName == 'LI' || startBlock.tagName == 'TD' || startBlock === obj || domUtils.isBody(startBlock)){
12300 domUtils.remove(obj,true);
12301 }else{
12302 domUtils.breakParent(startBlock,obj);
12303 }
12304
12305 if(startBlock !== endBlock){
12306 obj = domUtils.findParentByTagName(endBlock,'blockquote');
12307 if(obj){
12308 if(endBlock.tagName == 'LI' || endBlock.tagName == 'TD'|| domUtils.isBody(endBlock)){
12309 obj.parentNode && domUtils.remove(obj,true);
12310 }else{
12311 domUtils.breakParent(endBlock,obj);
12312 }
12313
12314 }
12315 }
12316
12317 var blockquotes = domUtils.getElementsByTagName(this.document,'blockquote');
12318 for(var i=0,bi;bi=blockquotes[i++];){
12319 if(!bi.childNodes.length){
12320 domUtils.remove(bi);
12321 }else if(domUtils.getPosition(bi,startBlock)&domUtils.POSITION_FOLLOWING && domUtils.getPosition(bi,endBlock)&domUtils.POSITION_PRECEDING){
12322 domUtils.remove(bi,true);
12323 }
12324 }
12325
12326
12327
12328
12329 } else {
12330
12331 var tmpRange = range.cloneRange(),
12332 node = tmpRange.startContainer.nodeType == 1 ? tmpRange.startContainer : tmpRange.startContainer.parentNode,
12333 preNode = node,
12334 doEnd = 1;
12335
12336 //调整开始
12337 while ( 1 ) {
12338 if ( domUtils.isBody(node) ) {
12339 if ( preNode !== node ) {
12340 if ( range.collapsed ) {
12341 tmpRange.selectNode( preNode );
12342 doEnd = 0;
12343 } else {
12344 tmpRange.setStartBefore( preNode );
12345 }
12346 }else{
12347 tmpRange.setStart(node,0);
12348 }
12349
12350 break;
12351 }
12352 if ( !blockquote[node.tagName] ) {
12353 if ( range.collapsed ) {
12354 tmpRange.selectNode( preNode );
12355 } else{
12356 tmpRange.setStartBefore( preNode);
12357 }
12358 break;
12359 }
12360
12361 preNode = node;
12362 node = node.parentNode;
12363 }
12364
12365 //调整结束
12366 if ( doEnd ) {
12367 preNode = node = node = tmpRange.endContainer.nodeType == 1 ? tmpRange.endContainer : tmpRange.endContainer.parentNode;
12368 while ( 1 ) {
12369
12370 if ( domUtils.isBody( node ) ) {
12371 if ( preNode !== node ) {
12372
12373 tmpRange.setEndAfter( preNode );
12374
12375 } else {
12376 tmpRange.setEnd( node, node.childNodes.length );
12377 }
12378
12379 break;
12380 }
12381 if ( !blockquote[node.tagName] ) {
12382 tmpRange.setEndAfter( preNode );
12383 break;
12384 }
12385
12386 preNode = node;
12387 node = node.parentNode;
12388 }
12389
12390 }
12391
12392
12393 node = range.document.createElement( 'blockquote' );
12394 domUtils.setAttributes( node, attrs );
12395 node.appendChild( tmpRange.extractContents() );
12396 tmpRange.insertNode( node );
12397 //去除重复的
12398 var childs = domUtils.getElementsByTagName(node,'blockquote');
12399 for(var i=0,ci;ci=childs[i++];){
12400 if(ci.parentNode){
12401 domUtils.remove(ci,true);
12402 }
12403 }
12404
12405 }
12406 range.moveToBookmark( bookmark ).select();
12407 },
12408 queryCommandState : function() {
12409 return getObj(this) ? 1 : 0;
12410 }
12411 };
12412};
12413
12414
12415
12416// plugins/convertcase.js
12417/**
12418 * 大小写转换
12419 * @file
12420 * @since 1.2.6.1
12421 */
12422
12423/**
12424 * 把选区内文本变大写,与“tolowercase”命令互斥
12425 * @command touppercase
12426 * @method execCommand
12427 * @param { String } cmd 命令字符串
12428 * @example
12429 * ```javascript
12430 * editor.execCommand( 'touppercase' );
12431 * ```
12432 */
12433
12434/**
12435 * 把选区内文本变小写,与“touppercase”命令互斥
12436 * @command tolowercase
12437 * @method execCommand
12438 * @param { String } cmd 命令字符串
12439 * @example
12440 * ```javascript
12441 * editor.execCommand( 'tolowercase' );
12442 * ```
12443 */
12444UE.commands['touppercase'] =
12445UE.commands['tolowercase'] = {
12446 execCommand:function (cmd) {
12447 var me = this;
12448 var rng = me.selection.getRange();
12449 if(rng.collapsed){
12450 return rng;
12451 }
12452 var bk = rng.createBookmark(),
12453 bkEnd = bk.end,
12454 filterFn = function( node ) {
12455 return !domUtils.isBr(node) && !domUtils.isWhitespace( node );
12456 },
12457 curNode = domUtils.getNextDomNode( bk.start, false, filterFn );
12458 while ( curNode && (domUtils.getPosition( curNode, bkEnd ) & domUtils.POSITION_PRECEDING) ) {
12459
12460 if ( curNode.nodeType == 3 ) {
12461 curNode.nodeValue = curNode.nodeValue[cmd == 'touppercase' ? 'toUpperCase' : 'toLowerCase']();
12462 }
12463 curNode = domUtils.getNextDomNode( curNode, true, filterFn );
12464 if(curNode === bkEnd){
12465 break;
12466 }
12467
12468 }
12469 rng.moveToBookmark(bk).select();
12470 }
12471};
12472
12473
12474
12475// plugins/indent.js
12476/**
12477 * 首行缩进
12478 * @file
12479 * @since 1.2.6.1
12480 */
12481
12482/**
12483 * 缩进
12484 * @command indent
12485 * @method execCommand
12486 * @param { String } cmd 命令字符串
12487 * @example
12488 * ```javascript
12489 * editor.execCommand( 'indent' );
12490 * ```
12491 */
12492UE.commands['indent'] = {
12493 execCommand : function() {
12494 var me = this,value = me.queryCommandState("indent") ? "0em" : (me.options.indentValue || '2em');
12495 me.execCommand('Paragraph','p',{style:'text-indent:'+ value});
12496 },
12497 queryCommandState : function() {
12498 var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),'p h1 h2 h3 h4 h5 h6');
12499 return pN && pN.style.textIndent && parseInt(pN.style.textIndent) ? 1 : 0;
12500 }
12501
12502};
12503
12504
12505// plugins/print.js
12506/**
12507 * 打印
12508 * @file
12509 * @since 1.2.6.1
12510 */
12511
12512/**
12513 * 打印
12514 * @command print
12515 * @method execCommand
12516 * @param { String } cmd 命令字符串
12517 * @example
12518 * ```javascript
12519 * editor.execCommand( 'print' );
12520 * ```
12521 */
12522UE.commands['print'] = {
12523 execCommand : function(){
12524 this.window.print();
12525 },
12526 notNeedUndo : 1
12527};
12528
12529
12530
12531// plugins/preview.js
12532/**
12533 * 预览
12534 * @file
12535 * @since 1.2.6.1
12536 */
12537
12538/**
12539 * 预览
12540 * @command preview
12541 * @method execCommand
12542 * @param { String } cmd 命令字符串
12543 * @example
12544 * ```javascript
12545 * editor.execCommand( 'preview' );
12546 * ```
12547 */
12548UE.commands['preview'] = {
12549 execCommand : function(){
12550 var w = window.open('', '_blank', ''),
12551 d = w.document;
12552 d.open();
12553 d.write('<!DOCTYPE html><html><head><meta charset="utf-8"/><script src="'+this.options.UEDITOR_HOME_URL+'ueditor.parse.js"></script><script>' +
12554 "setTimeout(function(){uParse('div',{rootPath: '"+ this.options.UEDITOR_HOME_URL +"'})},300)" +
12555 '</script></head><body><div>'+this.getContent(null,null,true)+'</div></body></html>');
12556 d.close();
12557 },
12558 notNeedUndo : 1
12559};
12560
12561
12562// plugins/selectall.js
12563/**
12564 * 全选
12565 * @file
12566 * @since 1.2.6.1
12567 */
12568
12569/**
12570 * 选中所有内容
12571 * @command selectall
12572 * @method execCommand
12573 * @param { String } cmd 命令字符串
12574 * @example
12575 * ```javascript
12576 * editor.execCommand( 'selectall' );
12577 * ```
12578 */
12579UE.plugins['selectall'] = function(){
12580 var me = this;
12581 me.commands['selectall'] = {
12582 execCommand : function(){
12583 //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
12584 var me = this,body = me.body,
12585 range = me.selection.getRange();
12586 range.selectNodeContents(body);
12587 if(domUtils.isEmptyBlock(body)){
12588 //opera不能自动合并到元素的里边,要手动处理一下
12589 if(browser.opera && body.firstChild && body.firstChild.nodeType == 1){
12590 range.setStartAtFirst(body.firstChild);
12591 }
12592 range.collapse(true);
12593 }
12594 range.select(true);
12595 },
12596 notNeedUndo : 1
12597 };
12598
12599
12600 //快捷键
12601 me.addshortcutkey({
12602 "selectAll" : "ctrl+65"
12603 });
12604};
12605
12606
12607// plugins/paragraph.js
12608/**
12609 * 段落样式
12610 * @file
12611 * @since 1.2.6.1
12612 */
12613
12614/**
12615 * 段落格式
12616 * @command paragraph
12617 * @method execCommand
12618 * @param { String } cmd 命令字符串
12619 * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
12620 * @param {Object} attrs 标签的属性
12621 * @example
12622 * ```javascript
12623 * editor.execCommand( 'Paragraph','h1','{
12624 * class:'test'
12625 * }' );
12626 * ```
12627 */
12628
12629/**
12630 * 返回选区内节点标签名
12631 * @command paragraph
12632 * @method queryCommandValue
12633 * @param { String } cmd 命令字符串
12634 * @return { String } 节点标签名
12635 * @example
12636 * ```javascript
12637 * editor.queryCommandValue( 'Paragraph' );
12638 * ```
12639 */
12640
12641UE.plugins['paragraph'] = function() {
12642 var me = this,
12643 block = domUtils.isBlockElm,
12644 notExchange = ['TD','LI','PRE'],
12645
12646 doParagraph = function(range,style,attrs,sourceCmdName){
12647 var bookmark = range.createBookmark(),
12648 filterFn = function( node ) {
12649 return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace( node );
12650 },
12651 para;
12652
12653 range.enlarge( true );
12654 var bookmark2 = range.createBookmark(),
12655 current = domUtils.getNextDomNode( bookmark2.start, false, filterFn ),
12656 tmpRange = range.cloneRange(),
12657 tmpNode;
12658 while ( current && !(domUtils.getPosition( current, bookmark2.end ) & domUtils.POSITION_FOLLOWING) ) {
12659 if ( current.nodeType == 3 || !block( current ) ) {
12660 tmpRange.setStartBefore( current );
12661 while ( current && current !== bookmark2.end && !block( current ) ) {
12662 tmpNode = current;
12663 current = domUtils.getNextDomNode( current, false, null, function( node ) {
12664 return !block( node );
12665 } );
12666 }
12667 tmpRange.setEndAfter( tmpNode );
12668
12669 para = range.document.createElement( style );
12670 if(attrs){
12671 domUtils.setAttributes(para,attrs);
12672 if(sourceCmdName && sourceCmdName == 'customstyle' && attrs.style){
12673 para.style.cssText = attrs.style;
12674 }
12675 }
12676 para.appendChild( tmpRange.extractContents() );
12677 //需要内容占位
12678 if(domUtils.isEmptyNode(para)){
12679 domUtils.fillChar(range.document,para);
12680
12681 }
12682
12683 tmpRange.insertNode( para );
12684
12685 var parent = para.parentNode;
12686 //如果para上一级是一个block元素且不是body,td就删除它
12687 if ( block( parent ) && !domUtils.isBody( para.parentNode ) && utils.indexOf(notExchange,parent.tagName)==-1) {
12688 //存储dir,style
12689 if(!(sourceCmdName && sourceCmdName == 'customstyle')){
12690 parent.getAttribute('dir') && para.setAttribute('dir',parent.getAttribute('dir'));
12691 //trace:1070
12692 parent.style.cssText && (para.style.cssText = parent.style.cssText + ';' + para.style.cssText);
12693 //trace:1030
12694 parent.style.textAlign && !para.style.textAlign && (para.style.textAlign = parent.style.textAlign);
12695 parent.style.textIndent && !para.style.textIndent && (para.style.textIndent = parent.style.textIndent);
12696 parent.style.padding && !para.style.padding && (para.style.padding = parent.style.padding);
12697 }
12698
12699 //trace:1706 选择的就是h1-6要删除
12700 if(attrs && /h\d/i.test(parent.tagName) && !/h\d/i.test(para.tagName) ){
12701 domUtils.setAttributes(parent,attrs);
12702 if(sourceCmdName && sourceCmdName == 'customstyle' && attrs.style){
12703 parent.style.cssText = attrs.style;
12704 }
12705 domUtils.remove(para,true);
12706 para = parent;
12707 }else{
12708 domUtils.remove( para.parentNode, true );
12709 }
12710
12711 }
12712 if( utils.indexOf(notExchange,parent.tagName)!=-1){
12713 current = parent;
12714 }else{
12715 current = para;
12716 }
12717
12718
12719 current = domUtils.getNextDomNode( current, false, filterFn );
12720 } else {
12721 current = domUtils.getNextDomNode( current, true, filterFn );
12722 }
12723 }
12724 return range.moveToBookmark( bookmark2 ).moveToBookmark( bookmark );
12725 };
12726 me.setOpt('paragraph',{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''});
12727 me.commands['paragraph'] = {
12728 execCommand : function( cmdName, style,attrs,sourceCmdName ) {
12729 var range = this.selection.getRange();
12730 //闭合时单独处理
12731 if(range.collapsed){
12732 var txt = this.document.createTextNode('p');
12733 range.insertNode(txt);
12734 //去掉冗余的fillchar
12735 if(browser.ie){
12736 var node = txt.previousSibling;
12737 if(node && domUtils.isWhitespace(node)){
12738 domUtils.remove(node);
12739 }
12740 node = txt.nextSibling;
12741 if(node && domUtils.isWhitespace(node)){
12742 domUtils.remove(node);
12743 }
12744 }
12745
12746 }
12747 range = doParagraph(range,style,attrs,sourceCmdName);
12748 if(txt){
12749 range.setStartBefore(txt).collapse(true);
12750 pN = txt.parentNode;
12751
12752 domUtils.remove(txt);
12753
12754 if(domUtils.isBlockElm(pN)&&domUtils.isEmptyNode(pN)){
12755 domUtils.fillNode(this.document,pN);
12756 }
12757
12758 }
12759
12760 if(browser.gecko && range.collapsed && range.startContainer.nodeType == 1){
12761 var child = range.startContainer.childNodes[range.startOffset];
12762 if(child && child.nodeType == 1 && child.tagName.toLowerCase() == style){
12763 range.setStart(child,0).collapse(true);
12764 }
12765 }
12766 //trace:1097 原来有true,原因忘了,但去了就不能清除多余的占位符了
12767 range.select();
12768
12769
12770 return true;
12771 },
12772 queryCommandValue : function() {
12773 var node = domUtils.filterNodeList(this.selection.getStartElementPath(),'p h1 h2 h3 h4 h5 h6');
12774 return node ? node.tagName.toLowerCase() : '';
12775 }
12776 };
12777};
12778
12779
12780// plugins/directionality.js
12781/**
12782 * 设置文字输入的方向的插件
12783 * @file
12784 * @since 1.2.6.1
12785 */
12786(function() {
12787 var block = domUtils.isBlockElm ,
12788 getObj = function(editor){
12789// var startNode = editor.selection.getStart(),
12790// parents;
12791// if ( startNode ) {
12792// //查找所有的是block的父亲节点
12793// parents = domUtils.findParents( startNode, true, block, true );
12794// for ( var i = 0,ci; ci = parents[i++]; ) {
12795// if ( ci.getAttribute( 'dir' ) ) {
12796// return ci;
12797// }
12798// }
12799// }
12800 return domUtils.filterNodeList(editor.selection.getStartElementPath(),function(n){return n && n.nodeType == 1 && n.getAttribute('dir')});
12801
12802 },
12803 doDirectionality = function(range,editor,forward){
12804
12805 var bookmark,
12806 filterFn = function( node ) {
12807 return node.nodeType == 1 ? !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node);
12808 },
12809
12810 obj = getObj( editor );
12811
12812 if ( obj && range.collapsed ) {
12813 obj.setAttribute( 'dir', forward );
12814 return range;
12815 }
12816 bookmark = range.createBookmark();
12817 range.enlarge( true );
12818 var bookmark2 = range.createBookmark(),
12819 current = domUtils.getNextDomNode( bookmark2.start, false, filterFn ),
12820 tmpRange = range.cloneRange(),
12821 tmpNode;
12822 while ( current && !(domUtils.getPosition( current, bookmark2.end ) & domUtils.POSITION_FOLLOWING) ) {
12823 if ( current.nodeType == 3 || !block( current ) ) {
12824 tmpRange.setStartBefore( current );
12825 while ( current && current !== bookmark2.end && !block( current ) ) {
12826 tmpNode = current;
12827 current = domUtils.getNextDomNode( current, false, null, function( node ) {
12828 return !block( node );
12829 } );
12830 }
12831 tmpRange.setEndAfter( tmpNode );
12832 var common = tmpRange.getCommonAncestor();
12833 if ( !domUtils.isBody( common ) && block( common ) ) {
12834 //遍历到了block节点
12835 common.setAttribute( 'dir', forward );
12836 current = common;
12837 } else {
12838 //没有遍历到,添加一个block节点
12839 var p = range.document.createElement( 'p' );
12840 p.setAttribute( 'dir', forward );
12841 var frag = tmpRange.extractContents();
12842 p.appendChild( frag );
12843 tmpRange.insertNode( p );
12844 current = p;
12845 }
12846
12847 current = domUtils.getNextDomNode( current, false, filterFn );
12848 } else {
12849 current = domUtils.getNextDomNode( current, true, filterFn );
12850 }
12851 }
12852 return range.moveToBookmark( bookmark2 ).moveToBookmark( bookmark );
12853 };
12854
12855 /**
12856 * 文字输入方向
12857 * @command directionality
12858 * @method execCommand
12859 * @param { String } cmdName 命令字符串
12860 * @param { String } forward 传入'ltr'表示从左向右输入,传入'rtl'表示从右向左输入
12861 * @example
12862 * ```javascript
12863 * editor.execCommand( 'directionality', 'ltr');
12864 * ```
12865 */
12866
12867 /**
12868 * 查询当前选区的文字输入方向
12869 * @command directionality
12870 * @method queryCommandValue
12871 * @param { String } cmdName 命令字符串
12872 * @return { String } 返回'ltr'表示从左向右输入,返回'rtl'表示从右向左输入
12873 * @example
12874 * ```javascript
12875 * editor.queryCommandValue( 'directionality');
12876 * ```
12877 */
12878 UE.commands['directionality'] = {
12879 execCommand : function( cmdName,forward ) {
12880 var range = this.selection.getRange();
12881 //闭合时单独处理
12882 if(range.collapsed){
12883 var txt = this.document.createTextNode('d');
12884 range.insertNode(txt);
12885 }
12886 doDirectionality(range,this,forward);
12887 if(txt){
12888 range.setStartBefore(txt).collapse(true);
12889 domUtils.remove(txt);
12890 }
12891
12892 range.select();
12893 return true;
12894 },
12895 queryCommandValue : function() {
12896 var node = getObj(this);
12897 return node ? node.getAttribute('dir') : 'ltr';
12898 }
12899 };
12900})();
12901
12902
12903
12904// plugins/horizontal.js
12905/**
12906 * 插入分割线插件
12907 * @file
12908 * @since 1.2.6.1
12909 */
12910
12911/**
12912 * 插入分割线
12913 * @command horizontal
12914 * @method execCommand
12915 * @param { String } cmdName 命令字符串
12916 * @example
12917 * ```javascript
12918 * editor.execCommand( 'horizontal' );
12919 * ```
12920 */
12921UE.plugins['horizontal'] = function(){
12922 var me = this;
12923 me.commands['horizontal'] = {
12924 execCommand : function( cmdName ) {
12925 var me = this;
12926 if(me.queryCommandState(cmdName)!==-1){
12927 me.execCommand('insertHtml','<hr>');
12928 var range = me.selection.getRange(),
12929 start = range.startContainer;
12930 if(start.nodeType == 1 && !start.childNodes[range.startOffset] ){
12931
12932 var tmp;
12933 if(tmp = start.childNodes[range.startOffset - 1]){
12934 if(tmp.nodeType == 1 && tmp.tagName == 'HR'){
12935 if(me.options.enterTag == 'p'){
12936 tmp = me.document.createElement('p');
12937 range.insertNode(tmp);
12938 range.setStart(tmp,0).setCursor();
12939
12940 }else{
12941 tmp = me.document.createElement('br');
12942 range.insertNode(tmp);
12943 range.setStartBefore(tmp).setCursor();
12944 }
12945 }
12946 }
12947
12948 }
12949 return true;
12950 }
12951
12952 },
12953 //边界在table里不能加分隔线
12954 queryCommandState : function() {
12955 return domUtils.filterNodeList(this.selection.getStartElementPath(),'table') ? -1 : 0;
12956 }
12957 };
12958// me.addListener('delkeyup',function(){
12959// var rng = this.selection.getRange();
12960// if(browser.ie && browser.version > 8){
12961// rng.txtToElmBoundary(true);
12962// if(domUtils.isStartInblock(rng)){
12963// var tmpNode = rng.startContainer;
12964// var pre = tmpNode.previousSibling;
12965// if(pre && domUtils.isTagNode(pre,'hr')){
12966// domUtils.remove(pre);
12967// rng.select();
12968// return;
12969// }
12970// }
12971// }
12972// if(domUtils.isBody(rng.startContainer)){
12973// var hr = rng.startContainer.childNodes[rng.startOffset -1];
12974// if(hr && hr.nodeName == 'HR'){
12975// var next = hr.nextSibling;
12976// if(next){
12977// rng.setStart(next,0)
12978// }else if(hr.previousSibling){
12979// rng.setStartAtLast(hr.previousSibling)
12980// }else{
12981// var p = this.document.createElement('p');
12982// hr.parentNode.insertBefore(p,hr);
12983// domUtils.fillNode(this.document,p);
12984// rng.setStart(p,0);
12985// }
12986// domUtils.remove(hr);
12987// rng.setCursor(false,true);
12988// }
12989// }
12990// })
12991 me.addListener('delkeydown',function(name,evt){
12992 var rng = this.selection.getRange();
12993 rng.txtToElmBoundary(true);
12994 if(domUtils.isStartInblock(rng)){
12995 var tmpNode = rng.startContainer;
12996 var pre = tmpNode.previousSibling;
12997 if(pre && domUtils.isTagNode(pre,'hr')){
12998 domUtils.remove(pre);
12999 rng.select();
13000 domUtils.preventDefault(evt);
13001 return true;
13002
13003 }
13004 }
13005
13006 })
13007};
13008
13009
13010
13011// plugins/time.js
13012/**
13013 * 插入时间和日期
13014 * @file
13015 * @since 1.2.6.1
13016 */
13017
13018/**
13019 * 插入时间,默认格式:12:59:59
13020 * @command time
13021 * @method execCommand
13022 * @param { String } cmd 命令字符串
13023 * @example
13024 * ```javascript
13025 * editor.execCommand( 'time');
13026 * ```
13027 */
13028
13029/**
13030 * 插入日期,默认格式:2013-08-30
13031 * @command date
13032 * @method execCommand
13033 * @param { String } cmd 命令字符串
13034 * @example
13035 * ```javascript
13036 * editor.execCommand( 'date');
13037 * ```
13038 */
13039UE.commands['time'] = UE.commands["date"] = {
13040 execCommand : function(cmd, format){
13041 var date = new Date;
13042
13043 function formatTime(date, format) {
13044 var hh = ('0' + date.getHours()).slice(-2),
13045 ii = ('0' + date.getMinutes()).slice(-2),
13046 ss = ('0' + date.getSeconds()).slice(-2);
13047 format = format || 'hh:ii:ss';
13048 return format.replace(/hh/ig, hh).replace(/ii/ig, ii).replace(/ss/ig, ss);
13049 }
13050 function formatDate(date, format) {
13051 var yyyy = ('000' + date.getFullYear()).slice(-4),
13052 yy = yyyy.slice(-2),
13053 mm = ('0' + (date.getMonth()+1)).slice(-2),
13054 dd = ('0' + date.getDate()).slice(-2);
13055 format = format || 'yyyy-mm-dd';
13056 return format.replace(/yyyy/ig, yyyy).replace(/yy/ig, yy).replace(/mm/ig, mm).replace(/dd/ig, dd);
13057 }
13058
13059 this.execCommand('insertHtml',cmd == "time" ? formatTime(date, format):formatDate(date, format) );
13060 }
13061};
13062
13063
13064// plugins/rowspacing.js
13065/**
13066 * 段前段后间距插件
13067 * @file
13068 * @since 1.2.6.1
13069 */
13070
13071/**
13072 * 设置段间距
13073 * @command rowspacing
13074 * @method execCommand
13075 * @param { String } cmd 命令字符串
13076 * @param { String } value 段间距的值,以px为单位
13077 * @param { String } dir 间距位置,top或bottom,分别表示段前和段后
13078 * @example
13079 * ```javascript
13080 * editor.execCommand( 'rowspacing', '10', 'top' );
13081 * ```
13082 */
13083
13084UE.plugins['rowspacing'] = function(){
13085 var me = this;
13086 me.setOpt({
13087 'rowspacingtop':['5', '10', '15', '20', '25'],
13088 'rowspacingbottom':['5', '10', '15', '20', '25']
13089
13090 });
13091 me.commands['rowspacing'] = {
13092 execCommand : function( cmdName,value,dir ) {
13093 this.execCommand('paragraph','p',{style:'margin-'+dir+':'+value + 'px'});
13094 return true;
13095 },
13096 queryCommandValue : function(cmdName,dir) {
13097 var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),function(node){return domUtils.isBlockElm(node) }),
13098 value;
13099 //trace:1026
13100 if(pN){
13101 value = domUtils.getComputedStyle(pN,'margin-'+dir).replace(/[^\d]/g,'');
13102 return !value ? 0 : value;
13103 }
13104 return 0;
13105
13106 }
13107 };
13108};
13109
13110
13111
13112
13113// plugins/lineheight.js
13114/**
13115 * 设置行内间距
13116 * @file
13117 * @since 1.2.6.1
13118 */
13119UE.plugins['lineheight'] = function(){
13120 var me = this;
13121 me.setOpt({'lineheight':['1', '1.5','1.75','2', '3', '4', '5']});
13122
13123 /**
13124 * 行距
13125 * @command lineheight
13126 * @method execCommand
13127 * @param { String } cmdName 命令字符串
13128 * @param { String } value 传入的行高值, 该值是当前字体的倍数, 例如: 1.5, 1.75
13129 * @example
13130 * ```javascript
13131 * editor.execCommand( 'lineheight', 1.5);
13132 * ```
13133 */
13134 /**
13135 * 查询当前选区内容的行高大小
13136 * @command lineheight
13137 * @method queryCommandValue
13138 * @param { String } cmd 命令字符串
13139 * @return { String } 返回当前行高大小
13140 * @example
13141 * ```javascript
13142 * editor.queryCommandValue( 'lineheight' );
13143 * ```
13144 */
13145
13146 me.commands['lineheight'] = {
13147 execCommand : function( cmdName,value ) {
13148 this.execCommand('paragraph','p',{style:'line-height:'+ (value == "1" ? "normal" : value + 'em') });
13149 return true;
13150 },
13151 queryCommandValue : function() {
13152 var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),function(node){return domUtils.isBlockElm(node)});
13153 if(pN){
13154 var value = domUtils.getComputedStyle(pN,'line-height');
13155 return value == 'normal' ? 1 : value.replace(/[^\d.]*/ig,"");
13156 }
13157 }
13158 };
13159};
13160
13161
13162
13163
13164// plugins/insertcode.js
13165/**
13166 * 插入代码插件
13167 * @file
13168 * @since 1.2.6.1
13169 */
13170
13171UE.plugins['insertcode'] = function() {
13172 var me = this;
13173 me.ready(function(){
13174 utils.cssRule('pre','pre{margin:.5em 0;padding:.4em .6em;border-radius:8px;background:#f8f8f8;}',
13175 me.document)
13176 });
13177 me.setOpt('insertcode',{
13178 'as3':'ActionScript3',
13179 'bash':'Bash/Shell',
13180 'cpp':'C/C++',
13181 'css':'Css',
13182 'cf':'CodeFunction',
13183 'c#':'C#',
13184 'delphi':'Delphi',
13185 'diff':'Diff',
13186 'erlang':'Erlang',
13187 'groovy':'Groovy',
13188 'html':'Html',
13189 'java':'Java',
13190 'jfx':'JavaFx',
13191 'js':'Javascript',
13192 'pl':'Perl',
13193 'php':'Php',
13194 'plain':'Plain Text',
13195 'ps':'PowerShell',
13196 'python':'Python',
13197 'ruby':'Ruby',
13198 'scala':'Scala',
13199 'sql':'Sql',
13200 'vb':'Vb',
13201 'xml':'Xml'
13202 });
13203
13204 /**
13205 * 插入代码
13206 * @command insertcode
13207 * @method execCommand
13208 * @param { String } cmd 命令字符串
13209 * @param { String } lang 插入代码的语言
13210 * @example
13211 * ```javascript
13212 * editor.execCommand( 'insertcode', 'javascript' );
13213 * ```
13214 */
13215
13216 /**
13217 * 如果选区所在位置是插入插入代码区域,返回代码的语言
13218 * @command insertcode
13219 * @method queryCommandValue
13220 * @param { String } cmd 命令字符串
13221 * @return { String } 返回代码的语言
13222 * @example
13223 * ```javascript
13224 * editor.queryCommandValue( 'insertcode' );
13225 * ```
13226 */
13227
13228 me.commands['insertcode'] = {
13229 execCommand : function(cmd,lang){
13230 var me = this,
13231 rng = me.selection.getRange(),
13232 pre = domUtils.findParentByTagName(rng.startContainer,'pre',true);
13233 if(pre){
13234 pre.className = 'brush:'+lang+';toolbar:false;';
13235 }else{
13236 var code = '';
13237 if(rng.collapsed){
13238 code = browser.ie && browser.ie11below ? (browser.version <= 8 ? '&nbsp;':''):'<br/>';
13239 }else{
13240 var frag = rng.extractContents();
13241 var div = me.document.createElement('div');
13242 div.appendChild(frag);
13243
13244 utils.each(UE.filterNode(UE.htmlparser(div.innerHTML.replace(/[\r\t]/g,'')),me.options.filterTxtRules).children,function(node){
13245 if(browser.ie && browser.ie11below && browser.version > 8){
13246
13247 if(node.type =='element'){
13248 if(node.tagName == 'br'){
13249 code += '\n'
13250 }else if(!dtd.$empty[node.tagName]){
13251 utils.each(node.children,function(cn){
13252 if(cn.type =='element'){
13253 if(cn.tagName == 'br'){
13254 code += '\n'
13255 }else if(!dtd.$empty[node.tagName]){
13256 code += cn.innerText();
13257 }
13258 }else{
13259 code += cn.data
13260 }
13261 })
13262 if(!/\n$/.test(code)){
13263 code += '\n';
13264 }
13265 }
13266 }else{
13267 code += node.data + '\n'
13268 }
13269 if(!node.nextSibling() && /\n$/.test(code)){
13270 code = code.replace(/\n$/,'');
13271 }
13272 }else{
13273 if(browser.ie && browser.ie11below){
13274
13275 if(node.type =='element'){
13276 if(node.tagName == 'br'){
13277 code += '<br>'
13278 }else if(!dtd.$empty[node.tagName]){
13279 utils.each(node.children,function(cn){
13280 if(cn.type =='element'){
13281 if(cn.tagName == 'br'){
13282 code += '<br>'
13283 }else if(!dtd.$empty[node.tagName]){
13284 code += cn.innerText();
13285 }
13286 }else{
13287 code += cn.data
13288 }
13289 });
13290 if(!/br>$/.test(code)){
13291 code += '<br>';
13292 }
13293 }
13294 }else{
13295 code += node.data + '<br>'
13296 }
13297 if(!node.nextSibling() && /<br>$/.test(code)){
13298 code = code.replace(/<br>$/,'');
13299 }
13300
13301 }else{
13302 code += (node.type == 'element' ? (dtd.$empty[node.tagName] ? '' : node.innerText()) : node.data);
13303 if(!/br\/?\s*>$/.test(code)){
13304 if(!node.nextSibling())
13305 return;
13306 code += '<br>'
13307 }
13308 }
13309
13310 }
13311
13312 });
13313 }
13314 me.execCommand('inserthtml','<pre id="coder"class="brush:'+lang+';toolbar:false">'+code+'</pre>',true);
13315
13316 pre = me.document.getElementById('coder');
13317 domUtils.removeAttributes(pre,'id');
13318 var tmpNode = pre.previousSibling;
13319
13320 if(tmpNode && (tmpNode.nodeType == 3 && tmpNode.nodeValue.length == 1 && browser.ie && browser.version == 6 || domUtils.isEmptyBlock(tmpNode))){
13321
13322 domUtils.remove(tmpNode)
13323 }
13324 var rng = me.selection.getRange();
13325 if(domUtils.isEmptyBlock(pre)){
13326 rng.setStart(pre,0).setCursor(false,true)
13327 }else{
13328 rng.selectNodeContents(pre).select()
13329 }
13330 }
13331
13332
13333
13334 },
13335 queryCommandValue : function(){
13336 var path = this.selection.getStartElementPath();
13337 var lang = '';
13338 utils.each(path,function(node){
13339 if(node.nodeName =='PRE'){
13340 var match = node.className.match(/brush:([^;]+)/);
13341 lang = match && match[1] ? match[1] : '';
13342 return false;
13343 }
13344 });
13345 return lang;
13346 }
13347 };
13348
13349 me.addInputRule(function(root){
13350 utils.each(root.getNodesByTagName('pre'),function(pre){
13351 var brs = pre.getNodesByTagName('br');
13352 if(brs.length){
13353 browser.ie && browser.ie11below && browser.version > 8 && utils.each(brs,function(br){
13354 var txt = UE.uNode.createText('\n');
13355 br.parentNode.insertBefore(txt,br);
13356 br.parentNode.removeChild(br);
13357 });
13358 return;
13359 }
13360 if(browser.ie && browser.ie11below && browser.version > 8)
13361 return;
13362 var code = pre.innerText().split(/\n/);
13363 pre.innerHTML('');
13364 utils.each(code,function(c){
13365 if(c.length){
13366 pre.appendChild(UE.uNode.createText(c));
13367 }
13368 pre.appendChild(UE.uNode.createElement('br'))
13369 })
13370 })
13371 });
13372 me.addOutputRule(function(root){
13373 utils.each(root.getNodesByTagName('pre'),function(pre){
13374 var code = '';
13375 utils.each(pre.children,function(n){
13376 if(n.type == 'text'){
13377 //在ie下文本内容有可能末尾带有\n要去掉
13378 //trace:3396
13379 code += n.data.replace(/[ ]/g,'&nbsp;').replace(/\n$/,'');
13380 }else{
13381 if(n.tagName == 'br'){
13382 code += '\n'
13383 }else{
13384 code += (!dtd.$empty[n.tagName] ? '' : n.innerText());
13385 }
13386
13387 }
13388
13389 });
13390
13391 pre.innerText(code.replace(/(&nbsp;|\n)+$/,''))
13392 })
13393 });
13394 //不需要判断highlight的command列表
13395 me.notNeedCodeQuery ={
13396 help:1,
13397 undo:1,
13398 redo:1,
13399 source:1,
13400 print:1,
13401 searchreplace:1,
13402 fullscreen:1,
13403 preview:1,
13404 insertparagraph:1,
13405 elementpath:1,
13406 insertcode:1,
13407 inserthtml:1,
13408 selectall:1
13409 };
13410 //将queyCommamndState重置
13411 var orgQuery = me.queryCommandState;
13412 me.queryCommandState = function(cmd){
13413 var me = this;
13414
13415 if(!me.notNeedCodeQuery[cmd.toLowerCase()] && me.selection && me.queryCommandValue('insertcode')){
13416 return -1;
13417 }
13418 return UE.Editor.prototype.queryCommandState.apply(this,arguments)
13419 };
13420 me.addListener('beforeenterkeydown',function(){
13421 var rng = me.selection.getRange();
13422 var pre = domUtils.findParentByTagName(rng.startContainer,'pre',true);
13423 if(pre){
13424 me.fireEvent('saveScene');
13425 if(!rng.collapsed){
13426 rng.deleteContents();
13427 }
13428 if(!browser.ie || browser.ie9above){
13429 var tmpNode = me.document.createElement('br'),pre;
13430 rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true);
13431 var next = tmpNode.nextSibling;
13432 if(!next && (!browser.ie || browser.version > 10)){
13433 rng.insertNode(tmpNode.cloneNode(false));
13434 }else{
13435 rng.setStartAfter(tmpNode);
13436 }
13437 pre = tmpNode.previousSibling;
13438 var tmp;
13439 while(pre ){
13440 tmp = pre;
13441 pre = pre.previousSibling;
13442 if(!pre || pre.nodeName == 'BR'){
13443 pre = tmp;
13444 break;
13445 }
13446 }
13447 if(pre){
13448 var str = '';
13449 while(pre && pre.nodeName != 'BR' && new RegExp('^[\\s'+domUtils.fillChar+']*$').test(pre.nodeValue)){
13450 str += pre.nodeValue;
13451 pre = pre.nextSibling;
13452 }
13453 if(pre.nodeName != 'BR'){
13454 var match = pre.nodeValue.match(new RegExp('^([\\s'+domUtils.fillChar+']+)'));
13455 if(match && match[1]){
13456 str += match[1]
13457 }
13458
13459 }
13460 if(str){
13461 str = me.document.createTextNode(str);
13462 rng.insertNode(str).setStartAfter(str);
13463 }
13464 }
13465 rng.collapse(true).select(true);
13466 }else{
13467 if(browser.version > 8){
13468
13469 var txt = me.document.createTextNode('\n');
13470 var start = rng.startContainer;
13471 if(rng.startOffset == 0){
13472 var preNode = start.previousSibling;
13473 if(preNode){
13474 rng.insertNode(txt);
13475 var fillchar = me.document.createTextNode(' ');
13476 rng.setStartAfter(txt).insertNode(fillchar).setStart(fillchar,0).collapse(true).select(true)
13477 }
13478 }else{
13479 rng.insertNode(txt).setStartAfter(txt);
13480 var fillchar = me.document.createTextNode(' ');
13481 start = rng.startContainer.childNodes[rng.startOffset];
13482 if(start && !/^\n/.test(start.nodeValue)){
13483 rng.setStartBefore(txt)
13484 }
13485 rng.insertNode(fillchar).setStart(fillchar,0).collapse(true).select(true)
13486 }
13487
13488 }else{
13489 var tmpNode = me.document.createElement('br');
13490 rng.insertNode(tmpNode);
13491 rng.insertNode(me.document.createTextNode(domUtils.fillChar));
13492 rng.setStartAfter(tmpNode);
13493 pre = tmpNode.previousSibling;
13494 var tmp;
13495 while(pre ){
13496 tmp = pre;
13497 pre = pre.previousSibling;
13498 if(!pre || pre.nodeName == 'BR'){
13499 pre = tmp;
13500 break;
13501 }
13502 }
13503 if(pre){
13504 var str = '';
13505 while(pre && pre.nodeName != 'BR' && new RegExp('^[ '+domUtils.fillChar+']*$').test(pre.nodeValue)){
13506 str += pre.nodeValue;
13507 pre = pre.nextSibling;
13508 }
13509 if(pre.nodeName != 'BR'){
13510 var match = pre.nodeValue.match(new RegExp('^([ '+domUtils.fillChar+']+)'));
13511 if(match && match[1]){
13512 str += match[1]
13513 }
13514
13515 }
13516
13517 str = me.document.createTextNode(str);
13518 rng.insertNode(str).setStartAfter(str);
13519 }
13520 rng.collapse(true).select();
13521 }
13522
13523
13524 }
13525 me.fireEvent('saveScene');
13526 return true;
13527 }
13528
13529
13530 });
13531
13532 me.addListener('tabkeydown',function(cmd,evt){
13533 var rng = me.selection.getRange();
13534 var pre = domUtils.findParentByTagName(rng.startContainer,'pre',true);
13535 if(pre){
13536 me.fireEvent('saveScene');
13537 if(evt.shiftKey){
13538
13539 }else{
13540 if(!rng.collapsed){
13541 var bk = rng.createBookmark();
13542 var start = bk.start.previousSibling;
13543
13544 while(start){
13545 if(pre.firstChild === start && !domUtils.isBr(start)){
13546 pre.insertBefore(me.document.createTextNode(' '),start);
13547
13548 break;
13549 }
13550 if(domUtils.isBr(start)){
13551 pre.insertBefore(me.document.createTextNode(' '),start.nextSibling);
13552
13553 break;
13554 }
13555 start = start.previousSibling;
13556 }
13557 var end = bk.end;
13558 start = bk.start.nextSibling;
13559 if(pre.firstChild === bk.start){
13560 pre.insertBefore(me.document.createTextNode(' '),start.nextSibling)
13561
13562 }
13563 while(start && start !== end){
13564 if(domUtils.isBr(start) && start.nextSibling){
13565 if(start.nextSibling === end){
13566 break;
13567 }
13568 pre.insertBefore(me.document.createTextNode(' '),start.nextSibling)
13569 }
13570
13571 start = start.nextSibling;
13572 }
13573 rng.moveToBookmark(bk).select();
13574 }else{
13575 var tmpNode = me.document.createTextNode(' ');
13576 rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true).select(true);
13577 }
13578 }
13579
13580
13581 me.fireEvent('saveScene');
13582 return true;
13583 }
13584
13585
13586 });
13587
13588
13589 me.addListener('beforeinserthtml',function(evtName,html){
13590 var me = this,
13591 rng = me.selection.getRange(),
13592 pre = domUtils.findParentByTagName(rng.startContainer,'pre',true);
13593 if(pre){
13594 if(!rng.collapsed){
13595 rng.deleteContents()
13596 }
13597 var htmlstr = '';
13598 if(browser.ie && browser.version > 8){
13599
13600 utils.each(UE.filterNode(UE.htmlparser(html),me.options.filterTxtRules).children,function(node){
13601 if(node.type =='element'){
13602 if(node.tagName == 'br'){
13603 htmlstr += '\n'
13604 }else if(!dtd.$empty[node.tagName]){
13605 utils.each(node.children,function(cn){
13606 if(cn.type =='element'){
13607 if(cn.tagName == 'br'){
13608 htmlstr += '\n'
13609 }else if(!dtd.$empty[node.tagName]){
13610 htmlstr += cn.innerText();
13611 }
13612 }else{
13613 htmlstr += cn.data
13614 }
13615 })
13616 if(!/\n$/.test(htmlstr)){
13617 htmlstr += '\n';
13618 }
13619 }
13620 }else{
13621 htmlstr += node.data + '\n'
13622 }
13623 if(!node.nextSibling() && /\n$/.test(htmlstr)){
13624 htmlstr = htmlstr.replace(/\n$/,'');
13625 }
13626 });
13627 var tmpNode = me.document.createTextNode(utils.html(htmlstr.replace(/&nbsp;/g,' ')));
13628 rng.insertNode(tmpNode).selectNode(tmpNode).select();
13629 }else{
13630 var frag = me.document.createDocumentFragment();
13631
13632 utils.each(UE.filterNode(UE.htmlparser(html),me.options.filterTxtRules).children,function(node){
13633 if(node.type =='element'){
13634 if(node.tagName == 'br'){
13635 frag.appendChild(me.document.createElement('br'))
13636 }else if(!dtd.$empty[node.tagName]){
13637 utils.each(node.children,function(cn){
13638 if(cn.type =='element'){
13639 if(cn.tagName == 'br'){
13640
13641 frag.appendChild(me.document.createElement('br'))
13642 }else if(!dtd.$empty[node.tagName]){
13643 frag.appendChild(me.document.createTextNode(utils.html(cn.innerText().replace(/&nbsp;/g,' '))));
13644
13645 }
13646 }else{
13647 frag.appendChild(me.document.createTextNode(utils.html( cn.data.replace(/&nbsp;/g,' '))));
13648
13649 }
13650 })
13651 if(frag.lastChild.nodeName != 'BR'){
13652 frag.appendChild(me.document.createElement('br'))
13653 }
13654 }
13655 }else{
13656 frag.appendChild(me.document.createTextNode(utils.html( node.data.replace(/&nbsp;/g,' '))));
13657 }
13658 if(!node.nextSibling() && frag.lastChild.nodeName == 'BR'){
13659 frag.removeChild(frag.lastChild)
13660 }
13661
13662
13663 });
13664 rng.insertNode(frag).select();
13665
13666 }
13667
13668 return true;
13669 }
13670 });
13671 //方向键的处理
13672 me.addListener('keydown',function(cmd,evt){
13673 var me = this,keyCode = evt.keyCode || evt.which;
13674 if(keyCode == 40){
13675 var rng = me.selection.getRange(),pre,start = rng.startContainer;
13676 if(rng.collapsed && (pre = domUtils.findParentByTagName(rng.startContainer,'pre',true)) && !pre.nextSibling){
13677 var last = pre.lastChild
13678 while(last && last.nodeName == 'BR'){
13679 last = last.previousSibling;
13680 }
13681 if(last === start || rng.startContainer === pre && rng.startOffset == pre.childNodes.length){
13682 me.execCommand('insertparagraph');
13683 domUtils.preventDefault(evt)
13684 }
13685
13686 }
13687 }
13688 });
13689 //trace:3395
13690 me.addListener('delkeydown',function(type,evt){
13691 var rng = this.selection.getRange();
13692 rng.txtToElmBoundary(true);
13693 var start = rng.startContainer;
13694 if(domUtils.isTagNode(start,'pre') && rng.collapsed && domUtils.isStartInblock(rng)){
13695 var p = me.document.createElement('p');
13696 domUtils.fillNode(me.document,p);
13697 start.parentNode.insertBefore(p,start);
13698 domUtils.remove(start);
13699 rng.setStart(p,0).setCursor(false,true);
13700 domUtils.preventDefault(evt);
13701 return true;
13702 }
13703 })
13704};
13705
13706
13707// plugins/cleardoc.js
13708/**
13709 * 清空文档插件
13710 * @file
13711 * @since 1.2.6.1
13712 */
13713
13714/**
13715 * 清空文档
13716 * @command cleardoc
13717 * @method execCommand
13718 * @param { String } cmd 命令字符串
13719 * @example
13720 * ```javascript
13721 * //editor 是编辑器实例
13722 * editor.execCommand('cleardoc');
13723 * ```
13724 */
13725
13726UE.commands['cleardoc'] = {
13727 execCommand : function( cmdName) {
13728 var me = this,
13729 enterTag = me.options.enterTag,
13730 range = me.selection.getRange();
13731 if(enterTag == "br"){
13732 me.body.innerHTML = "<br/>";
13733 range.setStart(me.body,0).setCursor();
13734 }else{
13735 me.body.innerHTML = "<p>"+(ie ? "" : "<br/>")+"</p>";
13736 range.setStart(me.body.firstChild,0).setCursor(false,true);
13737 }
13738 setTimeout(function(){
13739 me.fireEvent("clearDoc");
13740 },0);
13741
13742 }
13743};
13744
13745
13746
13747// plugins/anchor.js
13748/**
13749 * 锚点插件,为UEditor提供插入锚点支持
13750 * @file
13751 * @since 1.2.6.1
13752 */
13753UE.plugin.register('anchor', function (){
13754
13755 return {
13756 bindEvents:{
13757 'ready':function(){
13758 utils.cssRule('anchor',
13759 '.anchorclass{background: url(\''
13760 + this.options.themePath
13761 + this.options.theme +'/images/anchor.gif\') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 15px;}',
13762 this.document);
13763 }
13764 },
13765 outputRule: function(root){
13766 utils.each(root.getNodesByTagName('img'),function(a){
13767 var val;
13768 if(val = a.getAttr('anchorname')){
13769 a.tagName = 'a';
13770 a.setAttr({
13771 anchorname : '',
13772 name : val,
13773 'class' : ''
13774 })
13775 }
13776 })
13777 },
13778 inputRule:function(root){
13779 utils.each(root.getNodesByTagName('a'),function(a){
13780 var val;
13781 if((val = a.getAttr('name')) && !a.getAttr('href')){
13782 a.tagName = 'img';
13783 a.setAttr({
13784 anchorname :a.getAttr('name'),
13785 'class' : 'anchorclass'
13786 });
13787 a.setAttr('name')
13788
13789 }
13790 })
13791
13792 },
13793 commands:{
13794 /**
13795 * 插入锚点
13796 * @command anchor
13797 * @method execCommand
13798 * @param { String } cmd 命令字符串
13799 * @param { String } name 锚点名称字符串
13800 * @example
13801 * ```javascript
13802 * //editor 是编辑器实例
13803 * editor.execCommand('anchor', 'anchor1');
13804 * ```
13805 */
13806 'anchor':{
13807 execCommand:function (cmd, name) {
13808 var range = this.selection.getRange(),img = range.getClosedNode();
13809 if (img && img.getAttribute('anchorname')) {
13810 if (name) {
13811 img.setAttribute('anchorname', name);
13812 } else {
13813 range.setStartBefore(img).setCursor();
13814 domUtils.remove(img);
13815 }
13816 } else {
13817 if (name) {
13818 //只在选区的开始插入
13819 var anchor = this.document.createElement('img');
13820 range.collapse(true);
13821 domUtils.setAttributes(anchor,{
13822 'anchorname':name,
13823 'class':'anchorclass'
13824 });
13825 range.insertNode(anchor).setStartAfter(anchor).setCursor(false,true);
13826 }
13827 }
13828 }
13829 }
13830 }
13831 }
13832});
13833
13834
13835// plugins/wordcount.js
13836///import core
13837///commands 字数统计
13838///commandsName WordCount,wordCount
13839///commandsTitle 字数统计
13840/*
13841 * Created by JetBrains WebStorm.
13842 * User: taoqili
13843 * Date: 11-9-7
13844 * Time: 下午8:18
13845 * To change this template use File | Settings | File Templates.
13846 */
13847
13848UE.plugins['wordcount'] = function(){
13849 var me = this;
13850 me.setOpt('wordCount',true);
13851 me.addListener('contentchange',function(){
13852 me.fireEvent('wordcount');
13853 });
13854 var timer;
13855 me.addListener('ready',function(){
13856 var me = this;
13857 domUtils.on(me.body,"keyup",function(evt){
13858 var code = evt.keyCode||evt.which,
13859 //忽略的按键,ctr,alt,shift,方向键
13860 ignores = {"16":1,"18":1,"20":1,"37":1,"38":1,"39":1,"40":1};
13861 if(code in ignores) return;
13862 clearTimeout(timer);
13863 timer = setTimeout(function(){
13864 me.fireEvent('wordcount');
13865 },200)
13866 })
13867 });
13868};
13869
13870
13871// plugins/pagebreak.js
13872/**
13873 * 分页功能插件
13874 * @file
13875 * @since 1.2.6.1
13876 */
13877UE.plugins['pagebreak'] = function () {
13878 var me = this,
13879 notBreakTags = ['td'];
13880 me.setOpt('pageBreakTag','_ueditor_page_break_tag_');
13881
13882 function fillNode(node){
13883 if(domUtils.isEmptyBlock(node)){
13884 var firstChild = node.firstChild,tmpNode;
13885
13886 while(firstChild && firstChild.nodeType == 1 && domUtils.isEmptyBlock(firstChild)){
13887 tmpNode = firstChild;
13888 firstChild = firstChild.firstChild;
13889 }
13890 !tmpNode && (tmpNode = node);
13891 domUtils.fillNode(me.document,tmpNode);
13892 }
13893 }
13894 //分页符样式添加
13895
13896 me.ready(function(){
13897 utils.cssRule('pagebreak','.pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}',me.document);
13898 });
13899 function isHr(node){
13900 return node && node.nodeType == 1 && node.tagName == 'HR' && node.className == 'pagebreak';
13901 }
13902 me.addInputRule(function(root){
13903 root.traversal(function(node){
13904 if(node.type == 'text' && node.data == me.options.pageBreakTag){
13905 var hr = UE.uNode.createElement('<hr class="pagebreak" noshade="noshade" size="5" style="-webkit-user-select: none;">');
13906 node.parentNode.insertBefore(hr,node);
13907 node.parentNode.removeChild(node)
13908 }
13909 })
13910 });
13911 me.addOutputRule(function(node){
13912 utils.each(node.getNodesByTagName('hr'),function(n){
13913 if(n.getAttr('class') == 'pagebreak'){
13914 var txt = UE.uNode.createText(me.options.pageBreakTag);
13915 n.parentNode.insertBefore(txt,n);
13916 n.parentNode.removeChild(n);
13917 }
13918 })
13919
13920 });
13921
13922 /**
13923 * 插入分页符
13924 * @command pagebreak
13925 * @method execCommand
13926 * @param { String } cmd 命令字符串
13927 * @remind 在表格中插入分页符会把表格切分成两部分
13928 * @remind 获取编辑器内的数据时, 编辑器会把分页符转换成“_ueditor_page_break_tag_”字符串,
13929 * 以便于提交数据到服务器端后处理分页。
13930 * @example
13931 * ```javascript
13932 * editor.execCommand( 'pagebreak'); //插入一个hr标签,带有样式类名pagebreak
13933 * ```
13934 */
13935
13936 me.commands['pagebreak'] = {
13937 execCommand:function () {
13938 var range = me.selection.getRange(),hr = me.document.createElement('hr');
13939 domUtils.setAttributes(hr,{
13940 'class' : 'pagebreak',
13941 noshade:"noshade",
13942 size:"5"
13943 });
13944 domUtils.unSelectable(hr);
13945 //table单独处理
13946 var node = domUtils.findParentByTagName(range.startContainer, notBreakTags, true),
13947
13948 parents = [], pN;
13949 if (node) {
13950 switch (node.tagName) {
13951 case 'TD':
13952 pN = node.parentNode;
13953 if (!pN.previousSibling) {
13954 var table = domUtils.findParentByTagName(pN, 'table');
13955// var tableWrapDiv = table.parentNode;
13956// if(tableWrapDiv && tableWrapDiv.nodeType == 1
13957// && tableWrapDiv.tagName == 'DIV'
13958// && tableWrapDiv.getAttribute('dropdrag')
13959// ){
13960// domUtils.remove(tableWrapDiv,true);
13961// }
13962 table.parentNode.insertBefore(hr, table);
13963 parents = domUtils.findParents(hr, true);
13964
13965 } else {
13966 pN.parentNode.insertBefore(hr, pN);
13967 parents = domUtils.findParents(hr);
13968
13969 }
13970 pN = parents[1];
13971 if (hr !== pN) {
13972 domUtils.breakParent(hr, pN);
13973
13974 }
13975 //table要重写绑定一下拖拽
13976 me.fireEvent('afteradjusttable',me.document);
13977 }
13978
13979 } else {
13980
13981 if (!range.collapsed) {
13982 range.deleteContents();
13983 var start = range.startContainer;
13984 while ( !domUtils.isBody(start) && domUtils.isBlockElm(start) && domUtils.isEmptyNode(start)) {
13985 range.setStartBefore(start).collapse(true);
13986 domUtils.remove(start);
13987 start = range.startContainer;
13988 }
13989
13990 }
13991 range.insertNode(hr);
13992
13993 var pN = hr.parentNode, nextNode;
13994 while (!domUtils.isBody(pN)) {
13995 domUtils.breakParent(hr, pN);
13996 nextNode = hr.nextSibling;
13997 if (nextNode && domUtils.isEmptyBlock(nextNode)) {
13998 domUtils.remove(nextNode);
13999 }
14000 pN = hr.parentNode;
14001 }
14002 nextNode = hr.nextSibling;
14003 var pre = hr.previousSibling;
14004 if(isHr(pre)){
14005 domUtils.remove(pre);
14006 }else{
14007 pre && fillNode(pre);
14008 }
14009
14010 if(!nextNode){
14011 var p = me.document.createElement('p');
14012
14013 hr.parentNode.appendChild(p);
14014 domUtils.fillNode(me.document,p);
14015 range.setStart(p,0).collapse(true);
14016 }else{
14017 if(isHr(nextNode)){
14018 domUtils.remove(nextNode);
14019 }else{
14020 fillNode(nextNode);
14021 }
14022 range.setEndAfter(hr).collapse(false);
14023 }
14024
14025 range.select(true);
14026
14027 }
14028
14029 }
14030 };
14031};
14032
14033// plugins/wordimage.js
14034///import core
14035///commands 本地图片引导上传
14036///commandsName WordImage
14037///commandsTitle 本地图片引导上传
14038///commandsDialog dialogs\wordimage
14039
14040UE.plugin.register('wordimage',function(){
14041 var me = this,
14042 images = [];
14043 return {
14044 commands : {
14045 'wordimage':{
14046 execCommand:function () {
14047 var images = domUtils.getElementsByTagName(me.body, "img");
14048 var urlList = [];
14049 for (var i = 0, ci; ci = images[i++];) {
14050 var url = ci.getAttribute("word_img");
14051 url && urlList.push(url);
14052 }
14053 return urlList;
14054 },
14055 queryCommandState:function () {
14056 images = domUtils.getElementsByTagName(me.body, "img");
14057 for (var i = 0, ci; ci = images[i++];) {
14058 if (ci.getAttribute("word_img")) {
14059 return 1;
14060 }
14061 }
14062 return -1;
14063 },
14064 notNeedUndo:true
14065 }
14066 },
14067 inputRule : function (root) {
14068 utils.each(root.getNodesByTagName('img'), function (img) {
14069 var attrs = img.attrs,
14070 flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43,
14071 opt = me.options,
14072 src = opt.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif';
14073 if (attrs['src'] && /^(?:(file:\/+))/.test(attrs['src'])) {
14074 img.setAttr({
14075 width:attrs.width,
14076 height:attrs.height,
14077 alt:attrs.alt,
14078 word_img: attrs.src,
14079 src:src,
14080 'style':'background:url(' + ( flag ? opt.themePath + opt.theme + '/images/word.gif' : opt.langPath + opt.lang + '/images/localimage.png') + ') no-repeat center center;border:1px solid #ddd'
14081 })
14082 }
14083 })
14084 }
14085 }
14086});
14087
14088// plugins/dragdrop.js
14089UE.plugins['dragdrop'] = function (){
14090
14091 var me = this;
14092 me.ready(function(){
14093 domUtils.on(this.body,'dragend',function(){
14094 var rng = me.selection.getRange();
14095 var node = rng.getClosedNode()||me.selection.getStart();
14096
14097 if(node && node.tagName == 'IMG'){
14098
14099 var pre = node.previousSibling,next;
14100 while(next = node.nextSibling){
14101 if(next.nodeType == 1 && next.tagName == 'SPAN' && !next.firstChild){
14102 domUtils.remove(next)
14103 }else{
14104 break;
14105 }
14106 }
14107
14108
14109 if((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre) || !pre) && (!next || next && !domUtils.isEmptyBlock(next))){
14110 if(pre && pre.tagName == 'P' && !domUtils.isEmptyBlock(pre)){
14111 pre.appendChild(node);
14112 domUtils.moveChild(next,pre);
14113 domUtils.remove(next);
14114 }else if(next && next.tagName == 'P' && !domUtils.isEmptyBlock(next)){
14115 next.insertBefore(node,next.firstChild);
14116 }
14117
14118 if(pre && pre.tagName == 'P' && domUtils.isEmptyBlock(pre)){
14119 domUtils.remove(pre)
14120 }
14121 if(next && next.tagName == 'P' && domUtils.isEmptyBlock(next)){
14122 domUtils.remove(next)
14123 }
14124 rng.selectNode(node).select();
14125 me.fireEvent('saveScene');
14126
14127 }
14128
14129 }
14130
14131 })
14132 });
14133 me.addListener('keyup', function(type, evt) {
14134 var keyCode = evt.keyCode || evt.which;
14135 if (keyCode == 13) {
14136 var rng = me.selection.getRange(),node;
14137 if(node = domUtils.findParentByTagName(rng.startContainer,'p',true)){
14138 if(domUtils.getComputedStyle(node,'text-align') == 'center'){
14139 domUtils.removeStyle(node,'text-align')
14140 }
14141 }
14142 }
14143 })
14144};
14145
14146
14147// plugins/undo.js
14148/**
14149 * undo redo
14150 * @file
14151 * @since 1.2.6.1
14152 */
14153
14154/**
14155 * 撤销上一次执行的命令
14156 * @command undo
14157 * @method execCommand
14158 * @param { String } cmd 命令字符串
14159 * @example
14160 * ```javascript
14161 * editor.execCommand( 'undo' );
14162 * ```
14163 */
14164
14165/**
14166 * 重做上一次执行的命令
14167 * @command redo
14168 * @method execCommand
14169 * @param { String } cmd 命令字符串
14170 * @example
14171 * ```javascript
14172 * editor.execCommand( 'redo' );
14173 * ```
14174 */
14175
14176UE.plugins['undo'] = function () {
14177 var saveSceneTimer;
14178 var me = this,
14179 maxUndoCount = me.options.maxUndoCount || 20,
14180 maxInputCount = me.options.maxInputCount || 20,
14181 fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的</hr>
14182 var noNeedFillCharTags = {
14183 ol:1,ul:1,table:1,tbody:1,tr:1,body:1
14184 };
14185 var orgState = me.options.autoClearEmptyNode;
14186 function compareAddr(indexA, indexB) {
14187 if (indexA.length != indexB.length)
14188 return 0;
14189 for (var i = 0, l = indexA.length; i < l; i++) {
14190 if (indexA[i] != indexB[i])
14191 return 0
14192 }
14193 return 1;
14194 }
14195
14196 function compareRangeAddress(rngAddrA, rngAddrB) {
14197 if (rngAddrA.collapsed != rngAddrB.collapsed) {
14198 return 0;
14199 }
14200 if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) {
14201 return 0;
14202 }
14203 return 1;
14204 }
14205
14206 function UndoManager() {
14207 this.list = [];
14208 this.index = 0;
14209 this.hasUndo = false;
14210 this.hasRedo = false;
14211 this.undo = function () {
14212 if (this.hasUndo) {
14213 if (!this.list[this.index - 1] && this.list.length == 1) {
14214 this.reset();
14215 return;
14216 }
14217 while (this.list[this.index].content == this.list[this.index - 1].content) {
14218 this.index--;
14219 if (this.index == 0) {
14220 return this.restore(0);
14221 }
14222 }
14223 this.restore(--this.index);
14224 }
14225 };
14226 this.redo = function () {
14227 if (this.hasRedo) {
14228 while (this.list[this.index].content == this.list[this.index + 1].content) {
14229 this.index++;
14230 if (this.index == this.list.length - 1) {
14231 return this.restore(this.index);
14232 }
14233 }
14234 this.restore(++this.index);
14235 }
14236 };
14237
14238 this.restore = function () {
14239 var me = this.editor;
14240 var scene = this.list[this.index];
14241 var root = UE.htmlparser(scene.content.replace(fillchar, ''));
14242 me.options.autoClearEmptyNode = false;
14243 me.filterInputRule(root);
14244 me.options.autoClearEmptyNode = orgState;
14245 //trace:873
14246 //去掉展位符
14247 me.document.body.innerHTML = root.toHtml();
14248 me.fireEvent('afterscencerestore');
14249 //处理undo后空格不展位的问题
14250 if (browser.ie) {
14251 utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){
14252 if(domUtils.isEmptyNode(node)){
14253 domUtils.fillNode(me.document, node);
14254 }
14255 })
14256 }
14257
14258 try{
14259 var rng = new dom.Range(me.document).moveToAddress(scene.address);
14260 rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]);
14261 }catch(e){}
14262
14263 this.update();
14264 this.clearKey();
14265 //不能把自己reset了
14266 me.fireEvent('reset', true);
14267 };
14268
14269 this.getScene = function () {
14270 var me = this.editor;
14271 var rng = me.selection.getRange(),
14272 rngAddress = rng.createAddress(false,true);
14273 me.fireEvent('beforegetscene');
14274 var root = UE.htmlparser(me.body.innerHTML);
14275 me.options.autoClearEmptyNode = false;
14276 me.filterOutputRule(root);
14277 me.options.autoClearEmptyNode = orgState;
14278 var cont = root.toHtml();
14279 //trace:3461
14280 //这个会引起回退时导致空格丢失的情况
14281// browser.ie && (cont = cont.replace(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\s*/g, '>'));
14282 me.fireEvent('aftergetscene');
14283
14284 return {
14285 address:rngAddress,
14286 content:cont
14287 }
14288 };
14289 this.save = function (notCompareRange,notSetCursor) {
14290 clearTimeout(saveSceneTimer);
14291 var currentScene = this.getScene(notSetCursor),
14292 lastScene = this.list[this.index];
14293
14294 if(lastScene && lastScene.content != currentScene.content){
14295 me.trigger('contentchange')
14296 }
14297 //内容相同位置相同不存
14298 if (lastScene && lastScene.content == currentScene.content &&
14299 ( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) )
14300 ) {
14301 return;
14302 }
14303 this.list = this.list.slice(0, this.index + 1);
14304 this.list.push(currentScene);
14305 //如果大于最大数量了,就把最前的剔除
14306 if (this.list.length > maxUndoCount) {
14307 this.list.shift();
14308 }
14309 this.index = this.list.length - 1;
14310 this.clearKey();
14311 //跟新undo/redo状态
14312 this.update();
14313
14314 };
14315 this.update = function () {
14316 this.hasRedo = !!this.list[this.index + 1];
14317 this.hasUndo = !!this.list[this.index - 1];
14318 };
14319 this.reset = function () {
14320 this.list = [];
14321 this.index = 0;
14322 this.hasUndo = false;
14323 this.hasRedo = false;
14324 this.clearKey();
14325 };
14326 this.clearKey = function () {
14327 keycont = 0;
14328 lastKeyCode = null;
14329 };
14330 }
14331
14332 me.undoManger = new UndoManager();
14333 me.undoManger.editor = me;
14334 function saveScene() {
14335 this.undoManger.save();
14336 }
14337
14338 me.addListener('saveScene', function () {
14339 var args = Array.prototype.splice.call(arguments,1);
14340 this.undoManger.save.apply(this.undoManger,args);
14341 });
14342
14343// me.addListener('beforeexeccommand', saveScene);
14344// me.addListener('afterexeccommand', saveScene);
14345
14346 me.addListener('reset', function (type, exclude) {
14347 if (!exclude) {
14348 this.undoManger.reset();
14349 }
14350 });
14351 me.commands['redo'] = me.commands['undo'] = {
14352 execCommand:function (cmdName) {
14353 this.undoManger[cmdName]();
14354 },
14355 queryCommandState:function (cmdName) {
14356 return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
14357 },
14358 notNeedUndo:1
14359 };
14360
14361 var keys = {
14362 // /*Backspace*/ 8:1, /*Delete*/ 46:1,
14363 /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
14364 37:1, 38:1, 39:1, 40:1
14365
14366 },
14367 keycont = 0,
14368 lastKeyCode;
14369 //输入法状态下不计算字符数
14370 var inputType = false;
14371 me.addListener('ready', function () {
14372 domUtils.on(this.body, 'compositionstart', function () {
14373 inputType = true;
14374 });
14375 domUtils.on(this.body, 'compositionend', function () {
14376 inputType = false;
14377 })
14378 });
14379 //快捷键
14380 me.addshortcutkey({
14381 "Undo":"ctrl+90", //undo
14382 "Redo":"ctrl+89" //redo
14383
14384 });
14385 var isCollapsed = true;
14386 me.addListener('keydown', function (type, evt) {
14387
14388 var me = this;
14389 var keyCode = evt.keyCode || evt.which;
14390 if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
14391 if (inputType)
14392 return;
14393
14394 if(!me.selection.getRange().collapsed){
14395 me.undoManger.save(false,true);
14396 isCollapsed = false;
14397 return;
14398 }
14399 if (me.undoManger.list.length == 0) {
14400 me.undoManger.save(true);
14401 }
14402 clearTimeout(saveSceneTimer);
14403 function save(cont){
14404 cont.undoManger.save(false,true);
14405 cont.fireEvent('selectionchange');
14406 }
14407 saveSceneTimer = setTimeout(function(){
14408 if(inputType){
14409 var interalTimer = setInterval(function(){
14410 if(!inputType){
14411 save(me);
14412 clearInterval(interalTimer)
14413 }
14414 },300)
14415 return;
14416 }
14417 save(me);
14418 },200);
14419
14420 lastKeyCode = keyCode;
14421 keycont++;
14422 if (keycont >= maxInputCount ) {
14423 save(me)
14424 }
14425 }
14426 });
14427 me.addListener('keyup', function (type, evt) {
14428 var keyCode = evt.keyCode || evt.which;
14429 if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
14430 if (inputType)
14431 return;
14432 if(!isCollapsed){
14433 this.undoManger.save(false,true);
14434 isCollapsed = true;
14435 }
14436 }
14437 });
14438 //扩展实例,添加关闭和开启命令undo
14439 me.stopCmdUndo = function(){
14440 me.__hasEnterExecCommand = true;
14441 };
14442 me.startCmdUndo = function(){
14443 me.__hasEnterExecCommand = false;
14444 }
14445};
14446
14447
14448// plugins/copy.js
14449UE.plugin.register('copy', function () {
14450
14451 var me = this;
14452
14453 function initZeroClipboard() {
14454
14455 ZeroClipboard.config({
14456 debug: false,
14457 swfPath: me.options.UEDITOR_HOME_URL + 'third-party/zeroclipboard/ZeroClipboard.swf'
14458 });
14459
14460 var client = me.zeroclipboard = new ZeroClipboard();
14461
14462 // 复制内容
14463 client.on('copy', function (e) {
14464 var client = e.client,
14465 rng = me.selection.getRange(),
14466 div = document.createElement('div');
14467
14468 div.appendChild(rng.cloneContents());
14469 client.setText(div.innerText || div.textContent);
14470 client.setHtml(div.innerHTML);
14471 rng.select();
14472 });
14473 // hover事件传递到target
14474 client.on('mouseover mouseout', function (e) {
14475 var target = e.target;
14476 if (e.type == 'mouseover') {
14477 domUtils.addClass(target, 'edui-state-hover');
14478 } else if (e.type == 'mouseout') {
14479 domUtils.removeClasses(target, 'edui-state-hover');
14480 }
14481 });
14482 // flash加载不成功
14483 client.on('wrongflash noflash', function () {
14484 ZeroClipboard.destroy();
14485 });
14486 }
14487
14488 return {
14489 bindEvents: {
14490 'ready': function () {
14491 if (!browser.ie) {
14492 if (window.ZeroClipboard) {
14493 initZeroClipboard();
14494 } else {
14495 utils.loadFile(document, {
14496 src: me.options.UEDITOR_HOME_URL + "third-party/zeroclipboard/ZeroClipboard.js",
14497 tag: "script",
14498 type: "text/javascript",
14499 defer: "defer"
14500 }, function () {
14501 initZeroClipboard();
14502 });
14503 }
14504 }
14505 }
14506 },
14507 commands: {
14508 'copy': {
14509 execCommand: function (cmd) {
14510 if (!me.document.execCommand('copy')) {
14511 alert(me.getLang('copymsg'));
14512 }
14513 }
14514 }
14515 }
14516 }
14517});
14518
14519
14520// plugins/paste.js
14521///import core
14522///import plugins/inserthtml.js
14523///import plugins/undo.js
14524///import plugins/serialize.js
14525///commands 粘贴
14526///commandsName PastePlain
14527///commandsTitle 纯文本粘贴模式
14528/**
14529 * @description 粘贴
14530 * @author zhanyi
14531 */
14532UE.plugins['paste'] = function () {
14533 function getClipboardData(callback) {
14534 var doc = this.document;
14535 if (doc.getElementById('baidu_pastebin')) {
14536 return;
14537 }
14538 var range = this.selection.getRange(),
14539 bk = range.createBookmark(),
14540 //创建剪贴的容器div
14541 pastebin = doc.createElement('div');
14542 pastebin.id = 'baidu_pastebin';
14543 // Safari 要求div必须有内容,才能粘贴内容进来
14544 browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
14545 doc.body.appendChild(pastebin);
14546 //trace:717 隐藏的span不能得到top
14547 //bk.start.innerHTML = '&nbsp;';
14548 bk.start.style.display = '';
14549 pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
14550 //要在现在光标平行的位置加入,否则会出现跳动的问题
14551 domUtils.getXY(bk.start).y + 'px';
14552
14553 range.selectNodeContents(pastebin).select(true);
14554
14555 setTimeout(function () {
14556 if (browser.webkit) {
14557 for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
14558 if (domUtils.isEmptyNode(pi)) {
14559 domUtils.remove(pi);
14560 } else {
14561 pastebin = pi;
14562 break;
14563 }
14564 }
14565 }
14566 try {
14567 pastebin.parentNode.removeChild(pastebin);
14568 } catch (e) {
14569 }
14570 range.moveToBookmark(bk).select(true);
14571 callback(pastebin);
14572 }, 0);
14573 }
14574
14575 var me = this;
14576
14577 me.setOpt({
14578 retainOnlyLabelPasted : false
14579 });
14580
14581 var txtContent, htmlContent, address;
14582
14583 function getPureHtml(html){
14584 return html.replace(/<(\/?)([\w\-]+)([^>]*)>/gi, function (a, b, tagName, attrs) {
14585 tagName = tagName.toLowerCase();
14586 if ({img: 1}[tagName]) {
14587 return a;
14588 }
14589 attrs = attrs.replace(/([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi, function (str, atr, val) {
14590 if ({
14591 'src': 1,
14592 'href': 1,
14593 'name': 1
14594 }[atr.toLowerCase()]) {
14595 return atr + '=' + val + ' '
14596 }
14597 return ''
14598 });
14599 if ({
14600 'span': 1,
14601 'div': 1
14602 }[tagName]) {
14603 return ''
14604 } else {
14605
14606 return '<' + b + tagName + ' ' + utils.trim(attrs) + '>'
14607 }
14608
14609 });
14610 }
14611 function filter(div) {
14612 var html;
14613 if (div.firstChild) {
14614 //去掉cut中添加的边界值
14615 var nodes = domUtils.getElementsByTagName(div, 'span');
14616 for (var i = 0, ni; ni = nodes[i++];) {
14617 if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
14618 domUtils.remove(ni);
14619 }
14620 }
14621
14622 if (browser.webkit) {
14623
14624 var brs = div.querySelectorAll('div br');
14625 for (var i = 0, bi; bi = brs[i++];) {
14626 var pN = bi.parentNode;
14627 if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
14628 pN.innerHTML = '<p><br/></p>';
14629 domUtils.remove(pN);
14630 }
14631 }
14632 var divs = div.querySelectorAll('#baidu_pastebin');
14633 for (var i = 0, di; di = divs[i++];) {
14634 var tmpP = me.document.createElement('p');
14635 di.parentNode.insertBefore(tmpP, di);
14636 while (di.firstChild) {
14637 tmpP.appendChild(di.firstChild);
14638 }
14639 domUtils.remove(di);
14640 }
14641
14642 var metas = div.querySelectorAll('meta');
14643 for (var i = 0, ci; ci = metas[i++];) {
14644 domUtils.remove(ci);
14645 }
14646
14647 var brs = div.querySelectorAll('br');
14648 for (i = 0; ci = brs[i++];) {
14649 if (/^apple-/i.test(ci.className)) {
14650 domUtils.remove(ci);
14651 }
14652 }
14653 }
14654 if (browser.gecko) {
14655 var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
14656 for (i = 0; ci = dirtyNodes[i++];) {
14657 ci.removeAttribute('_moz_dirty');
14658 }
14659 }
14660 if (!browser.ie) {
14661 var spans = div.querySelectorAll('span.Apple-style-span');
14662 for (var i = 0, ci; ci = spans[i++];) {
14663 domUtils.remove(ci, true);
14664 }
14665 }
14666
14667 //ie下使用innerHTML会产生多余的\r\n字符,也会产生&nbsp;这里过滤掉
14668 html = div.innerHTML;//.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
14669
14670 //过滤word粘贴过来的冗余属性
14671 html = UE.filterWord(html);
14672 //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
14673 var root = UE.htmlparser(html);
14674 //如果给了过滤规则就先进行过滤
14675 if (me.options.filterRules) {
14676 UE.filterNode(root, me.options.filterRules);
14677 }
14678 //执行默认的处理
14679 me.filterInputRule(root);
14680 //针对chrome的处理
14681 if (browser.webkit) {
14682 var br = root.lastChild();
14683 if (br && br.type == 'element' && br.tagName == 'br') {
14684 root.removeChild(br)
14685 }
14686 utils.each(me.body.querySelectorAll('div'), function (node) {
14687 if (domUtils.isEmptyBlock(node)) {
14688 domUtils.remove(node,true)
14689 }
14690 })
14691 }
14692 html = {'html': root.toHtml()};
14693 me.fireEvent('beforepaste', html, root);
14694 //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
14695 if(!html.html){
14696 return;
14697 }
14698 root = UE.htmlparser(html.html,true);
14699 //如果开启了纯文本模式
14700 if (me.queryCommandState('pasteplain') === 1) {
14701 me.execCommand('insertHtml', UE.filterNode(root, me.options.filterTxtRules).toHtml(), true);
14702 } else {
14703 //文本模式
14704 UE.filterNode(root, me.options.filterTxtRules);
14705 txtContent = root.toHtml();
14706 //完全模式
14707 htmlContent = html.html;
14708
14709 address = me.selection.getRange().createAddress(true);
14710 me.execCommand('insertHtml', me.getOpt('retainOnlyLabelPasted') === true ? getPureHtml(htmlContent) : htmlContent, true);
14711 }
14712 me.fireEvent("afterpaste", html);
14713 }
14714 }
14715
14716 me.addListener('pasteTransfer', function (cmd, plainType) {
14717
14718 if (address && txtContent && htmlContent && txtContent != htmlContent) {
14719 var range = me.selection.getRange();
14720 range.moveToAddress(address, true);
14721
14722 if (!range.collapsed) {
14723
14724 while (!domUtils.isBody(range.startContainer)
14725 ) {
14726 var start = range.startContainer;
14727 if(start.nodeType == 1){
14728 start = start.childNodes[range.startOffset];
14729 if(!start){
14730 range.setStartBefore(range.startContainer);
14731 continue;
14732 }
14733 var pre = start.previousSibling;
14734
14735 if(pre && pre.nodeType == 3 && new RegExp('^[\n\r\t '+domUtils.fillChar+']*$').test(pre.nodeValue)){
14736 range.setStartBefore(pre)
14737 }
14738 }
14739 if(range.startOffset == 0){
14740 range.setStartBefore(range.startContainer);
14741 }else{
14742 break;
14743 }
14744
14745 }
14746 while (!domUtils.isBody(range.endContainer)
14747 ) {
14748 var end = range.endContainer;
14749 if(end.nodeType == 1){
14750 end = end.childNodes[range.endOffset];
14751 if(!end){
14752 range.setEndAfter(range.endContainer);
14753 continue;
14754 }
14755 var next = end.nextSibling;
14756 if(next && next.nodeType == 3 && new RegExp('^[\n\r\t'+domUtils.fillChar+']*$').test(next.nodeValue)){
14757 range.setEndAfter(next)
14758 }
14759 }
14760 if(range.endOffset == range.endContainer[range.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length){
14761 range.setEndAfter(range.endContainer);
14762 }else{
14763 break;
14764 }
14765
14766 }
14767
14768 }
14769
14770 range.deleteContents();
14771 range.select(true);
14772 me.__hasEnterExecCommand = true;
14773 var html = htmlContent;
14774 if (plainType === 2 ) {
14775 html = getPureHtml(html);
14776 } else if (plainType) {
14777 html = txtContent;
14778 }
14779 me.execCommand('inserthtml', html, true);
14780 me.__hasEnterExecCommand = false;
14781 var rng = me.selection.getRange();
14782 while (!domUtils.isBody(rng.startContainer) && !rng.startOffset &&
14783 rng.startContainer[rng.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
14784 ) {
14785 rng.setStartBefore(rng.startContainer);
14786 }
14787 var tmpAddress = rng.createAddress(true);
14788 address.endAddress = tmpAddress.startAddress;
14789 }
14790 });
14791
14792 me.addListener('ready', function () {
14793 domUtils.on(me.body, 'cut', function () {
14794 var range = me.selection.getRange();
14795 if (!range.collapsed && me.undoManger) {
14796 me.undoManger.save();
14797 }
14798 });
14799
14800 //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
14801 domUtils.on(me.body, browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
14802 if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
14803 return;
14804 }
14805 getClipboardData.call(me, function (div) {
14806 filter(div);
14807 });
14808 });
14809
14810 });
14811
14812 me.commands['paste'] = {
14813 execCommand: function (cmd) {
14814 if (browser.ie) {
14815 getClipboardData.call(me, function (div) {
14816 filter(div);
14817 });
14818 me.document.execCommand('paste');
14819 } else {
14820 alert(me.getLang('pastemsg'));
14821 }
14822 }
14823 }
14824};
14825
14826
14827
14828// plugins/puretxtpaste.js
14829/**
14830 * 纯文本粘贴插件
14831 * @file
14832 * @since 1.2.6.1
14833 */
14834
14835UE.plugins['pasteplain'] = function(){
14836 var me = this;
14837 me.setOpt({
14838 'pasteplain':false,
14839 'filterTxtRules' : function(){
14840 function transP(node){
14841 node.tagName = 'p';
14842 node.setStyle();
14843 }
14844 function removeNode(node){
14845 node.parentNode.removeChild(node,true)
14846 }
14847 return {
14848 //直接删除及其字节点内容
14849 '-' : 'script style object iframe embed input select',
14850 'p': {$:{}},
14851 'br':{$:{}},
14852 div: function (node) {
14853 var tmpNode, p = UE.uNode.createElement('p');
14854 while (tmpNode = node.firstChild()) {
14855 if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
14856 p.appendChild(tmpNode);
14857 } else {
14858 if (p.firstChild()) {
14859 node.parentNode.insertBefore(p, node);
14860 p = UE.uNode.createElement('p');
14861 } else {
14862 node.parentNode.insertBefore(tmpNode, node);
14863 }
14864 }
14865 }
14866 if (p.firstChild()) {
14867 node.parentNode.insertBefore(p, node);
14868 }
14869 node.parentNode.removeChild(node);
14870 },
14871 ol: removeNode,
14872 ul: removeNode,
14873 dl:removeNode,
14874 dt:removeNode,
14875 dd:removeNode,
14876 'li':removeNode,
14877 'caption':transP,
14878 'th':transP,
14879 'tr':transP,
14880 'h1':transP,'h2':transP,'h3':transP,'h4':transP,'h5':transP,'h6':transP,
14881 'td':function(node){
14882 //没有内容的td直接删掉
14883 var txt = !!node.innerText();
14884 if(txt){
14885 node.parentNode.insertAfter(UE.uNode.createText(' &nbsp; &nbsp;'),node);
14886 }
14887 node.parentNode.removeChild(node,node.innerText())
14888 }
14889 }
14890 }()
14891 });
14892 //暂时这里支持一下老版本的属性
14893 var pasteplain = me.options.pasteplain;
14894
14895 /**
14896 * 启用或取消纯文本粘贴模式
14897 * @command pasteplain
14898 * @method execCommand
14899 * @param { String } cmd 命令字符串
14900 * @example
14901 * ```javascript
14902 * editor.queryCommandState( 'pasteplain' );
14903 * ```
14904 */
14905
14906 /**
14907 * 查询当前是否处于纯文本粘贴模式
14908 * @command pasteplain
14909 * @method queryCommandState
14910 * @param { String } cmd 命令字符串
14911 * @return { int } 如果处于纯文本模式,返回1,否则,返回0
14912 * @example
14913 * ```javascript
14914 * editor.queryCommandState( 'pasteplain' );
14915 * ```
14916 */
14917 me.commands['pasteplain'] = {
14918 queryCommandState: function (){
14919 return pasteplain ? 1 : 0;
14920 },
14921 execCommand: function (){
14922 pasteplain = !pasteplain|0;
14923 },
14924 notNeedUndo : 1
14925 };
14926};
14927
14928// plugins/list.js
14929/**
14930 * 有序列表,无序列表插件
14931 * @file
14932 * @since 1.2.6.1
14933 */
14934
14935UE.plugins['list'] = function () {
14936 var me = this,
14937 notExchange = {
14938 'TD':1,
14939 'PRE':1,
14940 'BLOCKQUOTE':1
14941 };
14942 var customStyle = {
14943 'cn' : 'cn-1-',
14944 'cn1' : 'cn-2-',
14945 'cn2' : 'cn-3-',
14946 'num': 'num-1-',
14947 'num1' : 'num-2-',
14948 'num2' : 'num-3-',
14949 'dash' : 'dash',
14950 'dot':'dot'
14951 };
14952
14953 me.setOpt( {
14954 'autoTransWordToList':false,
14955 'insertorderedlist':{
14956 'num':'',
14957 'num1':'',
14958 'num2':'',
14959 'cn':'',
14960 'cn1':'',
14961 'cn2':'',
14962 'decimal':'',
14963 'lower-alpha':'',
14964 'lower-roman':'',
14965 'upper-alpha':'',
14966 'upper-roman':''
14967 },
14968 'insertunorderedlist':{
14969 'circle':'',
14970 'disc':'',
14971 'square':'',
14972 'dash' : '',
14973 'dot':''
14974 },
14975 listDefaultPaddingLeft : '30',
14976 listiconpath : 'http://bs.baidu.com/listicon/',
14977 maxListLevel : -1,//-1不限制
14978 disablePInList:false
14979 } );
14980 function listToArray(list){
14981 var arr = [];
14982 for(var p in list){
14983 arr.push(p)
14984 }
14985 return arr;
14986 }
14987 var listStyle = {
14988 'OL':listToArray(me.options.insertorderedlist),
14989 'UL':listToArray(me.options.insertunorderedlist)
14990 };
14991 var liiconpath = me.options.listiconpath;
14992
14993 //根据用户配置,调整customStyle
14994 for(var s in customStyle){
14995 if(!me.options.insertorderedlist.hasOwnProperty(s) && !me.options.insertunorderedlist.hasOwnProperty(s)){
14996 delete customStyle[s];
14997 }
14998 }
14999
15000 me.ready(function () {
15001 var customCss = [];
15002 for(var p in customStyle){
15003 if(p == 'dash' || p == 'dot'){
15004 customCss.push('li.list-' + customStyle[p] + '{background-image:url(' + liiconpath +customStyle[p]+'.gif)}');
15005 customCss.push('ul.custom_'+p+'{list-style:none;}ul.custom_'+p+' li{background-position:0 3px;background-repeat:no-repeat}');
15006 }else{
15007 for(var i= 0;i<99;i++){
15008 customCss.push('li.list-' + customStyle[p] + i + '{background-image:url(' + liiconpath + 'list-'+customStyle[p] + i + '.gif)}')
15009 }
15010 customCss.push('ol.custom_'+p+'{list-style:none;}ol.custom_'+p+' li{background-position:0 3px;background-repeat:no-repeat}');
15011 }
15012 switch(p){
15013 case 'cn':
15014 customCss.push('li.list-'+p+'-paddingleft-1{padding-left:25px}');
15015 customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}');
15016 customCss.push('li.list-'+p+'-paddingleft-3{padding-left:55px}');
15017 break;
15018 case 'cn1':
15019 customCss.push('li.list-'+p+'-paddingleft-1{padding-left:30px}');
15020 customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}');
15021 customCss.push('li.list-'+p+'-paddingleft-3{padding-left:55px}');
15022 break;
15023 case 'cn2':
15024 customCss.push('li.list-'+p+'-paddingleft-1{padding-left:40px}');
15025 customCss.push('li.list-'+p+'-paddingleft-2{padding-left:55px}');
15026 customCss.push('li.list-'+p+'-paddingleft-3{padding-left:68px}');
15027 break;
15028 case 'num':
15029 case 'num1':
15030 customCss.push('li.list-'+p+'-paddingleft-1{padding-left:25px}');
15031 break;
15032 case 'num2':
15033 customCss.push('li.list-'+p+'-paddingleft-1{padding-left:35px}');
15034 customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}');
15035 break;
15036 case 'dash':
15037 customCss.push('li.list-'+p+'-paddingleft{padding-left:35px}');
15038 break;
15039 case 'dot':
15040 customCss.push('li.list-'+p+'-paddingleft{padding-left:20px}');
15041 }
15042 }
15043 customCss.push('.list-paddingleft-1{padding-left:0}');
15044 customCss.push('.list-paddingleft-2{padding-left:'+me.options.listDefaultPaddingLeft+'px}');
15045 customCss.push('.list-paddingleft-3{padding-left:'+me.options.listDefaultPaddingLeft*2+'px}');
15046 //如果不给宽度会在自定应样式里出现滚动条
15047 utils.cssRule('list', 'ol,ul{margin:0;pading:0;'+(browser.ie ? '' : 'width:95%')+'}li{clear:both;}'+customCss.join('\n'), me.document);
15048 });
15049 //单独处理剪切的问题
15050 me.ready(function(){
15051 domUtils.on(me.body,'cut',function(){
15052 setTimeout(function(){
15053 var rng = me.selection.getRange(),li;
15054 //trace:3416
15055 if(!rng.collapsed){
15056 if(li = domUtils.findParentByTagName(rng.startContainer,'li',true)){
15057 if(!li.nextSibling && domUtils.isEmptyBlock(li)){
15058 var pn = li.parentNode,node;
15059 if(node = pn.previousSibling){
15060 domUtils.remove(pn);
15061 rng.setStartAtLast(node).collapse(true);
15062 rng.select(true);
15063 }else if(node = pn.nextSibling){
15064 domUtils.remove(pn);
15065 rng.setStartAtFirst(node).collapse(true);
15066 rng.select(true);
15067 }else{
15068 var tmpNode = me.document.createElement('p');
15069 domUtils.fillNode(me.document,tmpNode);
15070 pn.parentNode.insertBefore(tmpNode,pn);
15071 domUtils.remove(pn);
15072 rng.setStart(tmpNode,0).collapse(true);
15073 rng.select(true);
15074 }
15075 }
15076 }
15077 }
15078
15079 })
15080 })
15081 });
15082
15083 function getStyle(node){
15084 var cls = node.className;
15085 if(domUtils.hasClass(node,/custom_/)){
15086 return cls.match(/custom_(\w+)/)[1]
15087 }
15088 return domUtils.getStyle(node, 'list-style-type')
15089
15090 }
15091
15092 me.addListener('beforepaste',function(type,html){
15093 var me = this,
15094 rng = me.selection.getRange(),li;
15095 var root = UE.htmlparser(html.html,true);
15096 if(li = domUtils.findParentByTagName(rng.startContainer,'li',true)){
15097 var list = li.parentNode,tagName = list.tagName == 'OL' ? 'ul':'ol';
15098 utils.each(root.getNodesByTagName(tagName),function(n){
15099 n.tagName = list.tagName;
15100 n.setAttr();
15101 if(n.parentNode === root){
15102 type = getStyle(list) || (list.tagName == 'OL' ? 'decimal' : 'disc')
15103 }else{
15104 var className = n.parentNode.getAttr('class');
15105 if(className && /custom_/.test(className)){
15106 type = className.match(/custom_(\w+)/)[1]
15107 }else{
15108 type = n.parentNode.getStyle('list-style-type');
15109 }
15110 if(!type){
15111 type = list.tagName == 'OL' ? 'decimal' : 'disc';
15112 }
15113 }
15114 var index = utils.indexOf(listStyle[list.tagName], type);
15115 if(n.parentNode !== root)
15116 index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
15117 var currentStyle = listStyle[list.tagName][index];
15118 if(customStyle[currentStyle]){
15119 n.setAttr('class', 'custom_' + currentStyle)
15120
15121 }else{
15122 n.setStyle('list-style-type',currentStyle)
15123 }
15124 })
15125
15126 }
15127
15128 html.html = root.toHtml();
15129 });
15130 //导出时,去掉p标签
15131 me.getOpt('disablePInList') === true && me.addOutputRule(function(root){
15132 utils.each(root.getNodesByTagName('li'),function(li){
15133 var newChildrens = [],index=0;
15134 utils.each(li.children,function(n){
15135 if(n.tagName == 'p'){
15136 var tmpNode;
15137 while(tmpNode = n.children.pop()) {
15138 newChildrens.splice(index,0,tmpNode);
15139 tmpNode.parentNode = li;
15140 lastNode = tmpNode;
15141 }
15142 tmpNode = newChildrens[newChildrens.length-1];
15143 if(!tmpNode || tmpNode.type != 'element' || tmpNode.tagName != 'br'){
15144 var br = UE.uNode.createElement('br');
15145 br.parentNode = li;
15146 newChildrens.push(br);
15147 }
15148
15149 index = newChildrens.length;
15150 }
15151 });
15152 if(newChildrens.length){
15153 li.children = newChildrens;
15154 }
15155 });
15156 });
15157 //进入编辑器的li要套p标签
15158 me.addInputRule(function(root){
15159 utils.each(root.getNodesByTagName('li'),function(li){
15160 var tmpP = UE.uNode.createElement('p');
15161 for(var i= 0,ci;ci=li.children[i];){
15162 if(ci.type == 'text' || dtd.p[ci.tagName]){
15163 tmpP.appendChild(ci);
15164 }else{
15165 if(tmpP.firstChild()){
15166 li.insertBefore(tmpP,ci);
15167 tmpP = UE.uNode.createElement('p');
15168 i = i + 2;
15169 }else{
15170 i++;
15171 }
15172
15173 }
15174 }
15175 if(tmpP.firstChild() && !tmpP.parentNode || !li.firstChild()){
15176 li.appendChild(tmpP);
15177 }
15178 //trace:3357
15179 //p不能为空
15180 if (!tmpP.firstChild()) {
15181 tmpP.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
15182 }
15183 //去掉末尾的空白
15184 var p = li.firstChild();
15185 var lastChild = p.lastChild();
15186 if(lastChild && lastChild.type == 'text' && /^\s*$/.test(lastChild.data)){
15187 p.removeChild(lastChild)
15188 }
15189 });
15190 if(me.options.autoTransWordToList){
15191 var orderlisttype = {
15192 'num1':/^\d+\)/,
15193 'decimal':/^\d+\./,
15194 'lower-alpha':/^[a-z]+\)/,
15195 'upper-alpha':/^[A-Z]+\./,
15196 'cn':/^[\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+[\u3001]/,
15197 'cn2':/^\([\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+\)/
15198 },
15199 unorderlisttype = {
15200 'square':'n'
15201 };
15202 function checkListType(content,container){
15203 var span = container.firstChild();
15204 if(span && span.type == 'element' && span.tagName == 'span' && /Wingdings|Symbol/.test(span.getStyle('font-family'))){
15205 for(var p in unorderlisttype){
15206 if(unorderlisttype[p] == span.data){
15207 return p
15208 }
15209 }
15210 return 'disc'
15211 }
15212 for(var p in orderlisttype){
15213 if(orderlisttype[p].test(content)){
15214 return p;
15215 }
15216 }
15217
15218 }
15219 utils.each(root.getNodesByTagName('p'),function(node){
15220 if(node.getAttr('class') != 'MsoListParagraph'){
15221 return
15222 }
15223
15224 //word粘贴过来的会带有margin要去掉,但这样也可能会误命中一些央视
15225 node.setStyle('margin','');
15226 node.setStyle('margin-left','');
15227 node.setAttr('class','');
15228
15229 function appendLi(list,p,type){
15230 if(list.tagName == 'ol'){
15231 if(browser.ie){
15232 var first = p.firstChild();
15233 if(first.type =='element' && first.tagName == 'span' && orderlisttype[type].test(first.innerText())){
15234 p.removeChild(first);
15235 }
15236 }else{
15237 p.innerHTML(p.innerHTML().replace(orderlisttype[type],''));
15238 }
15239 }else{
15240 p.removeChild(p.firstChild())
15241 }
15242
15243 var li = UE.uNode.createElement('li');
15244 li.appendChild(p);
15245 list.appendChild(li);
15246 }
15247 var tmp = node,type,cacheNode = node;
15248
15249 if(node.parentNode.tagName != 'li' && (type = checkListType(node.innerText(),node))){
15250
15251 var list = UE.uNode.createElement(me.options.insertorderedlist.hasOwnProperty(type) ? 'ol' : 'ul');
15252 if(customStyle[type]){
15253 list.setAttr('class','custom_'+type)
15254 }else{
15255 list.setStyle('list-style-type',type)
15256 }
15257 while(node && node.parentNode.tagName != 'li' && checkListType(node.innerText(),node)){
15258 tmp = node.nextSibling();
15259 if(!tmp){
15260 node.parentNode.insertBefore(list,node)
15261 }
15262 appendLi(list,node,type);
15263 node = tmp;
15264 }
15265 if(!list.parentNode && node && node.parentNode){
15266 node.parentNode.insertBefore(list,node)
15267 }
15268 }
15269 var span = cacheNode.firstChild();
15270 if(span && span.type == 'element' && span.tagName == 'span' && /^\s*(&nbsp;)+\s*$/.test(span.innerText())){
15271 span.parentNode.removeChild(span)
15272 }
15273 })
15274 }
15275
15276 });
15277
15278 //调整索引标签
15279 me.addListener('contentchange',function(){
15280 adjustListStyle(me.document)
15281 });
15282
15283 function adjustListStyle(doc,ignore){
15284 utils.each(domUtils.getElementsByTagName(doc,'ol ul'),function(node){
15285
15286 if(!domUtils.inDoc(node,doc))
15287 return;
15288
15289 var parent = node.parentNode;
15290 if(parent.tagName == node.tagName){
15291 var nodeStyleType = getStyle(node) || (node.tagName == 'OL' ? 'decimal' : 'disc'),
15292 parentStyleType = getStyle(parent) || (parent.tagName == 'OL' ? 'decimal' : 'disc');
15293 if(nodeStyleType == parentStyleType){
15294 var styleIndex = utils.indexOf(listStyle[node.tagName], nodeStyleType);
15295 styleIndex = styleIndex + 1 == listStyle[node.tagName].length ? 0 : styleIndex + 1;
15296 setListStyle(node,listStyle[node.tagName][styleIndex])
15297 }
15298
15299 }
15300 var index = 0,type = 2;
15301 if( domUtils.hasClass(node,/custom_/)){
15302 if(!(/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent,/custom_/))){
15303 type = 1;
15304 }
15305 }else{
15306 if(/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent,/custom_/)){
15307 type = 3;
15308 }
15309 }
15310
15311 var style = domUtils.getStyle(node, 'list-style-type');
15312 style && (node.style.cssText = 'list-style-type:' + style);
15313 node.className = utils.trim(node.className.replace(/list-paddingleft-\w+/,'')) + ' list-paddingleft-' + type;
15314 utils.each(domUtils.getElementsByTagName(node,'li'),function(li){
15315 li.style.cssText && (li.style.cssText = '');
15316 if(!li.firstChild){
15317 domUtils.remove(li);
15318 return;
15319 }
15320 if(li.parentNode !== node){
15321 return;
15322 }
15323 index++;
15324 if(domUtils.hasClass(node,/custom_/) ){
15325 var paddingLeft = 1,currentStyle = getStyle(node);
15326 if(node.tagName == 'OL'){
15327 if(currentStyle){
15328 switch(currentStyle){
15329 case 'cn' :
15330 case 'cn1':
15331 case 'cn2':
15332 if(index > 10 && (index % 10 == 0 || index > 10 && index < 20)){
15333 paddingLeft = 2
15334 }else if(index > 20){
15335 paddingLeft = 3
15336 }
15337 break;
15338 case 'num2' :
15339 if(index > 9){
15340 paddingLeft = 2
15341 }
15342 }
15343 }
15344 li.className = 'list-'+customStyle[currentStyle]+ index + ' ' + 'list-'+currentStyle+'-paddingleft-' + paddingLeft;
15345 }else{
15346 li.className = 'list-'+customStyle[currentStyle] + ' ' + 'list-'+currentStyle+'-paddingleft';
15347 }
15348 }else{
15349 li.className = li.className.replace(/list-[\w\-]+/gi,'');
15350 }
15351 var className = li.getAttribute('class');
15352 if(className !== null && !className.replace(/\s/g,'')){
15353 domUtils.removeAttributes(li,'class')
15354 }
15355 });
15356 !ignore && adjustList(node,node.tagName.toLowerCase(),getStyle(node)||domUtils.getStyle(node, 'list-style-type'),true);
15357 })
15358 }
15359 function adjustList(list, tag, style,ignoreEmpty) {
15360 var nextList = list.nextSibling;
15361 if (nextList && nextList.nodeType == 1 && nextList.tagName.toLowerCase() == tag && (getStyle(nextList) || domUtils.getStyle(nextList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) == style) {
15362 domUtils.moveChild(nextList, list);
15363 if (nextList.childNodes.length == 0) {
15364 domUtils.remove(nextList);
15365 }
15366 }
15367 if(nextList && domUtils.isFillChar(nextList)){
15368 domUtils.remove(nextList);
15369 }
15370 var preList = list.previousSibling;
15371 if (preList && preList.nodeType == 1 && preList.tagName.toLowerCase() == tag && (getStyle(preList) || domUtils.getStyle(preList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) == style) {
15372 domUtils.moveChild(list, preList);
15373 }
15374 if(preList && domUtils.isFillChar(preList)){
15375 domUtils.remove(preList);
15376 }
15377 !ignoreEmpty && domUtils.isEmptyBlock(list) && domUtils.remove(list);
15378 if(getStyle(list)){
15379 adjustListStyle(list.ownerDocument,true)
15380 }
15381 }
15382
15383 function setListStyle(list,style){
15384 if(customStyle[style]){
15385 list.className = 'custom_' + style;
15386 }
15387 try{
15388 domUtils.setStyle(list, 'list-style-type', style);
15389 }catch(e){}
15390 }
15391 function clearEmptySibling(node) {
15392 var tmpNode = node.previousSibling;
15393 if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
15394 domUtils.remove(tmpNode);
15395 }
15396 tmpNode = node.nextSibling;
15397 if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
15398 domUtils.remove(tmpNode);
15399 }
15400 }
15401
15402 me.addListener('keydown', function (type, evt) {
15403 function preventAndSave() {
15404 evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
15405 me.fireEvent('contentchange');
15406 me.undoManger && me.undoManger.save();
15407 }
15408 function findList(node,filterFn){
15409 while(node && !domUtils.isBody(node)){
15410 if(filterFn(node)){
15411 return null
15412 }
15413 if(node.nodeType == 1 && /[ou]l/i.test(node.tagName)){
15414 return node;
15415 }
15416 node = node.parentNode;
15417 }
15418 return null;
15419 }
15420 var keyCode = evt.keyCode || evt.which;
15421 if (keyCode == 13 && !evt.shiftKey) {//回车
15422 var rng = me.selection.getRange(),
15423 parent = domUtils.findParent(rng.startContainer,function(node){return domUtils.isBlockElm(node)},true),
15424 li = domUtils.findParentByTagName(rng.startContainer,'li',true);
15425 if(parent && parent.tagName != 'PRE' && !li){
15426 var html = parent.innerHTML.replace(new RegExp(domUtils.fillChar, 'g'),'');
15427 if(/^\s*1\s*\.[^\d]/.test(html)){
15428 parent.innerHTML = html.replace(/^\s*1\s*\./,'');
15429 rng.setStartAtLast(parent).collapse(true).select();
15430 me.__hasEnterExecCommand = true;
15431 me.execCommand('insertorderedlist');
15432 me.__hasEnterExecCommand = false;
15433 }
15434 }
15435 var range = me.selection.getRange(),
15436 start = findList(range.startContainer,function (node) {
15437 return node.tagName == 'TABLE';
15438 }),
15439 end = range.collapsed ? start : findList(range.endContainer,function (node) {
15440 return node.tagName == 'TABLE';
15441 });
15442
15443 if (start && end && start === end) {
15444
15445 if (!range.collapsed) {
15446 start = domUtils.findParentByTagName(range.startContainer, 'li', true);
15447 end = domUtils.findParentByTagName(range.endContainer, 'li', true);
15448 if (start && end && start === end) {
15449 range.deleteContents();
15450 li = domUtils.findParentByTagName(range.startContainer, 'li', true);
15451 if (li && domUtils.isEmptyBlock(li)) {
15452
15453 pre = li.previousSibling;
15454 next = li.nextSibling;
15455 p = me.document.createElement('p');
15456
15457 domUtils.fillNode(me.document, p);
15458 parentList = li.parentNode;
15459 if (pre && next) {
15460 range.setStart(next, 0).collapse(true).select(true);
15461 domUtils.remove(li);
15462
15463 } else {
15464 if (!pre && !next || !pre) {
15465
15466 parentList.parentNode.insertBefore(p, parentList);
15467
15468
15469 } else {
15470 li.parentNode.parentNode.insertBefore(p, parentList.nextSibling);
15471 }
15472 domUtils.remove(li);
15473 if (!parentList.firstChild) {
15474 domUtils.remove(parentList);
15475 }
15476 range.setStart(p, 0).setCursor();
15477
15478
15479 }
15480 preventAndSave();
15481 return;
15482
15483 }
15484 } else {
15485 var tmpRange = range.cloneRange(),
15486 bk = tmpRange.collapse(false).createBookmark();
15487
15488 range.deleteContents();
15489 tmpRange.moveToBookmark(bk);
15490 var li = domUtils.findParentByTagName(tmpRange.startContainer, 'li', true);
15491
15492 clearEmptySibling(li);
15493 tmpRange.select();
15494 preventAndSave();
15495 return;
15496 }
15497 }
15498
15499
15500 li = domUtils.findParentByTagName(range.startContainer, 'li', true);
15501
15502 if (li) {
15503 if (domUtils.isEmptyBlock(li)) {
15504 bk = range.createBookmark();
15505 var parentList = li.parentNode;
15506 if (li !== parentList.lastChild) {
15507 domUtils.breakParent(li, parentList);
15508 clearEmptySibling(li);
15509 } else {
15510
15511 parentList.parentNode.insertBefore(li, parentList.nextSibling);
15512 if (domUtils.isEmptyNode(parentList)) {
15513 domUtils.remove(parentList);
15514 }
15515 }
15516 //嵌套不处理
15517 if (!dtd.$list[li.parentNode.tagName]) {
15518
15519 if (!domUtils.isBlockElm(li.firstChild)) {
15520 p = me.document.createElement('p');
15521 li.parentNode.insertBefore(p, li);
15522 while (li.firstChild) {
15523 p.appendChild(li.firstChild);
15524 }
15525 domUtils.remove(li);
15526 } else {
15527 domUtils.remove(li, true);
15528 }
15529 }
15530 range.moveToBookmark(bk).select();
15531
15532
15533 } else {
15534 var first = li.firstChild;
15535 if (!first || !domUtils.isBlockElm(first)) {
15536 var p = me.document.createElement('p');
15537
15538 !li.firstChild && domUtils.fillNode(me.document, p);
15539 while (li.firstChild) {
15540
15541 p.appendChild(li.firstChild);
15542 }
15543 li.appendChild(p);
15544 first = p;
15545 }
15546
15547 var span = me.document.createElement('span');
15548
15549 range.insertNode(span);
15550 domUtils.breakParent(span, li);
15551
15552 var nextLi = span.nextSibling;
15553 first = nextLi.firstChild;
15554
15555 if (!first) {
15556 p = me.document.createElement('p');
15557
15558 domUtils.fillNode(me.document, p);
15559 nextLi.appendChild(p);
15560 first = p;
15561 }
15562 if (domUtils.isEmptyNode(first)) {
15563 first.innerHTML = '';
15564 domUtils.fillNode(me.document, first);
15565 }
15566
15567 range.setStart(first, 0).collapse(true).shrinkBoundary().select();
15568 domUtils.remove(span);
15569 var pre = nextLi.previousSibling;
15570 if (pre && domUtils.isEmptyBlock(pre)) {
15571 pre.innerHTML = '<p></p>';
15572 domUtils.fillNode(me.document, pre.firstChild);
15573 }
15574
15575 }
15576// }
15577 preventAndSave();
15578 }
15579
15580
15581 }
15582
15583
15584 }
15585 if (keyCode == 8) {
15586 //修中ie中li下的问题
15587 range = me.selection.getRange();
15588 if (range.collapsed && domUtils.isStartInblock(range)) {
15589 tmpRange = range.cloneRange().trimBoundary();
15590 li = domUtils.findParentByTagName(range.startContainer, 'li', true);
15591 //要在li的最左边,才能处理
15592 if (li && domUtils.isStartInblock(tmpRange)) {
15593 start = domUtils.findParentByTagName(range.startContainer, 'p', true);
15594 if (start && start !== li.firstChild) {
15595 var parentList = domUtils.findParentByTagName(start,['ol','ul']);
15596 domUtils.breakParent(start,parentList);
15597 clearEmptySibling(start);
15598 me.fireEvent('contentchange');
15599 range.setStart(start,0).setCursor(false,true);
15600 me.fireEvent('saveScene');
15601 domUtils.preventDefault(evt);
15602 return;
15603 }
15604
15605 if (li && (pre = li.previousSibling)) {
15606 if (keyCode == 46 && li.childNodes.length) {
15607 return;
15608 }
15609 //有可能上边的兄弟节点是个2级菜单,要追加到2级菜单的最后的li
15610 if (dtd.$list[pre.tagName]) {
15611 pre = pre.lastChild;
15612 }
15613 me.undoManger && me.undoManger.save();
15614 first = li.firstChild;
15615 if (domUtils.isBlockElm(first)) {
15616 if (domUtils.isEmptyNode(first)) {
15617// range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
15618 pre.appendChild(first);
15619 range.setStart(first, 0).setCursor(false, true);
15620 //first不是唯一的节点
15621 while (li.firstChild) {
15622 pre.appendChild(li.firstChild);
15623 }
15624 } else {
15625
15626 span = me.document.createElement('span');
15627 range.insertNode(span);
15628 //判断pre是否是空的节点,如果是<p><br/></p>类型的空节点,干掉p标签防止它占位
15629 if (domUtils.isEmptyBlock(pre)) {
15630 pre.innerHTML = '';
15631 }
15632 domUtils.moveChild(li, pre);
15633 range.setStartBefore(span).collapse(true).select(true);
15634
15635 domUtils.remove(span);
15636
15637 }
15638 } else {
15639 if (domUtils.isEmptyNode(li)) {
15640 var p = me.document.createElement('p');
15641 pre.appendChild(p);
15642 range.setStart(p, 0).setCursor();
15643// range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
15644 } else {
15645 range.setEnd(pre, pre.childNodes.length).collapse().select(true);
15646 while (li.firstChild) {
15647 pre.appendChild(li.firstChild);
15648 }
15649 }
15650 }
15651 domUtils.remove(li);
15652 me.fireEvent('contentchange');
15653 me.fireEvent('saveScene');
15654 domUtils.preventDefault(evt);
15655 return;
15656
15657 }
15658 //trace:980
15659
15660 if (li && !li.previousSibling) {
15661 var parentList = li.parentNode;
15662 var bk = range.createBookmark();
15663 if(domUtils.isTagNode(parentList.parentNode,'ol ul')){
15664 parentList.parentNode.insertBefore(li,parentList);
15665 if(domUtils.isEmptyNode(parentList)){
15666 domUtils.remove(parentList)
15667 }
15668 }else{
15669
15670 while(li.firstChild){
15671 parentList.parentNode.insertBefore(li.firstChild,parentList);
15672 }
15673
15674 domUtils.remove(li);
15675 if(domUtils.isEmptyNode(parentList)){
15676 domUtils.remove(parentList)
15677 }
15678
15679 }
15680 range.moveToBookmark(bk).setCursor(false,true);
15681 me.fireEvent('contentchange');
15682 me.fireEvent('saveScene');
15683 domUtils.preventDefault(evt);
15684 return;
15685
15686 }
15687
15688
15689 }
15690
15691
15692 }
15693
15694 }
15695 });
15696
15697 me.addListener('keyup',function(type, evt){
15698 var keyCode = evt.keyCode || evt.which;
15699 if (keyCode == 8) {
15700 var rng = me.selection.getRange(),list;
15701 if(list = domUtils.findParentByTagName(rng.startContainer,['ol', 'ul'],true)){
15702 adjustList(list,list.tagName.toLowerCase(),getStyle(list)||domUtils.getComputedStyle(list,'list-style-type'),true)
15703 }
15704 }
15705 });
15706 //处理tab键
15707 me.addListener('tabkeydown',function(){
15708
15709 var range = me.selection.getRange();
15710
15711 //控制级数
15712 function checkLevel(li){
15713 if(me.options.maxListLevel != -1){
15714 var level = li.parentNode,levelNum = 0;
15715 while(/[ou]l/i.test(level.tagName)){
15716 levelNum++;
15717 level = level.parentNode;
15718 }
15719 if(levelNum >= me.options.maxListLevel){
15720 return true;
15721 }
15722 }
15723 }
15724 //只以开始为准
15725 //todo 后续改进
15726 var li = domUtils.findParentByTagName(range.startContainer, 'li', true);
15727 if(li){
15728
15729 var bk;
15730 if(range.collapsed){
15731 if(checkLevel(li))
15732 return true;
15733 var parentLi = li.parentNode,
15734 list = me.document.createElement(parentLi.tagName),
15735 index = utils.indexOf(listStyle[list.tagName], getStyle(parentLi)||domUtils.getComputedStyle(parentLi, 'list-style-type'));
15736 index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
15737 var currentStyle = listStyle[list.tagName][index];
15738 setListStyle(list,currentStyle);
15739 if(domUtils.isStartInblock(range)){
15740 me.fireEvent('saveScene');
15741 bk = range.createBookmark();
15742 parentLi.insertBefore(list, li);
15743 list.appendChild(li);
15744 adjustList(list,list.tagName.toLowerCase(),currentStyle);
15745 me.fireEvent('contentchange');
15746 range.moveToBookmark(bk).select(true);
15747 return true;
15748 }
15749 }else{
15750 me.fireEvent('saveScene');
15751 bk = range.createBookmark();
15752 for(var i= 0,closeList,parents = domUtils.findParents(li),ci;ci=parents[i++];){
15753 if(domUtils.isTagNode(ci,'ol ul')){
15754 closeList = ci;
15755 break;
15756 }
15757 }
15758 var current = li;
15759 if(bk.end){
15760 while(current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)){
15761 if(checkLevel(current)){
15762 current = domUtils.getNextDomNode(current,false,null,function(node){return node !== closeList});
15763 continue;
15764 }
15765 var parentLi = current.parentNode,
15766 list = me.document.createElement(parentLi.tagName),
15767 index = utils.indexOf(listStyle[list.tagName], getStyle(parentLi)||domUtils.getComputedStyle(parentLi, 'list-style-type'));
15768 var currentIndex = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
15769 var currentStyle = listStyle[list.tagName][currentIndex];
15770 setListStyle(list,currentStyle);
15771 parentLi.insertBefore(list, current);
15772 while(current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)){
15773 li = current.nextSibling;
15774 list.appendChild(current);
15775 if(!li || domUtils.isTagNode(li,'ol ul')){
15776 if(li){
15777 while(li = li.firstChild){
15778 if(li.tagName == 'LI'){
15779 break;
15780 }
15781 }
15782 }else{
15783 li = domUtils.getNextDomNode(current,false,null,function(node){return node !== closeList});
15784 }
15785 break;
15786 }
15787 current = li;
15788 }
15789 adjustList(list,list.tagName.toLowerCase(),currentStyle);
15790 current = li;
15791 }
15792 }
15793 me.fireEvent('contentchange');
15794 range.moveToBookmark(bk).select();
15795 return true;
15796 }
15797 }
15798
15799 });
15800 function getLi(start){
15801 while(start && !domUtils.isBody(start)){
15802 if(start.nodeName == 'TABLE'){
15803 return null;
15804 }
15805 if(start.nodeName == 'LI'){
15806 return start
15807 }
15808 start = start.parentNode;
15809 }
15810 }
15811
15812 /**
15813 * 有序列表,与“insertunorderedlist”命令互斥
15814 * @command insertorderedlist
15815 * @method execCommand
15816 * @param { String } command 命令字符串
15817 * @param { String } style 插入的有序列表类型,值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
15818 * @example
15819 * ```javascript
15820 * editor.execCommand( 'insertorderedlist','decimal');
15821 * ```
15822 */
15823 /**
15824 * 查询当前选区内容是否有序列表
15825 * @command insertorderedlist
15826 * @method queryCommandState
15827 * @param { String } cmd 命令字符串
15828 * @return { int } 如果当前选区是有序列表返回1,否则返回0
15829 * @example
15830 * ```javascript
15831 * editor.queryCommandState( 'insertorderedlist' );
15832 * ```
15833 */
15834 /**
15835 * 查询当前选区内容是否有序列表
15836 * @command insertorderedlist
15837 * @method queryCommandValue
15838 * @param { String } cmd 命令字符串
15839 * @return { String } 返回当前有序列表的类型,值为null或decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
15840 * @example
15841 * ```javascript
15842 * editor.queryCommandValue( 'insertorderedlist' );
15843 * ```
15844 */
15845
15846 /**
15847 * 无序列表,与“insertorderedlist”命令互斥
15848 * @command insertunorderedlist
15849 * @method execCommand
15850 * @param { String } command 命令字符串
15851 * @param { String } style 插入的无序列表类型,值为:circle,disc,square,dash,dot
15852 * @example
15853 * ```javascript
15854 * editor.execCommand( 'insertunorderedlist','circle');
15855 * ```
15856 */
15857 /**
15858 * 查询当前是否有word文档粘贴进来的图片
15859 * @command insertunorderedlist
15860 * @method insertunorderedlist
15861 * @param { String } command 命令字符串
15862 * @return { int } 如果当前选区是无序列表返回1,否则返回0
15863 * @example
15864 * ```javascript
15865 * editor.queryCommandState( 'insertunorderedlist' );
15866 * ```
15867 */
15868 /**
15869 * 查询当前选区内容是否有序列表
15870 * @command insertunorderedlist
15871 * @method queryCommandValue
15872 * @param { String } command 命令字符串
15873 * @return { String } 返回当前无序列表的类型,值为null或circle,disc,square,dash,dot
15874 * @example
15875 * ```javascript
15876 * editor.queryCommandValue( 'insertunorderedlist' );
15877 * ```
15878 */
15879
15880 me.commands['insertorderedlist'] =
15881 me.commands['insertunorderedlist'] = {
15882 execCommand:function (command, style) {
15883
15884 if (!style) {
15885 style = command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc';
15886 }
15887 var me = this,
15888 range = this.selection.getRange(),
15889 filterFn = function (node) {
15890 return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
15891 },
15892 tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul',
15893 frag = me.document.createDocumentFragment();
15894 //去掉是因为会出现选到末尾,导致adjustmentBoundary缩到ol/ul的位置
15895 //range.shrinkBoundary();//.adjustmentBoundary();
15896 range.adjustmentBoundary().shrinkBoundary();
15897 var bko = range.createBookmark(true),
15898 start = getLi(me.document.getElementById(bko.start)),
15899 modifyStart = 0,
15900 end = getLi(me.document.getElementById(bko.end)),
15901 modifyEnd = 0,
15902 startParent, endParent,
15903 list, tmp;
15904
15905 if (start || end) {
15906 start && (startParent = start.parentNode);
15907 if (!bko.end) {
15908 end = start;
15909 }
15910 end && (endParent = end.parentNode);
15911
15912 if (startParent === endParent) {
15913 while (start !== end) {
15914 tmp = start;
15915 start = start.nextSibling;
15916 if (!domUtils.isBlockElm(tmp.firstChild)) {
15917 var p = me.document.createElement('p');
15918 while (tmp.firstChild) {
15919 p.appendChild(tmp.firstChild);
15920 }
15921 tmp.appendChild(p);
15922 }
15923 frag.appendChild(tmp);
15924 }
15925 tmp = me.document.createElement('span');
15926 startParent.insertBefore(tmp, end);
15927 if (!domUtils.isBlockElm(end.firstChild)) {
15928 p = me.document.createElement('p');
15929 while (end.firstChild) {
15930 p.appendChild(end.firstChild);
15931 }
15932 end.appendChild(p);
15933 }
15934 frag.appendChild(end);
15935 domUtils.breakParent(tmp, startParent);
15936 if (domUtils.isEmptyNode(tmp.previousSibling)) {
15937 domUtils.remove(tmp.previousSibling);
15938 }
15939 if (domUtils.isEmptyNode(tmp.nextSibling)) {
15940 domUtils.remove(tmp.nextSibling)
15941 }
15942 var nodeStyle = getStyle(startParent) || domUtils.getComputedStyle(startParent, 'list-style-type') || (command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc');
15943 if (startParent.tagName.toLowerCase() == tag && nodeStyle == style) {
15944 for (var i = 0, ci, tmpFrag = me.document.createDocumentFragment(); ci = frag.firstChild;) {
15945 if(domUtils.isTagNode(ci,'ol ul')){
15946// 删除时,子列表不处理
15947// utils.each(domUtils.getElementsByTagName(ci,'li'),function(li){
15948// while(li.firstChild){
15949// tmpFrag.appendChild(li.firstChild);
15950// }
15951//
15952// });
15953 tmpFrag.appendChild(ci);
15954 }else{
15955 while (ci.firstChild) {
15956
15957 tmpFrag.appendChild(ci.firstChild);
15958 domUtils.remove(ci);
15959 }
15960 }
15961
15962 }
15963 tmp.parentNode.insertBefore(tmpFrag, tmp);
15964 } else {
15965 list = me.document.createElement(tag);
15966 setListStyle(list,style);
15967 list.appendChild(frag);
15968 tmp.parentNode.insertBefore(list, tmp);
15969 }
15970
15971 domUtils.remove(tmp);
15972 list && adjustList(list, tag, style);
15973 range.moveToBookmark(bko).select();
15974 return;
15975 }
15976 //开始
15977 if (start) {
15978 while (start) {
15979 tmp = start.nextSibling;
15980 if (domUtils.isTagNode(start, 'ol ul')) {
15981 frag.appendChild(start);
15982 } else {
15983 var tmpfrag = me.document.createDocumentFragment(),
15984 hasBlock = 0;
15985 while (start.firstChild) {
15986 if (domUtils.isBlockElm(start.firstChild)) {
15987 hasBlock = 1;
15988 }
15989 tmpfrag.appendChild(start.firstChild);
15990 }
15991 if (!hasBlock) {
15992 var tmpP = me.document.createElement('p');
15993 tmpP.appendChild(tmpfrag);
15994 frag.appendChild(tmpP);
15995 } else {
15996 frag.appendChild(tmpfrag);
15997 }
15998 domUtils.remove(start);
15999 }
16000
16001 start = tmp;
16002 }
16003 startParent.parentNode.insertBefore(frag, startParent.nextSibling);
16004 if (domUtils.isEmptyNode(startParent)) {
16005 range.setStartBefore(startParent);
16006 domUtils.remove(startParent);
16007 } else {
16008 range.setStartAfter(startParent);
16009 }
16010 modifyStart = 1;
16011 }
16012
16013 if (end && domUtils.inDoc(endParent, me.document)) {
16014 //结束
16015 start = endParent.firstChild;
16016 while (start && start !== end) {
16017 tmp = start.nextSibling;
16018 if (domUtils.isTagNode(start, 'ol ul')) {
16019 frag.appendChild(start);
16020 } else {
16021 tmpfrag = me.document.createDocumentFragment();
16022 hasBlock = 0;
16023 while (start.firstChild) {
16024 if (domUtils.isBlockElm(start.firstChild)) {
16025 hasBlock = 1;
16026 }
16027 tmpfrag.appendChild(start.firstChild);
16028 }
16029 if (!hasBlock) {
16030 tmpP = me.document.createElement('p');
16031 tmpP.appendChild(tmpfrag);
16032 frag.appendChild(tmpP);
16033 } else {
16034 frag.appendChild(tmpfrag);
16035 }
16036 domUtils.remove(start);
16037 }
16038 start = tmp;
16039 }
16040 var tmpDiv = domUtils.createElement(me.document, 'div', {
16041 'tmpDiv':1
16042 });
16043 domUtils.moveChild(end, tmpDiv);
16044
16045 frag.appendChild(tmpDiv);
16046 domUtils.remove(end);
16047 endParent.parentNode.insertBefore(frag, endParent);
16048 range.setEndBefore(endParent);
16049 if (domUtils.isEmptyNode(endParent)) {
16050 domUtils.remove(endParent);
16051 }
16052
16053 modifyEnd = 1;
16054 }
16055
16056
16057 }
16058
16059 if (!modifyStart) {
16060 range.setStartBefore(me.document.getElementById(bko.start));
16061 }
16062 if (bko.end && !modifyEnd) {
16063 range.setEndAfter(me.document.getElementById(bko.end));
16064 }
16065 range.enlarge(true, function (node) {
16066 return notExchange[node.tagName];
16067 });
16068
16069 frag = me.document.createDocumentFragment();
16070
16071 var bk = range.createBookmark(),
16072 current = domUtils.getNextDomNode(bk.start, false, filterFn),
16073 tmpRange = range.cloneRange(),
16074 tmpNode,
16075 block = domUtils.isBlockElm;
16076
16077 while (current && current !== bk.end && (domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING)) {
16078
16079 if (current.nodeType == 3 || dtd.li[current.tagName]) {
16080 if (current.nodeType == 1 && dtd.$list[current.tagName]) {
16081 while (current.firstChild) {
16082 frag.appendChild(current.firstChild);
16083 }
16084 tmpNode = domUtils.getNextDomNode(current, false, filterFn);
16085 domUtils.remove(current);
16086 current = tmpNode;
16087 continue;
16088
16089 }
16090 tmpNode = current;
16091 tmpRange.setStartBefore(current);
16092
16093 while (current && current !== bk.end && (!block(current) || domUtils.isBookmarkNode(current) )) {
16094 tmpNode = current;
16095 current = domUtils.getNextDomNode(current, false, null, function (node) {
16096 return !notExchange[node.tagName];
16097 });
16098 }
16099
16100 if (current && block(current)) {
16101 tmp = domUtils.getNextDomNode(tmpNode, false, filterFn);
16102 if (tmp && domUtils.isBookmarkNode(tmp)) {
16103 current = domUtils.getNextDomNode(tmp, false, filterFn);
16104 tmpNode = tmp;
16105 }
16106 }
16107 tmpRange.setEndAfter(tmpNode);
16108
16109 current = domUtils.getNextDomNode(tmpNode, false, filterFn);
16110
16111 var li = range.document.createElement('li');
16112
16113 li.appendChild(tmpRange.extractContents());
16114 if(domUtils.isEmptyNode(li)){
16115 var tmpNode = range.document.createElement('p');
16116 while(li.firstChild){
16117 tmpNode.appendChild(li.firstChild)
16118 }
16119 li.appendChild(tmpNode);
16120 }
16121 frag.appendChild(li);
16122 } else {
16123 current = domUtils.getNextDomNode(current, true, filterFn);
16124 }
16125 }
16126 range.moveToBookmark(bk).collapse(true);
16127 list = me.document.createElement(tag);
16128 setListStyle(list,style);
16129 list.appendChild(frag);
16130 range.insertNode(list);
16131 //当前list上下看能否合并
16132 adjustList(list, tag, style);
16133 //去掉冗余的tmpDiv
16134 for (var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, 'div'); ci = tmpDivs[i++];) {
16135 if (ci.getAttribute('tmpDiv')) {
16136 domUtils.remove(ci, true)
16137 }
16138 }
16139 range.moveToBookmark(bko).select();
16140
16141 },
16142 queryCommandState:function (command) {
16143 var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul';
16144 var path = this.selection.getStartElementPath();
16145 for(var i= 0,ci;ci = path[i++];){
16146 if(ci.nodeName == 'TABLE'){
16147 return 0
16148 }
16149 if(tag == ci.nodeName.toLowerCase()){
16150 return 1
16151 };
16152 }
16153 return 0;
16154
16155 },
16156 queryCommandValue:function (command) {
16157 var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul';
16158 var path = this.selection.getStartElementPath(),
16159 node;
16160 for(var i= 0,ci;ci = path[i++];){
16161 if(ci.nodeName == 'TABLE'){
16162 node = null;
16163 break;
16164 }
16165 if(tag == ci.nodeName.toLowerCase()){
16166 node = ci;
16167 break;
16168 };
16169 }
16170 return node ? getStyle(node) || domUtils.getComputedStyle(node, 'list-style-type') : null;
16171 }
16172 };
16173};
16174
16175
16176
16177// plugins/source.js
16178/**
16179 * 源码编辑插件
16180 * @file
16181 * @since 1.2.6.1
16182 */
16183
16184(function (){
16185 var sourceEditors = {
16186 textarea: function (editor, holder){
16187 var textarea = holder.ownerDocument.createElement('textarea');
16188 textarea.style.cssText = 'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;';
16189 // todo: IE下只有onresize属性可用... 很纠结
16190 if (browser.ie && browser.version < 8) {
16191 textarea.style.width = holder.offsetWidth + 'px';
16192 textarea.style.height = holder.offsetHeight + 'px';
16193 holder.onresize = function (){
16194 textarea.style.width = holder.offsetWidth + 'px';
16195 textarea.style.height = holder.offsetHeight + 'px';
16196 };
16197 }
16198 holder.appendChild(textarea);
16199 return {
16200 setContent: function (content){
16201 textarea.value = content;
16202 },
16203 getContent: function (){
16204 return textarea.value;
16205 },
16206 select: function (){
16207 var range;
16208 if (browser.ie) {
16209 range = textarea.createTextRange();
16210 range.collapse(true);
16211 range.select();
16212 } else {
16213 //todo: chrome下无法设置焦点
16214 textarea.setSelectionRange(0, 0);
16215 textarea.focus();
16216 }
16217 },
16218 dispose: function (){
16219 holder.removeChild(textarea);
16220 // todo
16221 holder.onresize = null;
16222 textarea = null;
16223 holder = null;
16224 }
16225 };
16226 },
16227 codemirror: function (editor, holder){
16228
16229 var codeEditor = window.CodeMirror(holder, {
16230 mode: "text/html",
16231 tabMode: "indent",
16232 lineNumbers: true,
16233 lineWrapping:true
16234 });
16235 var dom = codeEditor.getWrapperElement();
16236 dom.style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;';
16237 codeEditor.getScrollerElement().style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;';
16238 codeEditor.refresh();
16239 return {
16240 getCodeMirror:function(){
16241 return codeEditor;
16242 },
16243 setContent: function (content){
16244 codeEditor.setValue(content);
16245 },
16246 getContent: function (){
16247 return codeEditor.getValue();
16248 },
16249 select: function (){
16250 codeEditor.focus();
16251 },
16252 dispose: function (){
16253 holder.removeChild(dom);
16254 dom = null;
16255 codeEditor = null;
16256 }
16257 };
16258 }
16259 };
16260
16261 UE.plugins['source'] = function (){
16262 var me = this;
16263 var opt = this.options;
16264 var sourceMode = false;
16265 var sourceEditor;
16266 var orgSetContent;
16267 opt.sourceEditor = browser.ie ? 'textarea' : (opt.sourceEditor || 'codemirror');
16268
16269 me.setOpt({
16270 sourceEditorFirst:false
16271 });
16272 function createSourceEditor(holder){
16273 return sourceEditors[opt.sourceEditor == 'codemirror' && window.CodeMirror ? 'codemirror' : 'textarea'](me, holder);
16274 }
16275
16276 var bakCssText;
16277 //解决在源码模式下getContent不能得到最新的内容问题
16278 var oldGetContent,
16279 bakAddress;
16280
16281 /**
16282 * 切换源码模式和编辑模式
16283 * @command source
16284 * @method execCommand
16285 * @param { String } cmd 命令字符串
16286 * @example
16287 * ```javascript
16288 * editor.execCommand( 'source');
16289 * ```
16290 */
16291
16292 /**
16293 * 查询当前编辑区域的状态是源码模式还是可视化模式
16294 * @command source
16295 * @method queryCommandState
16296 * @param { String } cmd 命令字符串
16297 * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
16298 * @example
16299 * ```javascript
16300 * editor.queryCommandState( 'source' );
16301 * ```
16302 */
16303
16304 me.commands['source'] = {
16305 execCommand: function (){
16306
16307 sourceMode = !sourceMode;
16308 if (sourceMode) {
16309 bakAddress = me.selection.getRange().createAddress(false,true);
16310 me.undoManger && me.undoManger.save(true);
16311 if(browser.gecko){
16312 me.body.contentEditable = false;
16313 }
16314
16315 bakCssText = me.iframe.style.cssText;
16316 me.iframe.style.cssText += 'position:absolute;left:-32768px;top:-32768px;';
16317
16318
16319 me.fireEvent('beforegetcontent');
16320 var root = UE.htmlparser(me.body.innerHTML);
16321 me.filterOutputRule(root);
16322 root.traversal(function (node) {
16323 if (node.type == 'element') {
16324 switch (node.tagName) {
16325 case 'td':
16326 case 'th':
16327 case 'caption':
16328 if(node.children && node.children.length == 1){
16329 if(node.firstChild().tagName == 'br' ){
16330 node.removeChild(node.firstChild())
16331 }
16332 };
16333 break;
16334 case 'pre':
16335 node.innerText(node.innerText().replace(/&nbsp;/g,' '))
16336
16337 }
16338 }
16339 });
16340
16341 me.fireEvent('aftergetcontent');
16342
16343 var content = root.toHtml(true);
16344
16345 sourceEditor = createSourceEditor(me.iframe.parentNode);
16346
16347 sourceEditor.setContent(content);
16348
16349 orgSetContent = me.setContent;
16350
16351 me.setContent = function(html){
16352 //这里暂时不触发事件,防止报错
16353 var root = UE.htmlparser(html);
16354 me.filterInputRule(root);
16355 html = root.toHtml();
16356 sourceEditor.setContent(html);
16357 };
16358
16359 setTimeout(function (){
16360 sourceEditor.select();
16361 me.addListener('fullscreenchanged', function(){
16362 try{
16363 sourceEditor.getCodeMirror().refresh()
16364 }catch(e){}
16365 });
16366 });
16367
16368 //重置getContent,源码模式下取值也能是最新的数据
16369 oldGetContent = me.getContent;
16370 me.getContent = function (){
16371 return sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
16372 };
16373 } else {
16374 me.iframe.style.cssText = bakCssText;
16375 var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
16376 //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
16377 cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){
16378 if(b && !dtd.$inlineWithA[b.toLowerCase()]){
16379 return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,'');
16380 }
16381 return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'')
16382 });
16383
16384 me.setContent = orgSetContent;
16385
16386 me.setContent(cont);
16387 sourceEditor.dispose();
16388 sourceEditor = null;
16389 //还原getContent方法
16390 me.getContent = oldGetContent;
16391 var first = me.body.firstChild;
16392 //trace:1106 都删除空了,下边会报错,所以补充一个p占位
16393 if(!first){
16394 me.body.innerHTML = '<p>'+(browser.ie?'':'<br/>')+'</p>';
16395 first = me.body.firstChild;
16396 }
16397
16398
16399 //要在ifm为显示时ff才能取到selection,否则报错
16400 //这里不能比较位置了
16401 me.undoManger && me.undoManger.save(true);
16402
16403 if(browser.gecko){
16404
16405 var input = document.createElement('input');
16406 input.style.cssText = 'position:absolute;left:0;top:-32768px';
16407
16408 document.body.appendChild(input);
16409
16410 me.body.contentEditable = false;
16411 setTimeout(function(){
16412 domUtils.setViewportOffset(input, { left: -32768, top: 0 });
16413 input.focus();
16414 setTimeout(function(){
16415 me.body.contentEditable = true;
16416 me.selection.getRange().moveToAddress(bakAddress).select(true);
16417 domUtils.remove(input);
16418 });
16419
16420 });
16421 }else{
16422 //ie下有可能报错,比如在代码顶头的情况
16423 try{
16424 me.selection.getRange().moveToAddress(bakAddress).select(true);
16425 }catch(e){}
16426
16427 }
16428 }
16429 this.fireEvent('sourcemodechanged', sourceMode);
16430 },
16431 queryCommandState: function (){
16432 return sourceMode|0;
16433 },
16434 notNeedUndo : 1
16435 };
16436 var oldQueryCommandState = me.queryCommandState;
16437
16438 me.queryCommandState = function (cmdName){
16439 cmdName = cmdName.toLowerCase();
16440 if (sourceMode) {
16441 //源码模式下可以开启的命令
16442 return cmdName in {
16443 'source' : 1,
16444 'fullscreen' : 1
16445 } ? 1 : -1
16446 }
16447 return oldQueryCommandState.apply(this, arguments);
16448 };
16449
16450 if(opt.sourceEditor == "codemirror"){
16451
16452 me.addListener("ready",function(){
16453 utils.loadFile(document,{
16454 src : opt.codeMirrorJsUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.js",
16455 tag : "script",
16456 type : "text/javascript",
16457 defer : "defer"
16458 },function(){
16459 if(opt.sourceEditorFirst){
16460 setTimeout(function(){
16461 me.execCommand("source");
16462 },0);
16463 }
16464 });
16465 utils.loadFile(document,{
16466 tag : "link",
16467 rel : "stylesheet",
16468 type : "text/css",
16469 href : opt.codeMirrorCssUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.css"
16470 });
16471
16472 });
16473 }
16474
16475 };
16476
16477})();
16478
16479// plugins/enterkey.js
16480///import core
16481///import plugins/undo.js
16482///commands 设置回车标签p或br
16483///commandsName EnterKey
16484///commandsTitle 设置回车标签p或br
16485/**
16486 * @description 处理回车
16487 * @author zhanyi
16488 */
16489UE.plugins['enterkey'] = function() {
16490 var hTag,
16491 me = this,
16492 tag = me.options.enterTag;
16493 me.addListener('keyup', function(type, evt) {
16494
16495 var keyCode = evt.keyCode || evt.which;
16496 if (keyCode == 13) {
16497 var range = me.selection.getRange(),
16498 start = range.startContainer,
16499 doSave;
16500
16501 //修正在h1-h6里边回车后不能嵌套p的问题
16502 if (!browser.ie) {
16503
16504 if (/h\d/i.test(hTag)) {
16505 if (browser.gecko) {
16506 var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true);
16507 if (!h) {
16508 me.document.execCommand('formatBlock', false, '<p>');
16509 doSave = 1;
16510 }
16511 } else {
16512 //chrome remove div
16513 if (start.nodeType == 1) {
16514 var tmp = me.document.createTextNode(''),div;
16515 range.insertNode(tmp);
16516 div = domUtils.findParentByTagName(tmp, 'div', true);
16517 if (div) {
16518 var p = me.document.createElement('p');
16519 while (div.firstChild) {
16520 p.appendChild(div.firstChild);
16521 }
16522 div.parentNode.insertBefore(p, div);
16523 domUtils.remove(div);
16524 range.setStartBefore(tmp).setCursor();
16525 doSave = 1;
16526 }
16527 domUtils.remove(tmp);
16528
16529 }
16530 }
16531
16532 if (me.undoManger && doSave) {
16533 me.undoManger.save();
16534 }
16535 }
16536 //没有站位符,会出现多行的问题
16537 browser.opera && range.select();
16538 }else{
16539 me.fireEvent('saveScene',true,true)
16540 }
16541 }
16542 });
16543
16544 me.addListener('keydown', function(type, evt) {
16545 var keyCode = evt.keyCode || evt.which;
16546 if (keyCode == 13) {//回车
16547 if(me.fireEvent('beforeenterkeydown')){
16548 domUtils.preventDefault(evt);
16549 return;
16550 }
16551 me.fireEvent('saveScene',true,true);
16552 hTag = '';
16553
16554
16555 var range = me.selection.getRange();
16556
16557 if (!range.collapsed) {
16558 //跨td不能删
16559 var start = range.startContainer,
16560 end = range.endContainer,
16561 startTd = domUtils.findParentByTagName(start, 'td', true),
16562 endTd = domUtils.findParentByTagName(end, 'td', true);
16563 if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) {
16564 evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
16565 return;
16566 }
16567 }
16568 if (tag == 'p') {
16569
16570
16571 if (!browser.ie) {
16572
16573 start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true);
16574
16575 //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
16576 //trace:2431
16577 if (!start && !browser.opera) {
16578
16579 me.document.execCommand('formatBlock', false, '<p>');
16580
16581 if (browser.gecko) {
16582 range = me.selection.getRange();
16583 start = domUtils.findParentByTagName(range.startContainer, 'p', true);
16584 start && domUtils.removeDirtyAttr(start);
16585 }
16586
16587
16588 } else {
16589 hTag = start.tagName;
16590 start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start);
16591 }
16592
16593 }
16594
16595 } else {
16596 evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
16597
16598 if (!range.collapsed) {
16599 range.deleteContents();
16600 start = range.startContainer;
16601 if (start.nodeType == 1 && (start = start.childNodes[range.startOffset])) {
16602 while (start.nodeType == 1) {
16603 if (dtd.$empty[start.tagName]) {
16604 range.setStartBefore(start).setCursor();
16605 if (me.undoManger) {
16606 me.undoManger.save();
16607 }
16608 return false;
16609 }
16610 if (!start.firstChild) {
16611 var br = range.document.createElement('br');
16612 start.appendChild(br);
16613 range.setStart(start, 0).setCursor();
16614 if (me.undoManger) {
16615 me.undoManger.save();
16616 }
16617 return false;
16618 }
16619 start = start.firstChild;
16620 }
16621 if (start === range.startContainer.childNodes[range.startOffset]) {
16622 br = range.document.createElement('br');
16623 range.insertNode(br).setCursor();
16624
16625 } else {
16626 range.setStart(start, 0).setCursor();
16627 }
16628
16629
16630 } else {
16631 br = range.document.createElement('br');
16632 range.insertNode(br).setStartAfter(br).setCursor();
16633 }
16634
16635
16636 } else {
16637 br = range.document.createElement('br');
16638 range.insertNode(br);
16639 var parent = br.parentNode;
16640 if (parent.lastChild === br) {
16641 br.parentNode.insertBefore(br.cloneNode(true), br);
16642 range.setStartBefore(br);
16643 } else {
16644 range.setStartAfter(br);
16645 }
16646 range.setCursor();
16647
16648 }
16649
16650 }
16651
16652 }
16653 });
16654};
16655
16656
16657// plugins/keystrokes.js
16658/* 处理特殊键的兼容性问题 */
16659UE.plugins['keystrokes'] = function() {
16660 var me = this;
16661 var collapsed = true;
16662 me.addListener('keydown', function(type, evt) {
16663 var keyCode = evt.keyCode || evt.which,
16664 rng = me.selection.getRange();
16665
16666 //处理全选的情况
16667 if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90
16668 || keyCode >= 48 && keyCode <= 57 ||
16669 keyCode >= 96 && keyCode <= 111 || {
16670 13:1,
16671 8:1,
16672 46:1
16673 }[keyCode])
16674 ){
16675
16676 var tmpNode = rng.startContainer;
16677 if(domUtils.isFillChar(tmpNode)){
16678 rng.setStartBefore(tmpNode)
16679 }
16680 tmpNode = rng.endContainer;
16681 if(domUtils.isFillChar(tmpNode)){
16682 rng.setEndAfter(tmpNode)
16683 }
16684 rng.txtToElmBoundary();
16685 //结束边界可能放到了br的前边,要把br包含进来
16686 // x[xxx]<br/>
16687 if(rng.endContainer && rng.endContainer.nodeType == 1){
16688 tmpNode = rng.endContainer.childNodes[rng.endOffset];
16689 if(tmpNode && domUtils.isBr(tmpNode)){
16690 rng.setEndAfter(tmpNode);
16691 }
16692 }
16693 if(rng.startOffset == 0){
16694 tmpNode = rng.startContainer;
16695 if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
16696 tmpNode = rng.endContainer;
16697 if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
16698 me.fireEvent('saveScene');
16699 me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
16700 rng.setStart(me.body.firstChild,0).setCursor(false,true);
16701 me._selectionChange();
16702 return;
16703 }
16704 }
16705 }
16706 }
16707
16708 //处理backspace
16709 if (keyCode == keymap.Backspace) {
16710 rng = me.selection.getRange();
16711 collapsed = rng.collapsed;
16712 if(me.fireEvent('delkeydown',evt)){
16713 return;
16714 }
16715 var start,end;
16716 //避免按两次删除才能生效的问题
16717 if(rng.collapsed && rng.inFillChar()){
16718 start = rng.startContainer;
16719
16720 if(domUtils.isFillChar(start)){
16721 rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
16722 domUtils.remove(start)
16723 }else{
16724 start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
16725 rng.startOffset--;
16726 rng.collapse(true).select(true)
16727 }
16728 }
16729
16730 //解决选中control元素不能删除的问题
16731 if (start = rng.getClosedNode()) {
16732 me.fireEvent('saveScene');
16733 rng.setStartBefore(start);
16734 domUtils.remove(start);
16735 rng.setCursor();
16736 me.fireEvent('saveScene');
16737 domUtils.preventDefault(evt);
16738 return;
16739 }
16740 //阻止在table上的删除
16741 if (!browser.ie) {
16742 start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
16743 end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
16744 if (start && !end || !start && end || start !== end) {
16745 evt.preventDefault();
16746 return;
16747 }
16748 }
16749
16750 }
16751 //处理tab键的逻辑
16752 if (keyCode == keymap.Tab) {
16753 //不处理以下标签
16754 var excludeTagNameForTabKey = {
16755 'ol' : 1,
16756 'ul' : 1,
16757 'table':1
16758 };
16759 //处理组件里的tab按下事件
16760 if(me.fireEvent('tabkeydown',evt)){
16761 domUtils.preventDefault(evt);
16762 return;
16763 }
16764 var range = me.selection.getRange();
16765 me.fireEvent('saveScene');
16766 for (var i = 0,txt = '',tabSize = me.options.tabSize|| 4,tabNode = me.options.tabNode || '&nbsp;'; i < tabSize; i++) {
16767 txt += tabNode;
16768 }
16769 var span = me.document.createElement('span');
16770 span.innerHTML = txt + domUtils.fillChar;
16771 if (range.collapsed) {
16772 range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
16773 } else {
16774 var filterFn = function(node) {
16775 return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()]
16776
16777 };
16778 //普通的情况
16779 start = domUtils.findParent(range.startContainer, filterFn,true);
16780 end = domUtils.findParent(range.endContainer, filterFn,true);
16781 if (start && end && start === end) {
16782 range.deleteContents();
16783 range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
16784 } else {
16785 var bookmark = range.createBookmark();
16786 range.enlarge(true);
16787 var bookmark2 = range.createBookmark(),
16788 current = domUtils.getNextDomNode(bookmark2.start, false, filterFn);
16789 while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
16790 current.insertBefore(span.cloneNode(true).firstChild, current.firstChild);
16791 current = domUtils.getNextDomNode(current, false, filterFn);
16792 }
16793 range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select();
16794 }
16795 }
16796 domUtils.preventDefault(evt)
16797 }
16798 //trace:1634
16799 //ff的del键在容器空的时候,也会删除
16800 if(browser.gecko && keyCode == 46){
16801 range = me.selection.getRange();
16802 if(range.collapsed){
16803 start = range.startContainer;
16804 if(domUtils.isEmptyBlock(start)){
16805 var parent = start.parentNode;
16806 while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
16807 start = parent;
16808 parent = parent.parentNode;
16809 }
16810 if(start === parent.lastChild)
16811 evt.preventDefault();
16812 return;
16813 }
16814 }
16815 }
16816 });
16817 me.addListener('keyup', function(type, evt) {
16818 var keyCode = evt.keyCode || evt.which,
16819 rng,me = this;
16820 if(keyCode == keymap.Backspace){
16821 if(me.fireEvent('delkeyup')){
16822 return;
16823 }
16824 rng = me.selection.getRange();
16825 if(rng.collapsed){
16826 var tmpNode,
16827 autoClearTagName = ['h1','h2','h3','h4','h5','h6'];
16828 if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){
16829 if(domUtils.isEmptyBlock(tmpNode)){
16830 var pre = tmpNode.previousSibling;
16831 if(pre && pre.nodeName != 'TABLE'){
16832 domUtils.remove(tmpNode);
16833 rng.setStartAtLast(pre).setCursor(false,true);
16834 return;
16835 }else{
16836 var next = tmpNode.nextSibling;
16837 if(next && next.nodeName != 'TABLE'){
16838 domUtils.remove(tmpNode);
16839 rng.setStartAtFirst(next).setCursor(false,true);
16840 return;
16841 }
16842 }
16843 }
16844 }
16845 //处理当删除到body时,要重新给p标签展位
16846 if(domUtils.isBody(rng.startContainer)){
16847 var tmpNode = domUtils.createElement(me.document,'p',{
16848 'innerHTML' : browser.ie ? domUtils.fillChar : '<br/>'
16849 });
16850 rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
16851 }
16852 }
16853
16854
16855 //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
16856 if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){
16857 if(browser.ie){
16858 var span = rng.document.createElement('span');
16859 rng.insertNode(span).setStartBefore(span).collapse(true);
16860 rng.select();
16861 domUtils.remove(span)
16862 }else{
16863 rng.select()
16864 }
16865
16866 }
16867 }
16868
16869
16870 })
16871};
16872
16873// plugins/fiximgclick.js
16874///import core
16875///commands 修复chrome下图片不能点击的问题,出现八个角可改变大小
16876///commandsName FixImgClick
16877///commandsTitle 修复chrome下图片不能点击的问题,出现八个角可改变大小
16878//修复chrome下图片不能点击的问题,出现八个角可改变大小
16879
16880UE.plugins['fiximgclick'] = (function () {
16881
16882 var elementUpdated = false;
16883 function Scale() {
16884 this.editor = null;
16885 this.resizer = null;
16886 this.cover = null;
16887 this.doc = document;
16888 this.prePos = {x: 0, y: 0};
16889 this.startPos = {x: 0, y: 0};
16890 }
16891
16892 (function () {
16893 var rect = [
16894 //[left, top, width, height]
16895 [0, 0, -1, -1],
16896 [0, 0, 0, -1],
16897 [0, 0, 1, -1],
16898 [0, 0, -1, 0],
16899 [0, 0, 1, 0],
16900 [0, 0, -1, 1],
16901 [0, 0, 0, 1],
16902 [0, 0, 1, 1]
16903 ];
16904
16905 Scale.prototype = {
16906 init: function (editor) {
16907 var me = this;
16908 me.editor = editor;
16909 me.startPos = this.prePos = {x: 0, y: 0};
16910 me.dragId = -1;
16911
16912 var hands = [],
16913 cover = me.cover = document.createElement('div'),
16914 resizer = me.resizer = document.createElement('div');
16915
16916 cover.id = me.editor.ui.id + '_imagescale_cover';
16917 cover.style.cssText = 'position:absolute;display:none;z-index:' + (me.editor.options.zIndex) + ';filter:alpha(opacity=0); opacity:0;background:#CCC;';
16918 domUtils.on(cover, 'mousedown click', function () {
16919 me.hide();
16920 });
16921
16922 for (i = 0; i < 8; i++) {
16923 hands.push('<span class="edui-editor-imagescale-hand' + i + '"></span>');
16924 }
16925 resizer.id = me.editor.ui.id + '_imagescale';
16926 resizer.className = 'edui-editor-imagescale';
16927 resizer.innerHTML = hands.join('');
16928 resizer.style.cssText += ';display:none;border:1px solid #3b77ff;z-index:' + (me.editor.options.zIndex) + ';';
16929
16930 me.editor.ui.getDom().appendChild(cover);
16931 me.editor.ui.getDom().appendChild(resizer);
16932
16933 me.initStyle();
16934 me.initEvents();
16935 },
16936 initStyle: function () {
16937 utils.cssRule('imagescale', '.edui-editor-imagescale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}' +
16938 '.edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
16939 + '.edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
16940 + '.edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
16941 + '.edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
16942 + '.edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
16943 + '.edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
16944 + '.edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
16945 + '.edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
16946 + '.edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
16947 },
16948 initEvents: function () {
16949 var me = this;
16950
16951 me.startPos.x = me.startPos.y = 0;
16952 me.isDraging = false;
16953 },
16954 _eventHandler: function (e) {
16955 var me = this;
16956 switch (e.type) {
16957 case 'mousedown':
16958 var hand = e.target || e.srcElement, hand;
16959 if (hand.className.indexOf('edui-editor-imagescale-hand') != -1 && me.dragId == -1) {
16960 me.dragId = hand.className.slice(-1);
16961 me.startPos.x = me.prePos.x = e.clientX;
16962 me.startPos.y = me.prePos.y = e.clientY;
16963 domUtils.on(me.doc,'mousemove', me.proxy(me._eventHandler, me));
16964 }
16965 break;
16966 case 'mousemove':
16967 if (me.dragId != -1) {
16968 me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
16969 me.prePos.x = e.clientX;
16970 me.prePos.y = e.clientY;
16971 elementUpdated = true;
16972 me.updateTargetElement();
16973
16974 }
16975 break;
16976 case 'mouseup':
16977 if (me.dragId != -1) {
16978 me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
16979 me.updateTargetElement();
16980 if (me.target.parentNode) me.attachTo(me.target);
16981 me.dragId = -1;
16982 }
16983 domUtils.un(me.doc,'mousemove', me.proxy(me._eventHandler, me));
16984 //修复只是点击挪动点,但没有改变大小,不应该触发contentchange
16985 if(elementUpdated){
16986 elementUpdated = false;
16987 me.editor.fireEvent('contentchange');
16988 }
16989
16990 break;
16991 default:
16992 break;
16993 }
16994 },
16995 updateTargetElement: function () {
16996 var me = this;
16997 domUtils.setStyles(me.target, {
16998 'width': me.resizer.style.width,
16999 'height': me.resizer.style.height
17000 });
17001 me.target.width = parseInt(me.resizer.style.width);
17002 me.target.height = parseInt(me.resizer.style.height);
17003 me.attachTo(me.target);
17004 },
17005 updateContainerStyle: function (dir, offset) {
17006 var me = this,
17007 dom = me.resizer, tmp;
17008
17009 if (rect[dir][0] != 0) {
17010 tmp = parseInt(dom.style.left) + offset.x;
17011 dom.style.left = me._validScaledProp('left', tmp) + 'px';
17012 }
17013 if (rect[dir][1] != 0) {
17014 tmp = parseInt(dom.style.top) + offset.y;
17015 dom.style.top = me._validScaledProp('top', tmp) + 'px';
17016 }
17017 if (rect[dir][2] != 0) {
17018 tmp = dom.clientWidth + rect[dir][2] * offset.x;
17019 dom.style.width = me._validScaledProp('width', tmp) + 'px';
17020 }
17021 if (rect[dir][3] != 0) {
17022 tmp = dom.clientHeight + rect[dir][3] * offset.y;
17023 dom.style.height = me._validScaledProp('height', tmp) + 'px';
17024 }
17025 },
17026 _validScaledProp: function (prop, value) {
17027 var ele = this.resizer,
17028 wrap = document;
17029
17030 value = isNaN(value) ? 0 : value;
17031 switch (prop) {
17032 case 'left':
17033 return value < 0 ? 0 : (value + ele.clientWidth) > wrap.clientWidth ? wrap.clientWidth - ele.clientWidth : value;
17034 case 'top':
17035 return value < 0 ? 0 : (value + ele.clientHeight) > wrap.clientHeight ? wrap.clientHeight - ele.clientHeight : value;
17036 case 'width':
17037 return value <= 0 ? 1 : (value + ele.offsetLeft) > wrap.clientWidth ? wrap.clientWidth - ele.offsetLeft : value;
17038 case 'height':
17039 return value <= 0 ? 1 : (value + ele.offsetTop) > wrap.clientHeight ? wrap.clientHeight - ele.offsetTop : value;
17040 }
17041 },
17042 hideCover: function () {
17043 this.cover.style.display = 'none';
17044 },
17045 showCover: function () {
17046 var me = this,
17047 editorPos = domUtils.getXY(me.editor.ui.getDom()),
17048 iframePos = domUtils.getXY(me.editor.iframe);
17049
17050 domUtils.setStyles(me.cover, {
17051 'width': me.editor.iframe.offsetWidth + 'px',
17052 'height': me.editor.iframe.offsetHeight + 'px',
17053 'top': iframePos.y - editorPos.y + 'px',
17054 'left': iframePos.x - editorPos.x + 'px',
17055 'position': 'absolute',
17056 'display': ''
17057 })
17058 },
17059 show: function (targetObj) {
17060 var me = this;
17061 me.resizer.style.display = 'block';
17062 if(targetObj) me.attachTo(targetObj);
17063
17064 domUtils.on(this.resizer, 'mousedown', me.proxy(me._eventHandler, me));
17065 domUtils.on(me.doc, 'mouseup', me.proxy(me._eventHandler, me));
17066
17067 me.showCover();
17068 me.editor.fireEvent('afterscaleshow', me);
17069 me.editor.fireEvent('saveScene');
17070 },
17071 hide: function () {
17072 var me = this;
17073 me.hideCover();
17074 me.resizer.style.display = 'none';
17075
17076 domUtils.un(me.resizer, 'mousedown', me.proxy(me._eventHandler, me));
17077 domUtils.un(me.doc, 'mouseup', me.proxy(me._eventHandler, me));
17078 me.editor.fireEvent('afterscalehide', me);
17079 },
17080 proxy: function( fn, context ) {
17081 return function(e) {
17082 return fn.apply( context || this, arguments);
17083 };
17084 },
17085 attachTo: function (targetObj) {
17086 var me = this,
17087 target = me.target = targetObj,
17088 resizer = this.resizer,
17089 imgPos = domUtils.getXY(target),
17090 iframePos = domUtils.getXY(me.editor.iframe),
17091 editorPos = domUtils.getXY(resizer.parentNode);
17092
17093 domUtils.setStyles(resizer, {
17094 'width': target.width + 'px',
17095 'height': target.height + 'px',
17096 'left': iframePos.x + imgPos.x - me.editor.document.body.scrollLeft - editorPos.x - parseInt(resizer.style.borderLeftWidth) + 'px',
17097 'top': iframePos.y + imgPos.y - me.editor.document.body.scrollTop - editorPos.y - parseInt(resizer.style.borderTopWidth) + 'px'
17098 });
17099 }
17100 }
17101 })();
17102
17103 return function () {
17104 var me = this,
17105 imageScale;
17106
17107 me.setOpt('imageScaleEnabled', true);
17108
17109 if ( !browser.ie && me.options.imageScaleEnabled) {
17110 me.addListener('click', function (type, e) {
17111
17112 var range = me.selection.getRange(),
17113 img = range.getClosedNode();
17114
17115 if (img && img.tagName == 'IMG' && me.body.contentEditable!="false") {
17116
17117 if (img.className.indexOf("edui-faked-music") != -1 ||
17118 img.getAttribute("anchorname") ||
17119 domUtils.hasClass(img, 'loadingclass') ||
17120 domUtils.hasClass(img, 'loaderrorclass')) { return }
17121
17122 if (!imageScale) {
17123 imageScale = new Scale();
17124 imageScale.init(me);
17125 me.ui.getDom().appendChild(imageScale.resizer);
17126
17127 var _keyDownHandler = function (e) {
17128 imageScale.hide();
17129 if(imageScale.target) me.selection.getRange().selectNode(imageScale.target).select();
17130 }, _mouseDownHandler = function (e) {
17131 var ele = e.target || e.srcElement;
17132 if (ele && (ele.className===undefined || ele.className.indexOf('edui-editor-imagescale') == -1)) {
17133 _keyDownHandler(e);
17134 }
17135 }, timer;
17136
17137 me.addListener('afterscaleshow', function (e) {
17138 me.addListener('beforekeydown', _keyDownHandler);
17139 me.addListener('beforemousedown', _mouseDownHandler);
17140 domUtils.on(document, 'keydown', _keyDownHandler);
17141 domUtils.on(document,'mousedown', _mouseDownHandler);
17142 me.selection.getNative().removeAllRanges();
17143 });
17144 me.addListener('afterscalehide', function (e) {
17145 me.removeListener('beforekeydown', _keyDownHandler);
17146 me.removeListener('beforemousedown', _mouseDownHandler);
17147 domUtils.un(document, 'keydown', _keyDownHandler);
17148 domUtils.un(document,'mousedown', _mouseDownHandler);
17149 var target = imageScale.target;
17150 if (target.parentNode) {
17151 me.selection.getRange().selectNode(target).select();
17152 }
17153 });
17154 //TODO 有iframe的情况,mousedown不能往下传。。
17155 domUtils.on(imageScale.resizer, 'mousedown', function (e) {
17156 me.selection.getNative().removeAllRanges();
17157 var ele = e.target || e.srcElement;
17158 if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
17159 timer = setTimeout(function () {
17160 imageScale.hide();
17161 if(imageScale.target) me.selection.getRange().selectNode(ele).select();
17162 }, 200);
17163 }
17164 });
17165 domUtils.on(imageScale.resizer, 'mouseup', function (e) {
17166 var ele = e.target || e.srcElement;
17167 if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
17168 clearTimeout(timer);
17169 }
17170 });
17171 }
17172 imageScale.show(img);
17173 } else {
17174 if (imageScale && imageScale.resizer.style.display != 'none') imageScale.hide();
17175 }
17176 });
17177 }
17178
17179 if (browser.webkit) {
17180 me.addListener('click', function (type, e) {
17181 if (e.target.tagName == 'IMG' && me.body.contentEditable!="false") {
17182 var range = new dom.Range(me.document);
17183 range.selectNode(e.target).select();
17184 }
17185 });
17186 }
17187 }
17188})();
17189
17190// plugins/autolink.js
17191///import core
17192///commands 为非ie浏览器自动添加a标签
17193///commandsName AutoLink
17194///commandsTitle 自动增加链接
17195/**
17196 * @description 为非ie浏览器自动添加a标签
17197 * @author zhanyi
17198 */
17199
17200UE.plugin.register('autolink',function(){
17201 var cont = 0;
17202
17203 return !browser.ie ? {
17204
17205 bindEvents:{
17206 'reset' : function(){
17207 cont = 0;
17208 },
17209 'keydown':function(type, evt) {
17210 var me = this;
17211 var keyCode = evt.keyCode || evt.which;
17212
17213 if (keyCode == 32 || keyCode == 13) {
17214
17215 var sel = me.selection.getNative(),
17216 range = sel.getRangeAt(0).cloneRange(),
17217 offset,
17218 charCode;
17219
17220 var start = range.startContainer;
17221 while (start.nodeType == 1 && range.startOffset > 0) {
17222 start = range.startContainer.childNodes[range.startOffset - 1];
17223 if (!start){
17224 break;
17225 }
17226 range.setStart(start, start.nodeType == 1 ? start.childNodes.length : start.nodeValue.length);
17227 range.collapse(true);
17228 start = range.startContainer;
17229 }
17230
17231 do{
17232 if (range.startOffset == 0) {
17233 start = range.startContainer.previousSibling;
17234
17235 while (start && start.nodeType == 1) {
17236 start = start.lastChild;
17237 }
17238 if (!start || domUtils.isFillChar(start)){
17239 break;
17240 }
17241 offset = start.nodeValue.length;
17242 } else {
17243 start = range.startContainer;
17244 offset = range.startOffset;
17245 }
17246 range.setStart(start, offset - 1);
17247 charCode = range.toString().charCodeAt(0);
17248 } while (charCode != 160 && charCode != 32);
17249
17250 if (range.toString().replace(new RegExp(domUtils.fillChar, 'g'), '').match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i)) {
17251 while(range.toString().length){
17252 if(/^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test(range.toString())){
17253 break;
17254 }
17255 try{
17256 range.setStart(range.startContainer,range.startOffset+1);
17257 }catch(e){
17258 //trace:2121
17259 var start = range.startContainer;
17260 while(!(next = start.nextSibling)){
17261 if(domUtils.isBody(start)){
17262 return;
17263 }
17264 start = start.parentNode;
17265
17266 }
17267 range.setStart(next,0);
17268
17269 }
17270
17271 }
17272 //range的开始边界已经在a标签里的不再处理
17273 if(domUtils.findParentByTagName(range.startContainer,'a',true)){
17274 return;
17275 }
17276 var a = me.document.createElement('a'),text = me.document.createTextNode(' '),href;
17277
17278 me.undoManger && me.undoManger.save();
17279 a.appendChild(range.extractContents());
17280 a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g,'');
17281 href = a.getAttribute("href").replace(new RegExp(domUtils.fillChar,'g'),'');
17282 href = /^(?:https?:\/\/)/ig.test(href) ? href : "http://"+ href;
17283 a.setAttribute('_src',utils.html(href));
17284 a.href = utils.html(href);
17285
17286 range.insertNode(a);
17287 a.parentNode.insertBefore(text, a.nextSibling);
17288 range.setStart(text, 0);
17289 range.collapse(true);
17290 sel.removeAllRanges();
17291 sel.addRange(range);
17292 me.undoManger && me.undoManger.save();
17293 }
17294 }
17295 }
17296 }
17297 }:{}
17298 },function(){
17299 var keyCodes = {
17300 37:1, 38:1, 39:1, 40:1,
17301 13:1,32:1
17302 };
17303 function checkIsCludeLink(node){
17304 if(node.nodeType == 3){
17305 return null
17306 }
17307 if(node.nodeName == 'A'){
17308 return node;
17309 }
17310 var lastChild = node.lastChild;
17311
17312 while(lastChild){
17313 if(lastChild.nodeName == 'A'){
17314 return lastChild;
17315 }
17316 if(lastChild.nodeType == 3){
17317 if(domUtils.isWhitespace(lastChild)){
17318 lastChild = lastChild.previousSibling;
17319 continue;
17320 }
17321 return null
17322 }
17323 lastChild = lastChild.lastChild;
17324 }
17325 }
17326 browser.ie && this.addListener('keyup',function(cmd,evt){
17327 var me = this,keyCode = evt.keyCode;
17328 if(keyCodes[keyCode]){
17329 var rng = me.selection.getRange();
17330 var start = rng.startContainer;
17331
17332 if(keyCode == 13){
17333 while(start && !domUtils.isBody(start) && !domUtils.isBlockElm(start)){
17334 start = start.parentNode;
17335 }
17336 if(start && !domUtils.isBody(start) && start.nodeName == 'P'){
17337 var pre = start.previousSibling;
17338 if(pre && pre.nodeType == 1){
17339 var pre = checkIsCludeLink(pre);
17340 if(pre && !pre.getAttribute('_href')){
17341 domUtils.remove(pre,true);
17342 }
17343 }
17344 }
17345 }else if(keyCode == 32 ){
17346 if(start.nodeType == 3 && /^\s$/.test(start.nodeValue)){
17347 start = start.previousSibling;
17348 if(start && start.nodeName == 'A' && !start.getAttribute('_href')){
17349 domUtils.remove(start,true);
17350 }
17351 }
17352 }else {
17353 start = domUtils.findParentByTagName(start,'a',true);
17354 if(start && !start.getAttribute('_href')){
17355 var bk = rng.createBookmark();
17356
17357 domUtils.remove(start,true);
17358 rng.moveToBookmark(bk).select(true)
17359 }
17360 }
17361
17362 }
17363
17364
17365 });
17366 }
17367);
17368
17369// plugins/autoheight.js
17370///import core
17371///commands 当输入内容超过编辑器高度时,编辑器自动增高
17372///commandsName AutoHeight,autoHeightEnabled
17373///commandsTitle 自动增高
17374/**
17375 * @description 自动伸展
17376 * @author zhanyi
17377 */
17378UE.plugins['autoheight'] = function () {
17379 var me = this;
17380 //提供开关,就算加载也可以关闭
17381 me.autoHeightEnabled = me.options.autoHeightEnabled !== false;
17382 if (!me.autoHeightEnabled) {
17383 return;
17384 }
17385
17386 var bakOverflow,
17387 lastHeight = 0,
17388 options = me.options,
17389 currentHeight,
17390 timer;
17391
17392 function adjustHeight() {
17393 var me = this;
17394 clearTimeout(timer);
17395 if(isFullscreen)return;
17396 if (!me.queryCommandState || me.queryCommandState && me.queryCommandState('source') != 1) {
17397 timer = setTimeout(function(){
17398
17399 var node = me.body.lastChild;
17400 while(node && node.nodeType != 1){
17401 node = node.previousSibling;
17402 }
17403 if(node && node.nodeType == 1){
17404 node.style.clear = 'both';
17405 currentHeight = Math.max(domUtils.getXY(node).y + node.offsetHeight + 25 ,Math.max(options.minFrameHeight, options.initialFrameHeight)) ;
17406 if (currentHeight != lastHeight) {
17407 if (currentHeight !== parseInt(me.iframe.parentNode.style.height)) {
17408 me.iframe.parentNode.style.height = currentHeight + 'px';
17409 }
17410 me.body.style.height = currentHeight + 'px';
17411 lastHeight = currentHeight;
17412 }
17413 domUtils.removeStyle(node,'clear');
17414 }
17415
17416
17417 },50)
17418 }
17419 }
17420 var isFullscreen;
17421 me.addListener('fullscreenchanged',function(cmd,f){
17422 isFullscreen = f
17423 });
17424 me.addListener('destroy', function () {
17425 me.removeListener('contentchange afterinserthtml keyup mouseup',adjustHeight)
17426 });
17427 me.enableAutoHeight = function () {
17428 var me = this;
17429 if (!me.autoHeightEnabled) {
17430 return;
17431 }
17432 var doc = me.document;
17433 me.autoHeightEnabled = true;
17434 bakOverflow = doc.body.style.overflowY;
17435 doc.body.style.overflowY = 'hidden';
17436 me.addListener('contentchange afterinserthtml keyup mouseup',adjustHeight);
17437 //ff不给事件算得不对
17438
17439 setTimeout(function () {
17440 adjustHeight.call(me);
17441 }, browser.gecko ? 100 : 0);
17442 me.fireEvent('autoheightchanged', me.autoHeightEnabled);
17443 };
17444 me.disableAutoHeight = function () {
17445
17446 me.body.style.overflowY = bakOverflow || '';
17447
17448 me.removeListener('contentchange', adjustHeight);
17449 me.removeListener('keyup', adjustHeight);
17450 me.removeListener('mouseup', adjustHeight);
17451 me.autoHeightEnabled = false;
17452 me.fireEvent('autoheightchanged', me.autoHeightEnabled);
17453 };
17454
17455 me.on('setHeight',function(){
17456 me.disableAutoHeight()
17457 });
17458 me.addListener('ready', function () {
17459 me.enableAutoHeight();
17460 //trace:1764
17461 var timer;
17462 domUtils.on(browser.ie ? me.body : me.document, browser.webkit ? 'dragover' : 'drop', function () {
17463 clearTimeout(timer);
17464 timer = setTimeout(function () {
17465 //trace:3681
17466 adjustHeight.call(me);
17467 }, 100);
17468
17469 });
17470 //修复内容过多时,回到顶部,顶部内容被工具栏遮挡问题
17471 var lastScrollY;
17472 window.onscroll = function(){
17473 if(lastScrollY === null){
17474 lastScrollY = this.scrollY
17475 }else if(this.scrollY == 0 && lastScrollY != 0){
17476 me.window.scrollTo(0,0);
17477 lastScrollY = null;
17478 }
17479 }
17480 });
17481
17482
17483};
17484
17485
17486
17487// plugins/autofloat.js
17488///import core
17489///commands 悬浮工具栏
17490///commandsName AutoFloat,autoFloatEnabled
17491///commandsTitle 悬浮工具栏
17492/**
17493 * modified by chengchao01
17494 * 注意: 引入此功能后,在IE6下会将body的背景图片覆盖掉!
17495 */
17496UE.plugins['autofloat'] = function() {
17497 var me = this,
17498 lang = me.getLang();
17499 me.setOpt({
17500 topOffset:0
17501 });
17502 var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
17503 topOffset = me.options.topOffset;
17504
17505
17506 //如果不固定toolbar的位置,则直接退出
17507 if(!optsAutoFloatEnabled){
17508 return;
17509 }
17510 var uiUtils = UE.ui.uiUtils,
17511 LteIE6 = browser.ie && browser.version <= 6,
17512 quirks = browser.quirks;
17513
17514 function checkHasUI(){
17515 if(!UE.ui){
17516 alert(lang.autofloatMsg);
17517 return 0;
17518 }
17519 return 1;
17520 }
17521 function fixIE6FixedPos(){
17522 var docStyle = document.body.style;
17523 docStyle.backgroundImage = 'url("about:blank")';
17524 docStyle.backgroundAttachment = 'fixed';
17525 }
17526 var bakCssText,
17527 placeHolder = document.createElement('div'),
17528 toolbarBox,orgTop,
17529 getPosition,
17530 flag =true; //ie7模式下需要偏移
17531 function setFloating(){
17532 var toobarBoxPos = domUtils.getXY(toolbarBox),
17533 origalFloat = domUtils.getComputedStyle(toolbarBox,'position'),
17534 origalLeft = domUtils.getComputedStyle(toolbarBox,'left');
17535 toolbarBox.style.width = toolbarBox.offsetWidth + 'px';
17536 toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
17537 toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
17538 if (LteIE6 || (quirks && browser.ie)) {
17539 if(toolbarBox.style.position != 'absolute'){
17540 toolbarBox.style.position = 'absolute';
17541 }
17542 toolbarBox.style.top = (document.body.scrollTop||document.documentElement.scrollTop) - orgTop + topOffset + 'px';
17543 } else {
17544 if (browser.ie7Compat && flag) {
17545 flag = false;
17546 toolbarBox.style.left = domUtils.getXY(toolbarBox).x - document.documentElement.getBoundingClientRect().left+2 + 'px';
17547 }
17548 if(toolbarBox.style.position != 'fixed'){
17549 toolbarBox.style.position = 'fixed';
17550 toolbarBox.style.top = topOffset +"px";
17551 ((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px');
17552 }
17553 }
17554 }
17555 function unsetFloating(){
17556 flag = true;
17557 if(placeHolder.parentNode){
17558 placeHolder.parentNode.removeChild(placeHolder);
17559 }
17560
17561 toolbarBox.style.cssText = bakCssText;
17562 }
17563
17564 function updateFloating(){
17565 var rect3 = getPosition(me.container);
17566 var offset=me.options.toolbarTopOffset||0;
17567 if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
17568 setFloating();
17569 }else{
17570 unsetFloating();
17571 }
17572 }
17573 var defer_updateFloating = utils.defer(function(){
17574 updateFloating();
17575 },browser.ie ? 200 : 100,true);
17576
17577 me.addListener('destroy',function(){
17578 domUtils.un(window, ['scroll','resize'], updateFloating);
17579 me.removeListener('keydown', defer_updateFloating);
17580 });
17581
17582 me.addListener('ready', function(){
17583 if(checkHasUI(me)){
17584 //加载了ui组件,但在new时,没有加载ui,导致编辑器实例上没有ui类,所以这里做判断
17585 if(!me.ui){
17586 return;
17587 }
17588 getPosition = uiUtils.getClientRect;
17589 toolbarBox = me.ui.getDom('toolbarbox');
17590 orgTop = getPosition(toolbarBox).top;
17591 bakCssText = toolbarBox.style.cssText;
17592 placeHolder.style.height = toolbarBox.offsetHeight + 'px';
17593 if(LteIE6){
17594 fixIE6FixedPos();
17595 }
17596 domUtils.on(window, ['scroll','resize'], updateFloating);
17597 me.addListener('keydown', defer_updateFloating);
17598
17599 me.addListener('beforefullscreenchange', function (t, enabled){
17600 if (enabled) {
17601 unsetFloating();
17602 }
17603 });
17604 me.addListener('fullscreenchanged', function (t, enabled){
17605 if (!enabled) {
17606 updateFloating();
17607 }
17608 });
17609 me.addListener('sourcemodechanged', function (t, enabled){
17610 setTimeout(function (){
17611 updateFloating();
17612 },0);
17613 });
17614 me.addListener("clearDoc",function(){
17615 setTimeout(function(){
17616 updateFloating();
17617 },0);
17618
17619 })
17620 }
17621 });
17622};
17623
17624
17625// plugins/video.js
17626/**
17627 * video插件, 为UEditor提供视频插入支持
17628 * @file
17629 * @since 1.2.6.1
17630 */
17631
17632UE.plugins['video'] = function (){
17633 var me =this;
17634
17635 /**
17636 * 创建插入视频字符窜
17637 * @param url 视频地址
17638 * @param width 视频宽度
17639 * @param height 视频高度
17640 * @param align 视频对齐
17641 * @param toEmbed 是否以flash代替显示
17642 * @param addParagraph 是否需要添加P 标签
17643 */
17644 function creatInsertStr(url,width,height,id,align,classname,type){
17645
17646 url = utils.unhtmlForUrl(url);
17647 align = utils.unhtml(align);
17648 classname = utils.unhtml(classname);
17649
17650 width = parseInt(width, 10) || 0;
17651 height = parseInt(height, 10) || 0;
17652
17653 var str;
17654 switch (type){
17655 case 'image':
17656 str = '<img ' + (id ? 'id="' + id+'"' : '') + ' width="'+ width +'" height="' + height + '" _url="'+url+'" class="' + classname.replace(/\bvideo-js\b/, '') + '"' +
17657 ' src="' + me.options.UEDITOR_HOME_URL+'themes/default/images/spacer.gif" style="background:url('+me.options.UEDITOR_HOME_URL+'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;'+(align ? 'float:' + align + ';': '')+'" />'
17658 break;
17659 case 'embed':
17660 str = '<embed type="application/x-shockwave-flash" class="' + classname + '" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
17661 ' src="' + utils.html(url) + '" width="' + width + '" height="' + height + '"' + (align ? ' style="float:' + align + '"': '') +
17662 ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
17663 break;
17664 case 'video':
17665 var ext = url.substr(url.lastIndexOf('.') + 1);
17666 if(ext == 'ogv') ext = 'ogg';
17667 str = '<video' + (id ? ' id="' + id + '"' : '') + ' class="' + classname + ' video-js" ' + (align ? ' style="float:' + align + '"': '') +
17668 ' controls preload="none" width="' + width + '" height="' + height + '" src="' + url + '" data-setup="{}">' +
17669 '<source src="' + url + '" type="video/' + ext + '" /></video>';
17670 break;
17671 }
17672 return str;
17673 }
17674
17675 function switchImgAndVideo(root,img2video){
17676 utils.each(root.getNodesByTagName(img2video ? 'img' : 'embed video'),function(node){
17677 var className = node.getAttr('class');
17678 if(className && className.indexOf('edui-faked-video') != -1){
17679 var html = creatInsertStr( img2video ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',className,img2video ? 'embed':'image');
17680 node.parentNode.replaceChild(UE.uNode.createElement(html),node);
17681 }
17682 if(className && className.indexOf('edui-upload-video') != -1){
17683 var html = creatInsertStr( img2video ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',className,img2video ? 'video':'image');
17684 node.parentNode.replaceChild(UE.uNode.createElement(html),node);
17685 }
17686 })
17687 }
17688
17689 me.addOutputRule(function(root){
17690 switchImgAndVideo(root,true)
17691 });
17692 me.addInputRule(function(root){
17693 switchImgAndVideo(root)
17694 });
17695
17696 /**
17697 * 插入视频
17698 * @command insertvideo
17699 * @method execCommand
17700 * @param { String } cmd 命令字符串
17701 * @param { Object } videoAttr 键值对对象, 描述一个视频的所有属性
17702 * @example
17703 * ```javascript
17704 *
17705 * var videoAttr = {
17706 * //视频地址
17707 * url: 'http://www.youku.com/xxx',
17708 * //视频宽高值, 单位px
17709 * width: 200,
17710 * height: 100
17711 * };
17712 *
17713 * //editor 是编辑器实例
17714 * //向编辑器插入单个视频
17715 * editor.execCommand( 'insertvideo', videoAttr );
17716 * ```
17717 */
17718
17719 /**
17720 * 插入视频
17721 * @command insertvideo
17722 * @method execCommand
17723 * @param { String } cmd 命令字符串
17724 * @param { Array } videoArr 需要插入的视频的数组, 其中的每一个元素都是一个键值对对象, 描述了一个视频的所有属性
17725 * @example
17726 * ```javascript
17727 *
17728 * var videoAttr1 = {
17729 * //视频地址
17730 * url: 'http://www.youku.com/xxx',
17731 * //视频宽高值, 单位px
17732 * width: 200,
17733 * height: 100
17734 * },
17735 * videoAttr2 = {
17736 * //视频地址
17737 * url: 'http://www.youku.com/xxx',
17738 * //视频宽高值, 单位px
17739 * width: 200,
17740 * height: 100
17741 * }
17742 *
17743 * //editor 是编辑器实例
17744 * //该方法将会向编辑器内插入两个视频
17745 * editor.execCommand( 'insertvideo', [ videoAttr1, videoAttr2 ] );
17746 * ```
17747 */
17748
17749 /**
17750 * 查询当前光标所在处是否是一个视频
17751 * @command insertvideo
17752 * @method queryCommandState
17753 * @param { String } cmd 需要查询的命令字符串
17754 * @return { int } 如果当前光标所在处的元素是一个视频对象, 则返回1,否则返回0
17755 * @example
17756 * ```javascript
17757 *
17758 * //editor 是编辑器实例
17759 * editor.queryCommandState( 'insertvideo' );
17760 * ```
17761 */
17762 me.commands["insertvideo"] = {
17763 execCommand: function (cmd, videoObjs, type){
17764 videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs];
17765 var html = [],id = 'tmpVedio', cl;
17766 for(var i=0,vi,len = videoObjs.length;i<len;i++){
17767 vi = videoObjs[i];
17768 cl = (type == 'upload' ? 'edui-upload-video video-js vjs-default-skin':'edui-faked-video');
17769 html.push(creatInsertStr( vi.url, vi.width || 420, vi.height || 280, id + i, null, cl, 'image'));
17770 }
17771 me.execCommand("inserthtml",html.join(""),true);
17772 var rng = this.selection.getRange();
17773 for(var i= 0,len=videoObjs.length;i<len;i++){
17774 var img = this.document.getElementById('tmpVedio'+i);
17775 domUtils.removeAttributes(img,'id');
17776 rng.selectNode(img).select();
17777 me.execCommand('imagefloat',videoObjs[i].align)
17778 }
17779 },
17780 queryCommandState : function(){
17781 var img = me.selection.getRange().getClosedNode(),
17782 flag = img && (img.className == "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1);
17783 return flag ? 1 : 0;
17784 }
17785 };
17786};
17787
17788
17789// plugins/table.core.js
17790/**
17791 * Created with JetBrains WebStorm.
17792 * User: taoqili
17793 * Date: 13-1-18
17794 * Time: 上午11:09
17795 * To change this template use File | Settings | File Templates.
17796 */
17797/**
17798 * UE表格操作类
17799 * @param table
17800 * @constructor
17801 */
17802(function () {
17803 var UETable = UE.UETable = function (table) {
17804 this.table = table;
17805 this.indexTable = [];
17806 this.selectedTds = [];
17807 this.cellsRange = {};
17808 this.update(table);
17809 };
17810
17811 //===以下为静态工具方法===
17812 UETable.removeSelectedClass = function (cells) {
17813 utils.each(cells, function (cell) {
17814 domUtils.removeClasses(cell, "selectTdClass");
17815 })
17816 };
17817 UETable.addSelectedClass = function (cells) {
17818 utils.each(cells, function (cell) {
17819 domUtils.addClass(cell, "selectTdClass");
17820 })
17821 };
17822 UETable.isEmptyBlock = function (node) {
17823 var reg = new RegExp(domUtils.fillChar, 'g');
17824 if (node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '').length > 0) {
17825 return 0;
17826 }
17827 for (var i in dtd.$isNotEmpty) if (dtd.$isNotEmpty.hasOwnProperty(i)) {
17828 if (node.getElementsByTagName(i).length) {
17829 return 0;
17830 }
17831 }
17832 return 1;
17833 };
17834 UETable.getWidth = function (cell) {
17835 if (!cell)return 0;
17836 return parseInt(domUtils.getComputedStyle(cell, "width"), 10);
17837 };
17838
17839 /**
17840 * 获取单元格或者单元格组的“对齐”状态。 如果当前的检测对象是一个单元格组, 只有在满足所有单元格的 水平和竖直 对齐属性都相同的
17841 * 条件时才会返回其状态值,否则将返回null; 如果当前只检测了一个单元格, 则直接返回当前单元格的对齐状态;
17842 * @param table cell or table cells , 支持单个单元格dom对象 或者 单元格dom对象数组
17843 * @return { align: 'left' || 'right' || 'center', valign: 'top' || 'middle' || 'bottom' } 或者 null
17844 */
17845 UETable.getTableCellAlignState = function ( cells ) {
17846
17847 !utils.isArray( cells ) && ( cells = [cells] );
17848
17849 var result = {},
17850 status = ['align', 'valign'],
17851 tempStatus = null,
17852 isSame = true;//状态是否相同
17853
17854 utils.each( cells, function( cellNode ){
17855
17856 utils.each( status, function( currentState ){
17857
17858 tempStatus = cellNode.getAttribute( currentState );
17859
17860 if( !result[ currentState ] && tempStatus ) {
17861 result[ currentState ] = tempStatus;
17862 } else if( !result[ currentState ] || ( tempStatus !== result[ currentState ] ) ) {
17863 isSame = false;
17864 return false;
17865 }
17866
17867 } );
17868
17869 return isSame;
17870
17871 });
17872
17873 return isSame ? result : null;
17874
17875 };
17876
17877 /**
17878 * 根据当前选区获取相关的table信息
17879 * @return {Object}
17880 */
17881 UETable.getTableItemsByRange = function (editor) {
17882 var start = editor.selection.getStart();
17883
17884 //ff下会选中bookmark
17885 if( start && start.id && start.id.indexOf('_baidu_bookmark_start_') === 0 && start.nextSibling) {
17886 start = start.nextSibling;
17887 }
17888
17889 //在table或者td边缘有可能存在选中tr的情况
17890 var cell = start && domUtils.findParentByTagName(start, ["td", "th"], true),
17891 tr = cell && cell.parentNode,
17892 caption = start && domUtils.findParentByTagName(start, 'caption', true),
17893 table = caption ? caption.parentNode : tr && tr.parentNode.parentNode;
17894
17895 return {
17896 cell:cell,
17897 tr:tr,
17898 table:table,
17899 caption:caption
17900 }
17901 };
17902 UETable.getUETableBySelected = function (editor) {
17903 var table = UETable.getTableItemsByRange(editor).table;
17904 if (table && table.ueTable && table.ueTable.selectedTds.length) {
17905 return table.ueTable;
17906 }
17907 return null;
17908 };
17909
17910 UETable.getDefaultValue = function (editor, table) {
17911 var borderMap = {
17912 thin:'0px',
17913 medium:'1px',
17914 thick:'2px'
17915 },
17916 tableBorder, tdPadding, tdBorder, tmpValue;
17917 if (!table) {
17918 table = editor.document.createElement('table');
17919 table.insertRow(0).insertCell(0).innerHTML = 'xxx';
17920 editor.body.appendChild(table);
17921 var td = table.getElementsByTagName('td')[0];
17922 tmpValue = domUtils.getComputedStyle(table, 'border-left-width');
17923 tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
17924 tmpValue = domUtils.getComputedStyle(td, 'padding-left');
17925 tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10);
17926 tmpValue = domUtils.getComputedStyle(td, 'border-left-width');
17927 tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
17928 domUtils.remove(table);
17929 return {
17930 tableBorder:tableBorder,
17931 tdPadding:tdPadding,
17932 tdBorder:tdBorder
17933 };
17934 } else {
17935 td = table.getElementsByTagName('td')[0];
17936 tmpValue = domUtils.getComputedStyle(table, 'border-left-width');
17937 tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
17938 tmpValue = domUtils.getComputedStyle(td, 'padding-left');
17939 tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10);
17940 tmpValue = domUtils.getComputedStyle(td, 'border-left-width');
17941 tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
17942 return {
17943 tableBorder:tableBorder,
17944 tdPadding:tdPadding,
17945 tdBorder:tdBorder
17946 };
17947 }
17948 };
17949 /**
17950 * 根据当前点击的td或者table获取索引对象
17951 * @param tdOrTable
17952 */
17953 UETable.getUETable = function (tdOrTable) {
17954 var tag = tdOrTable.tagName.toLowerCase();
17955 tdOrTable = (tag == "td" || tag == "th" || tag == 'caption') ? domUtils.findParentByTagName(tdOrTable, "table", true) : tdOrTable;
17956 if (!tdOrTable.ueTable) {
17957 tdOrTable.ueTable = new UETable(tdOrTable);
17958 }
17959 return tdOrTable.ueTable;
17960 };
17961
17962 UETable.cloneCell = function(cell,ignoreMerge,keepPro){
17963 if (!cell || utils.isString(cell)) {
17964 return this.table.ownerDocument.createElement(cell || 'td');
17965 }
17966 var flag = domUtils.hasClass(cell, "selectTdClass");
17967 flag && domUtils.removeClasses(cell, "selectTdClass");
17968 var tmpCell = cell.cloneNode(true);
17969 if (ignoreMerge) {
17970 tmpCell.rowSpan = tmpCell.colSpan = 1;
17971 }
17972 //去掉宽高
17973 !keepPro && domUtils.removeAttributes(tmpCell,'width height');
17974 !keepPro && domUtils.removeAttributes(tmpCell,'style');
17975
17976 tmpCell.style.borderLeftStyle = "";
17977 tmpCell.style.borderTopStyle = "";
17978 tmpCell.style.borderLeftColor = cell.style.borderRightColor;
17979 tmpCell.style.borderLeftWidth = cell.style.borderRightWidth;
17980 tmpCell.style.borderTopColor = cell.style.borderBottomColor;
17981 tmpCell.style.borderTopWidth = cell.style.borderBottomWidth;
17982 flag && domUtils.addClass(cell, "selectTdClass");
17983 return tmpCell;
17984 }
17985
17986 UETable.prototype = {
17987 getMaxRows:function () {
17988 var rows = this.table.rows, maxLen = 1;
17989 for (var i = 0, row; row = rows[i]; i++) {
17990 var currentMax = 1;
17991 for (var j = 0, cj; cj = row.cells[j++];) {
17992 currentMax = Math.max(cj.rowSpan || 1, currentMax);
17993 }
17994 maxLen = Math.max(currentMax + i, maxLen);
17995 }
17996 return maxLen;
17997 },
17998 /**
17999 * 获取当前表格的最大列数
18000 */
18001 getMaxCols:function () {
18002 var rows = this.table.rows, maxLen = 0, cellRows = {};
18003 for (var i = 0, row; row = rows[i]; i++) {
18004 var cellsNum = 0;
18005 for (var j = 0, cj; cj = row.cells[j++];) {
18006 cellsNum += (cj.colSpan || 1);
18007 if (cj.rowSpan && cj.rowSpan > 1) {
18008 for (var k = 1; k < cj.rowSpan; k++) {
18009 if (!cellRows['row_' + (i + k)]) {
18010 cellRows['row_' + (i + k)] = (cj.colSpan || 1);
18011 } else {
18012 cellRows['row_' + (i + k)]++
18013 }
18014 }
18015
18016 }
18017 }
18018 cellsNum += cellRows['row_' + i] || 0;
18019 maxLen = Math.max(cellsNum, maxLen);
18020 }
18021 return maxLen;
18022 },
18023 getCellColIndex:function (cell) {
18024
18025 },
18026 /**
18027 * 获取当前cell旁边的单元格,
18028 * @param cell
18029 * @param right
18030 */
18031 getHSideCell:function (cell, right) {
18032 try {
18033 var cellInfo = this.getCellInfo(cell),
18034 previewRowIndex, previewColIndex;
18035 var len = this.selectedTds.length,
18036 range = this.cellsRange;
18037 //首行或者首列没有前置单元格
18038 if ((!right && (!len ? !cellInfo.colIndex : !range.beginColIndex)) || (right && (!len ? (cellInfo.colIndex == (this.colsNum - 1)) : (range.endColIndex == this.colsNum - 1)))) return null;
18039
18040 previewRowIndex = !len ? cellInfo.rowIndex : range.beginRowIndex;
18041 previewColIndex = !right ? ( !len ? (cellInfo.colIndex < 1 ? 0 : (cellInfo.colIndex - 1)) : range.beginColIndex - 1)
18042 : ( !len ? cellInfo.colIndex + 1 : range.endColIndex + 1);
18043 return this.getCell(this.indexTable[previewRowIndex][previewColIndex].rowIndex, this.indexTable[previewRowIndex][previewColIndex].cellIndex);
18044 } catch (e) {
18045 showError(e);
18046 }
18047 },
18048 getTabNextCell:function (cell, preRowIndex) {
18049 var cellInfo = this.getCellInfo(cell),
18050 rowIndex = preRowIndex || cellInfo.rowIndex,
18051 colIndex = cellInfo.colIndex + 1 + (cellInfo.colSpan - 1),
18052 nextCell;
18053 try {
18054 nextCell = this.getCell(this.indexTable[rowIndex][colIndex].rowIndex, this.indexTable[rowIndex][colIndex].cellIndex);
18055 } catch (e) {
18056 try {
18057 rowIndex = rowIndex * 1 + 1;
18058 colIndex = 0;
18059 nextCell = this.getCell(this.indexTable[rowIndex][colIndex].rowIndex, this.indexTable[rowIndex][colIndex].cellIndex);
18060 } catch (e) {
18061 }
18062 }
18063 return nextCell;
18064
18065 },
18066 /**
18067 * 获取视觉上的后置单元格
18068 * @param cell
18069 * @param bottom
18070 */
18071 getVSideCell:function (cell, bottom, ignoreRange) {
18072 try {
18073 var cellInfo = this.getCellInfo(cell),
18074 nextRowIndex, nextColIndex;
18075 var len = this.selectedTds.length && !ignoreRange,
18076 range = this.cellsRange;
18077 //末行或者末列没有后置单元格
18078 if ((!bottom && (cellInfo.rowIndex == 0)) || (bottom && (!len ? (cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1) : (range.endRowIndex == this.rowsNum - 1)))) return null;
18079
18080 nextRowIndex = !bottom ? ( !len ? cellInfo.rowIndex - 1 : range.beginRowIndex - 1)
18081 : ( !len ? (cellInfo.rowIndex + cellInfo.rowSpan) : range.endRowIndex + 1);
18082 nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex;
18083 return this.getCell(this.indexTable[nextRowIndex][nextColIndex].rowIndex, this.indexTable[nextRowIndex][nextColIndex].cellIndex);
18084 } catch (e) {
18085 showError(e);
18086 }
18087 },
18088 /**
18089 * 获取相同结束位置的单元格,xOrY指代了是获取x轴相同还是y轴相同
18090 */
18091 getSameEndPosCells:function (cell, xOrY) {
18092 try {
18093 var flag = (xOrY.toLowerCase() === "x"),
18094 end = domUtils.getXY(cell)[flag ? 'x' : 'y'] + cell["offset" + (flag ? 'Width' : 'Height')],
18095 rows = this.table.rows,
18096 cells = null, returns = [];
18097 for (var i = 0; i < this.rowsNum; i++) {
18098 cells = rows[i].cells;
18099 for (var j = 0, tmpCell; tmpCell = cells[j++];) {
18100 var tmpEnd = domUtils.getXY(tmpCell)[flag ? 'x' : 'y'] + tmpCell["offset" + (flag ? 'Width' : 'Height')];
18101 //对应行的td已经被上面行rowSpan了
18102 if (tmpEnd > end && flag) break;
18103 if (cell == tmpCell || end == tmpEnd) {
18104 //只获取单一的单元格
18105 //todo 仅获取单一单元格在特定情况下会造成returns为空,从而影响后续的拖拽实现,修正这个。需考虑性能
18106 if (tmpCell[flag ? "colSpan" : "rowSpan"] == 1) {
18107 returns.push(tmpCell);
18108 }
18109 if (flag) break;
18110 }
18111 }
18112 }
18113 return returns;
18114 } catch (e) {
18115 showError(e);
18116 }
18117 },
18118 setCellContent:function (cell, content) {
18119 cell.innerHTML = content || (browser.ie ? domUtils.fillChar : "<br />");
18120 },
18121 cloneCell:UETable.cloneCell,
18122 /**
18123 * 获取跟当前单元格的右边竖线为左边的所有未合并单元格
18124 */
18125 getSameStartPosXCells:function (cell) {
18126 try {
18127 var start = domUtils.getXY(cell).x + cell.offsetWidth,
18128 rows = this.table.rows, cells , returns = [];
18129 for (var i = 0; i < this.rowsNum; i++) {
18130 cells = rows[i].cells;
18131 for (var j = 0, tmpCell; tmpCell = cells[j++];) {
18132 var tmpStart = domUtils.getXY(tmpCell).x;
18133 if (tmpStart > start) break;
18134 if (tmpStart == start && tmpCell.colSpan == 1) {
18135 returns.push(tmpCell);
18136 break;
18137 }
18138 }
18139 }
18140 return returns;
18141 } catch (e) {
18142 showError(e);
18143 }
18144 },
18145 /**
18146 * 更新table对应的索引表
18147 */
18148 update:function (table) {
18149 this.table = table || this.table;
18150 this.selectedTds = [];
18151 this.cellsRange = {};
18152 this.indexTable = [];
18153 var rows = this.table.rows,
18154 rowsNum = this.getMaxRows(),
18155 dNum = rowsNum - rows.length,
18156 colsNum = this.getMaxCols();
18157 while (dNum--) {
18158 this.table.insertRow(rows.length);
18159 }
18160 this.rowsNum = rowsNum;
18161 this.colsNum = colsNum;
18162 for (var i = 0, len = rows.length; i < len; i++) {
18163 this.indexTable[i] = new Array(colsNum);
18164 }
18165 //填充索引表
18166 for (var rowIndex = 0, row; row = rows[rowIndex]; rowIndex++) {
18167 for (var cellIndex = 0, cell, cells = row.cells; cell = cells[cellIndex]; cellIndex++) {
18168 //修正整行被rowSpan时导致的行数计算错误
18169 if (cell.rowSpan > rowsNum) {
18170 cell.rowSpan = rowsNum;
18171 }
18172 var colIndex = cellIndex,
18173 rowSpan = cell.rowSpan || 1,
18174 colSpan = cell.colSpan || 1;
18175 //当已经被上一行rowSpan或者被前一列colSpan了,则跳到下一个单元格进行
18176 while (this.indexTable[rowIndex][colIndex]) colIndex++;
18177 for (var j = 0; j < rowSpan; j++) {
18178 for (var k = 0; k < colSpan; k++) {
18179 this.indexTable[rowIndex + j][colIndex + k] = {
18180 rowIndex:rowIndex,
18181 cellIndex:cellIndex,
18182 colIndex:colIndex,
18183 rowSpan:rowSpan,
18184 colSpan:colSpan
18185 }
18186 }
18187 }
18188 }
18189 }
18190 //修复残缺td
18191 for (j = 0; j < rowsNum; j++) {
18192 for (k = 0; k < colsNum; k++) {
18193 if (this.indexTable[j][k] === undefined) {
18194 row = rows[j];
18195 cell = row.cells[row.cells.length - 1];
18196 cell = cell ? cell.cloneNode(true) : this.table.ownerDocument.createElement("td");
18197 this.setCellContent(cell);
18198 if (cell.colSpan !== 1)cell.colSpan = 1;
18199 if (cell.rowSpan !== 1)cell.rowSpan = 1;
18200 row.appendChild(cell);
18201 this.indexTable[j][k] = {
18202 rowIndex:j,
18203 cellIndex:cell.cellIndex,
18204 colIndex:k,
18205 rowSpan:1,
18206 colSpan:1
18207 }
18208 }
18209 }
18210 }
18211 //当框选后删除行或者列后撤销,需要重建选区。
18212 var tds = domUtils.getElementsByTagName(this.table, "td"),
18213 selectTds = [];
18214 utils.each(tds, function (td) {
18215 if (domUtils.hasClass(td, "selectTdClass")) {
18216 selectTds.push(td);
18217 }
18218 });
18219 if (selectTds.length) {
18220 var start = selectTds[0],
18221 end = selectTds[selectTds.length - 1],
18222 startInfo = this.getCellInfo(start),
18223 endInfo = this.getCellInfo(end);
18224 this.selectedTds = selectTds;
18225 this.cellsRange = {
18226 beginRowIndex:startInfo.rowIndex,
18227 beginColIndex:startInfo.colIndex,
18228 endRowIndex:endInfo.rowIndex + endInfo.rowSpan - 1,
18229 endColIndex:endInfo.colIndex + endInfo.colSpan - 1
18230 };
18231 }
18232 //给第一行设置firstRow的样式名称,在排序图标的样式上使用到
18233 if(!domUtils.hasClass(this.table.rows[0], "firstRow")) {
18234 domUtils.addClass(this.table.rows[0], "firstRow");
18235 for(var i = 1; i< this.table.rows.length; i++) {
18236 domUtils.removeClasses(this.table.rows[i], "firstRow");
18237 }
18238 }
18239 },
18240 /**
18241 * 获取单元格的索引信息
18242 */
18243 getCellInfo:function (cell) {
18244 if (!cell) return;
18245 var cellIndex = cell.cellIndex,
18246 rowIndex = cell.parentNode.rowIndex,
18247 rowInfo = this.indexTable[rowIndex],
18248 numCols = this.colsNum;
18249 for (var colIndex = cellIndex; colIndex < numCols; colIndex++) {
18250 var cellInfo = rowInfo[colIndex];
18251 if (cellInfo.rowIndex === rowIndex && cellInfo.cellIndex === cellIndex) {
18252 return cellInfo;
18253 }
18254 }
18255 },
18256 /**
18257 * 根据行列号获取单元格
18258 */
18259 getCell:function (rowIndex, cellIndex) {
18260 return rowIndex < this.rowsNum && this.table.rows[rowIndex].cells[cellIndex] || null;
18261 },
18262 /**
18263 * 删除单元格
18264 */
18265 deleteCell:function (cell, rowIndex) {
18266 rowIndex = typeof rowIndex == 'number' ? rowIndex : cell.parentNode.rowIndex;
18267 var row = this.table.rows[rowIndex];
18268 row.deleteCell(cell.cellIndex);
18269 },
18270 /**
18271 * 根据始末两个单元格获取被框选的所有单元格范围
18272 */
18273 getCellsRange:function (cellA, cellB) {
18274 function checkRange(beginRowIndex, beginColIndex, endRowIndex, endColIndex) {
18275 var tmpBeginRowIndex = beginRowIndex,
18276 tmpBeginColIndex = beginColIndex,
18277 tmpEndRowIndex = endRowIndex,
18278 tmpEndColIndex = endColIndex,
18279 cellInfo, colIndex, rowIndex;
18280 // 通过indexTable检查是否存在超出TableRange上边界的情况
18281 if (beginRowIndex > 0) {
18282 for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
18283 cellInfo = me.indexTable[beginRowIndex][colIndex];
18284 rowIndex = cellInfo.rowIndex;
18285 if (rowIndex < beginRowIndex) {
18286 tmpBeginRowIndex = Math.min(rowIndex, tmpBeginRowIndex);
18287 }
18288 }
18289 }
18290 // 通过indexTable检查是否存在超出TableRange右边界的情况
18291 if (endColIndex < me.colsNum) {
18292 for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
18293 cellInfo = me.indexTable[rowIndex][endColIndex];
18294 colIndex = cellInfo.colIndex + cellInfo.colSpan - 1;
18295 if (colIndex > endColIndex) {
18296 tmpEndColIndex = Math.max(colIndex, tmpEndColIndex);
18297 }
18298 }
18299 }
18300 // 检查是否有超出TableRange下边界的情况
18301 if (endRowIndex < me.rowsNum) {
18302 for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
18303 cellInfo = me.indexTable[endRowIndex][colIndex];
18304 rowIndex = cellInfo.rowIndex + cellInfo.rowSpan - 1;
18305 if (rowIndex > endRowIndex) {
18306 tmpEndRowIndex = Math.max(rowIndex, tmpEndRowIndex);
18307 }
18308 }
18309 }
18310 // 检查是否有超出TableRange左边界的情况
18311 if (beginColIndex > 0) {
18312 for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
18313 cellInfo = me.indexTable[rowIndex][beginColIndex];
18314 colIndex = cellInfo.colIndex;
18315 if (colIndex < beginColIndex) {
18316 tmpBeginColIndex = Math.min(cellInfo.colIndex, tmpBeginColIndex);
18317 }
18318 }
18319 }
18320 //递归调用直至所有完成所有框选单元格的扩展
18321 if (tmpBeginRowIndex != beginRowIndex || tmpBeginColIndex != beginColIndex || tmpEndRowIndex != endRowIndex || tmpEndColIndex != endColIndex) {
18322 return checkRange(tmpBeginRowIndex, tmpBeginColIndex, tmpEndRowIndex, tmpEndColIndex);
18323 } else {
18324 // 不需要扩展TableRange的情况
18325 return {
18326 beginRowIndex:beginRowIndex,
18327 beginColIndex:beginColIndex,
18328 endRowIndex:endRowIndex,
18329 endColIndex:endColIndex
18330 };
18331 }
18332 }
18333
18334 try {
18335 var me = this,
18336 cellAInfo = me.getCellInfo(cellA);
18337 if (cellA === cellB) {
18338 return {
18339 beginRowIndex:cellAInfo.rowIndex,
18340 beginColIndex:cellAInfo.colIndex,
18341 endRowIndex:cellAInfo.rowIndex + cellAInfo.rowSpan - 1,
18342 endColIndex:cellAInfo.colIndex + cellAInfo.colSpan - 1
18343 };
18344 }
18345 var cellBInfo = me.getCellInfo(cellB);
18346 // 计算TableRange的四个边
18347 var beginRowIndex = Math.min(cellAInfo.rowIndex, cellBInfo.rowIndex),
18348 beginColIndex = Math.min(cellAInfo.colIndex, cellBInfo.colIndex),
18349 endRowIndex = Math.max(cellAInfo.rowIndex + cellAInfo.rowSpan - 1, cellBInfo.rowIndex + cellBInfo.rowSpan - 1),
18350 endColIndex = Math.max(cellAInfo.colIndex + cellAInfo.colSpan - 1, cellBInfo.colIndex + cellBInfo.colSpan - 1);
18351
18352 return checkRange(beginRowIndex, beginColIndex, endRowIndex, endColIndex);
18353 } catch (e) {
18354 //throw e;
18355 }
18356 },
18357 /**
18358 * 依据cellsRange获取对应的单元格集合
18359 */
18360 getCells:function (range) {
18361 //每次获取cells之前必须先清除上次的选择,否则会对后续获取操作造成影响
18362 this.clearSelected();
18363 var beginRowIndex = range.beginRowIndex,
18364 beginColIndex = range.beginColIndex,
18365 endRowIndex = range.endRowIndex,
18366 endColIndex = range.endColIndex,
18367 cellInfo, rowIndex, colIndex, tdHash = {}, returnTds = [];
18368 for (var i = beginRowIndex; i <= endRowIndex; i++) {
18369 for (var j = beginColIndex; j <= endColIndex; j++) {
18370 cellInfo = this.indexTable[i][j];
18371 rowIndex = cellInfo.rowIndex;
18372 colIndex = cellInfo.colIndex;
18373 // 如果Cells里已经包含了此Cell则跳过
18374 var key = rowIndex + '|' + colIndex;
18375 if (tdHash[key]) continue;
18376 tdHash[key] = 1;
18377 if (rowIndex < i || colIndex < j || rowIndex + cellInfo.rowSpan - 1 > endRowIndex || colIndex + cellInfo.colSpan - 1 > endColIndex) {
18378 return null;
18379 }
18380 returnTds.push(this.getCell(rowIndex, cellInfo.cellIndex));
18381 }
18382 }
18383 return returnTds;
18384 },
18385 /**
18386 * 清理已经选中的单元格
18387 */
18388 clearSelected:function () {
18389 UETable.removeSelectedClass(this.selectedTds);
18390 this.selectedTds = [];
18391 this.cellsRange = {};
18392 },
18393 /**
18394 * 根据range设置已经选中的单元格
18395 */
18396 setSelected:function (range) {
18397 var cells = this.getCells(range);
18398 UETable.addSelectedClass(cells);
18399 this.selectedTds = cells;
18400 this.cellsRange = range;
18401 },
18402 isFullRow:function () {
18403 var range = this.cellsRange;
18404 return (range.endColIndex - range.beginColIndex + 1) == this.colsNum;
18405 },
18406 isFullCol:function () {
18407 var range = this.cellsRange,
18408 table = this.table,
18409 ths = table.getElementsByTagName("th"),
18410 rows = range.endRowIndex - range.beginRowIndex + 1;
18411 return !ths.length ? rows == this.rowsNum : rows == this.rowsNum || (rows == this.rowsNum - 1);
18412
18413 },
18414 /**
18415 * 获取视觉上的前置单元格,默认是左边,top传入时
18416 * @param cell
18417 * @param top
18418 */
18419 getNextCell:function (cell, bottom, ignoreRange) {
18420 try {
18421 var cellInfo = this.getCellInfo(cell),
18422 nextRowIndex, nextColIndex;
18423 var len = this.selectedTds.length && !ignoreRange,
18424 range = this.cellsRange;
18425 //末行或者末列没有后置单元格
18426 if ((!bottom && (cellInfo.rowIndex == 0)) || (bottom && (!len ? (cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1) : (range.endRowIndex == this.rowsNum - 1)))) return null;
18427
18428 nextRowIndex = !bottom ? ( !len ? cellInfo.rowIndex - 1 : range.beginRowIndex - 1)
18429 : ( !len ? (cellInfo.rowIndex + cellInfo.rowSpan) : range.endRowIndex + 1);
18430 nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex;
18431 return this.getCell(this.indexTable[nextRowIndex][nextColIndex].rowIndex, this.indexTable[nextRowIndex][nextColIndex].cellIndex);
18432 } catch (e) {
18433 showError(e);
18434 }
18435 },
18436 getPreviewCell:function (cell, top) {
18437 try {
18438 var cellInfo = this.getCellInfo(cell),
18439 previewRowIndex, previewColIndex;
18440 var len = this.selectedTds.length,
18441 range = this.cellsRange;
18442 //首行或者首列没有前置单元格
18443 if ((!top && (!len ? !cellInfo.colIndex : !range.beginColIndex)) || (top && (!len ? (cellInfo.rowIndex > (this.colsNum - 1)) : (range.endColIndex == this.colsNum - 1)))) return null;
18444
18445 previewRowIndex = !top ? ( !len ? cellInfo.rowIndex : range.beginRowIndex )
18446 : ( !len ? (cellInfo.rowIndex < 1 ? 0 : (cellInfo.rowIndex - 1)) : range.beginRowIndex);
18447 previewColIndex = !top ? ( !len ? (cellInfo.colIndex < 1 ? 0 : (cellInfo.colIndex - 1)) : range.beginColIndex - 1)
18448 : ( !len ? cellInfo.colIndex : range.endColIndex + 1);
18449 return this.getCell(this.indexTable[previewRowIndex][previewColIndex].rowIndex, this.indexTable[previewRowIndex][previewColIndex].cellIndex);
18450 } catch (e) {
18451 showError(e);
18452 }
18453 },
18454 /**
18455 * 移动单元格中的内容
18456 */
18457 moveContent:function (cellTo, cellFrom) {
18458 if (UETable.isEmptyBlock(cellFrom)) return;
18459 if (UETable.isEmptyBlock(cellTo)) {
18460 cellTo.innerHTML = cellFrom.innerHTML;
18461 return;
18462 }
18463 var child = cellTo.lastChild;
18464 if (child.nodeType == 3 || !dtd.$block[child.tagName]) {
18465 cellTo.appendChild(cellTo.ownerDocument.createElement('br'))
18466 }
18467 while (child = cellFrom.firstChild) {
18468 cellTo.appendChild(child);
18469 }
18470 },
18471 /**
18472 * 向右合并单元格
18473 */
18474 mergeRight:function (cell) {
18475 var cellInfo = this.getCellInfo(cell),
18476 rightColIndex = cellInfo.colIndex + cellInfo.colSpan,
18477 rightCellInfo = this.indexTable[cellInfo.rowIndex][rightColIndex],
18478 rightCell = this.getCell(rightCellInfo.rowIndex, rightCellInfo.cellIndex);
18479 //合并
18480 cell.colSpan = cellInfo.colSpan + rightCellInfo.colSpan;
18481 //被合并的单元格不应存在宽度属性
18482 cell.removeAttribute("width");
18483 //移动内容
18484 this.moveContent(cell, rightCell);
18485 //删掉被合并的Cell
18486 this.deleteCell(rightCell, rightCellInfo.rowIndex);
18487 this.update();
18488 },
18489 /**
18490 * 向下合并单元格
18491 */
18492 mergeDown:function (cell) {
18493 var cellInfo = this.getCellInfo(cell),
18494 downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan,
18495 downCellInfo = this.indexTable[downRowIndex][cellInfo.colIndex],
18496 downCell = this.getCell(downCellInfo.rowIndex, downCellInfo.cellIndex);
18497 cell.rowSpan = cellInfo.rowSpan + downCellInfo.rowSpan;
18498 cell.removeAttribute("height");
18499 this.moveContent(cell, downCell);
18500 this.deleteCell(downCell, downCellInfo.rowIndex);
18501 this.update();
18502 },
18503 /**
18504 * 合并整个range中的内容
18505 */
18506 mergeRange:function () {
18507 //由于合并操作可以在任意时刻进行,所以无法通过鼠标位置等信息实时生成range,只能通过缓存实例中的cellsRange对象来访问
18508 var range = this.cellsRange,
18509 leftTopCell = this.getCell(range.beginRowIndex, this.indexTable[range.beginRowIndex][range.beginColIndex].cellIndex);
18510
18511 if (leftTopCell.tagName == "TH" && range.endRowIndex !== range.beginRowIndex) {
18512 var index = this.indexTable,
18513 info = this.getCellInfo(leftTopCell);
18514 leftTopCell = this.getCell(1, index[1][info.colIndex].cellIndex);
18515 range = this.getCellsRange(leftTopCell, this.getCell(index[this.rowsNum - 1][info.colIndex].rowIndex, index[this.rowsNum - 1][info.colIndex].cellIndex));
18516 }
18517
18518 // 删除剩余的Cells
18519 var cells = this.getCells(range);
18520 for(var i= 0,ci;ci=cells[i++];){
18521 if (ci !== leftTopCell) {
18522 this.moveContent(leftTopCell, ci);
18523 this.deleteCell(ci);
18524 }
18525 }
18526 // 修改左上角Cell的rowSpan和colSpan,并调整宽度属性设置
18527 leftTopCell.rowSpan = range.endRowIndex - range.beginRowIndex + 1;
18528 leftTopCell.rowSpan > 1 && leftTopCell.removeAttribute("height");
18529 leftTopCell.colSpan = range.endColIndex - range.beginColIndex + 1;
18530 leftTopCell.colSpan > 1 && leftTopCell.removeAttribute("width");
18531 if (leftTopCell.rowSpan == this.rowsNum && leftTopCell.colSpan != 1) {
18532 leftTopCell.colSpan = 1;
18533 }
18534
18535 if (leftTopCell.colSpan == this.colsNum && leftTopCell.rowSpan != 1) {
18536 var rowIndex = leftTopCell.parentNode.rowIndex;
18537 //解决IE下的表格操作问题
18538 if( this.table.deleteRow ) {
18539 for (var i = rowIndex+ 1, curIndex=rowIndex+ 1, len=leftTopCell.rowSpan; i < len; i++) {
18540 this.table.deleteRow(curIndex);
18541 }
18542 } else {
18543 for (var i = 0, len=leftTopCell.rowSpan - 1; i < len; i++) {
18544 var row = this.table.rows[rowIndex + 1];
18545 row.parentNode.removeChild(row);
18546 }
18547 }
18548 leftTopCell.rowSpan = 1;
18549 }
18550 this.update();
18551 },
18552 /**
18553 * 插入一行单元格
18554 */
18555 insertRow:function (rowIndex, sourceCell) {
18556 var numCols = this.colsNum,
18557 table = this.table,
18558 row = table.insertRow(rowIndex), cell,
18559 isInsertTitle = typeof sourceCell == 'string' && sourceCell.toUpperCase() == 'TH';
18560
18561 function replaceTdToTh(colIndex, cell, tableRow) {
18562 if (colIndex == 0) {
18563 var tr = tableRow.nextSibling || tableRow.previousSibling,
18564 th = tr.cells[colIndex];
18565 if (th.tagName == 'TH') {
18566 th = cell.ownerDocument.createElement("th");
18567 th.appendChild(cell.firstChild);
18568 tableRow.insertBefore(th, cell);
18569 domUtils.remove(cell)
18570 }
18571 }else{
18572 if (cell.tagName == 'TH') {
18573 var td = cell.ownerDocument.createElement("td");
18574 td.appendChild(cell.firstChild);
18575 tableRow.insertBefore(td, cell);
18576 domUtils.remove(cell)
18577 }
18578 }
18579 }
18580
18581 //首行直接插入,无需考虑部分单元格被rowspan的情况
18582 if (rowIndex == 0 || rowIndex == this.rowsNum) {
18583 for (var colIndex = 0; colIndex < numCols; colIndex++) {
18584 cell = this.cloneCell(sourceCell, true);
18585 this.setCellContent(cell);
18586 cell.getAttribute('vAlign') && cell.setAttribute('vAlign', cell.getAttribute('vAlign'));
18587 row.appendChild(cell);
18588 if(!isInsertTitle) replaceTdToTh(colIndex, cell, row);
18589 }
18590 } else {
18591 var infoRow = this.indexTable[rowIndex],
18592 cellIndex = 0;
18593 for (colIndex = 0; colIndex < numCols; colIndex++) {
18594 var cellInfo = infoRow[colIndex];
18595 //如果存在某个单元格的rowspan穿过待插入行的位置,则修改该单元格的rowspan即可,无需插入单元格
18596 if (cellInfo.rowIndex < rowIndex) {
18597 cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
18598 cell.rowSpan = cellInfo.rowSpan + 1;
18599 } else {
18600 cell = this.cloneCell(sourceCell, true);
18601 this.setCellContent(cell);
18602 row.appendChild(cell);
18603 }
18604 if(!isInsertTitle) replaceTdToTh(colIndex, cell, row);
18605 }
18606 }
18607 //框选时插入不触发contentchange,需要手动更新索引。
18608 this.update();
18609 return row;
18610 },
18611 /**
18612 * 删除一行单元格
18613 * @param rowIndex
18614 */
18615 deleteRow:function (rowIndex) {
18616 var row = this.table.rows[rowIndex],
18617 infoRow = this.indexTable[rowIndex],
18618 colsNum = this.colsNum,
18619 count = 0; //处理计数
18620 for (var colIndex = 0; colIndex < colsNum;) {
18621 var cellInfo = infoRow[colIndex],
18622 cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
18623 if (cell.rowSpan > 1) {
18624 if (cellInfo.rowIndex == rowIndex) {
18625 var clone = cell.cloneNode(true);
18626 clone.rowSpan = cell.rowSpan - 1;
18627 clone.innerHTML = "";
18628 cell.rowSpan = 1;
18629 var nextRowIndex = rowIndex + 1,
18630 nextRow = this.table.rows[nextRowIndex],
18631 insertCellIndex,
18632 preMerged = this.getPreviewMergedCellsNum(nextRowIndex, colIndex) - count;
18633 if (preMerged < colIndex) {
18634 insertCellIndex = colIndex - preMerged - 1;
18635 //nextRow.insertCell(insertCellIndex);
18636 domUtils.insertAfter(nextRow.cells[insertCellIndex], clone);
18637 } else {
18638 if (nextRow.cells.length) nextRow.insertBefore(clone, nextRow.cells[0])
18639 }
18640 count += 1;
18641 //cell.parentNode.removeChild(cell);
18642 }
18643 }
18644 colIndex += cell.colSpan || 1;
18645 }
18646 var deleteTds = [], cacheMap = {};
18647 for (colIndex = 0; colIndex < colsNum; colIndex++) {
18648 var tmpRowIndex = infoRow[colIndex].rowIndex,
18649 tmpCellIndex = infoRow[colIndex].cellIndex,
18650 key = tmpRowIndex + "_" + tmpCellIndex;
18651 if (cacheMap[key])continue;
18652 cacheMap[key] = 1;
18653 cell = this.getCell(tmpRowIndex, tmpCellIndex);
18654 deleteTds.push(cell);
18655 }
18656 var mergeTds = [];
18657 utils.each(deleteTds, function (td) {
18658 if (td.rowSpan == 1) {
18659 td.parentNode.removeChild(td);
18660 } else {
18661 mergeTds.push(td);
18662 }
18663 });
18664 utils.each(mergeTds, function (td) {
18665 td.rowSpan--;
18666 });
18667 row.parentNode.removeChild(row);
18668 //浏览器方法本身存在bug,采用自定义方法删除
18669 //this.table.deleteRow(rowIndex);
18670 this.update();
18671 },
18672 insertCol:function (colIndex, sourceCell, defaultValue) {
18673 var rowsNum = this.rowsNum,
18674 rowIndex = 0,
18675 tableRow, cell,
18676 backWidth = parseInt((this.table.offsetWidth - (this.colsNum + 1) * 20 - (this.colsNum + 1)) / (this.colsNum + 1), 10),
18677 isInsertTitleCol = typeof sourceCell == 'string' && sourceCell.toUpperCase() == 'TH';
18678
18679 function replaceTdToTh(rowIndex, cell, tableRow) {
18680 if (rowIndex == 0) {
18681 var th = cell.nextSibling || cell.previousSibling;
18682 if (th.tagName == 'TH') {
18683 th = cell.ownerDocument.createElement("th");
18684 th.appendChild(cell.firstChild);
18685 tableRow.insertBefore(th, cell);
18686 domUtils.remove(cell)
18687 }
18688 }else{
18689 if (cell.tagName == 'TH') {
18690 var td = cell.ownerDocument.createElement("td");
18691 td.appendChild(cell.firstChild);
18692 tableRow.insertBefore(td, cell);
18693 domUtils.remove(cell)
18694 }
18695 }
18696 }
18697
18698 var preCell;
18699 if (colIndex == 0 || colIndex == this.colsNum) {
18700 for (; rowIndex < rowsNum; rowIndex++) {
18701 tableRow = this.table.rows[rowIndex];
18702 preCell = tableRow.cells[colIndex == 0 ? colIndex : tableRow.cells.length];
18703 cell = this.cloneCell(sourceCell, true); //tableRow.insertCell(colIndex == 0 ? colIndex : tableRow.cells.length);
18704 this.setCellContent(cell);
18705 cell.setAttribute('vAlign', cell.getAttribute('vAlign'));
18706 preCell && cell.setAttribute('width', preCell.getAttribute('width'));
18707 if (!colIndex) {
18708 tableRow.insertBefore(cell, tableRow.cells[0]);
18709 } else {
18710 domUtils.insertAfter(tableRow.cells[tableRow.cells.length - 1], cell);
18711 }
18712 if(!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow)
18713 }
18714 } else {
18715 for (; rowIndex < rowsNum; rowIndex++) {
18716 var cellInfo = this.indexTable[rowIndex][colIndex];
18717 if (cellInfo.colIndex < colIndex) {
18718 cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
18719 cell.colSpan = cellInfo.colSpan + 1;
18720 } else {
18721 tableRow = this.table.rows[rowIndex];
18722 preCell = tableRow.cells[cellInfo.cellIndex];
18723
18724 cell = this.cloneCell(sourceCell, true);//tableRow.insertCell(cellInfo.cellIndex);
18725 this.setCellContent(cell);
18726 cell.setAttribute('vAlign', cell.getAttribute('vAlign'));
18727 preCell && cell.setAttribute('width', preCell.getAttribute('width'));
18728 //防止IE下报错
18729 preCell ? tableRow.insertBefore(cell, preCell) : tableRow.appendChild(cell);
18730 }
18731 if(!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow);
18732 }
18733 }
18734 //框选时插入不触发contentchange,需要手动更新索引
18735 this.update();
18736 this.updateWidth(backWidth, defaultValue || {tdPadding:10, tdBorder:1});
18737 },
18738 updateWidth:function (width, defaultValue) {
18739 var table = this.table,
18740 tmpWidth = UETable.getWidth(table) - defaultValue.tdPadding * 2 - defaultValue.tdBorder + width;
18741 if (tmpWidth < table.ownerDocument.body.offsetWidth) {
18742 table.setAttribute("width", tmpWidth);
18743 return;
18744 }
18745 var tds = domUtils.getElementsByTagName(this.table, "td th");
18746 utils.each(tds, function (td) {
18747 td.setAttribute("width", width);
18748 })
18749 },
18750 deleteCol:function (colIndex) {
18751 var indexTable = this.indexTable,
18752 tableRows = this.table.rows,
18753 backTableWidth = this.table.getAttribute("width"),
18754 backTdWidth = 0,
18755 rowsNum = this.rowsNum,
18756 cacheMap = {};
18757 for (var rowIndex = 0; rowIndex < rowsNum;) {
18758 var infoRow = indexTable[rowIndex],
18759 cellInfo = infoRow[colIndex],
18760 key = cellInfo.rowIndex + '_' + cellInfo.colIndex;
18761 // 跳过已经处理过的Cell
18762 if (cacheMap[key])continue;
18763 cacheMap[key] = 1;
18764 var cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
18765 if (!backTdWidth) backTdWidth = cell && parseInt(cell.offsetWidth / cell.colSpan, 10).toFixed(0);
18766 // 如果Cell的colSpan大于1, 就修改colSpan, 否则就删掉这个Cell
18767 if (cell.colSpan > 1) {
18768 cell.colSpan--;
18769 } else {
18770 tableRows[rowIndex].deleteCell(cellInfo.cellIndex);
18771 }
18772 rowIndex += cellInfo.rowSpan || 1;
18773 }
18774 this.table.setAttribute("width", backTableWidth - backTdWidth);
18775 this.update();
18776 },
18777 splitToCells:function (cell) {
18778 var me = this,
18779 cells = this.splitToRows(cell);
18780 utils.each(cells, function (cell) {
18781 me.splitToCols(cell);
18782 })
18783 },
18784 splitToRows:function (cell) {
18785 var cellInfo = this.getCellInfo(cell),
18786 rowIndex = cellInfo.rowIndex,
18787 colIndex = cellInfo.colIndex,
18788 results = [];
18789 // 修改Cell的rowSpan
18790 cell.rowSpan = 1;
18791 results.push(cell);
18792 // 补齐单元格
18793 for (var i = rowIndex, endRow = rowIndex + cellInfo.rowSpan; i < endRow; i++) {
18794 if (i == rowIndex)continue;
18795 var tableRow = this.table.rows[i],
18796 tmpCell = tableRow.insertCell(colIndex - this.getPreviewMergedCellsNum(i, colIndex));
18797 tmpCell.colSpan = cellInfo.colSpan;
18798 this.setCellContent(tmpCell);
18799 tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign'));
18800 tmpCell.setAttribute('align', cell.getAttribute('align'));
18801 if (cell.style.cssText) {
18802 tmpCell.style.cssText = cell.style.cssText;
18803 }
18804 results.push(tmpCell);
18805 }
18806 this.update();
18807 return results;
18808 },
18809 getPreviewMergedCellsNum:function (rowIndex, colIndex) {
18810 var indexRow = this.indexTable[rowIndex],
18811 num = 0;
18812 for (var i = 0; i < colIndex;) {
18813 var colSpan = indexRow[i].colSpan,
18814 tmpRowIndex = indexRow[i].rowIndex;
18815 num += (colSpan - (tmpRowIndex == rowIndex ? 1 : 0));
18816 i += colSpan;
18817 }
18818 return num;
18819 },
18820 splitToCols:function (cell) {
18821 var backWidth = (cell.offsetWidth / cell.colSpan - 22).toFixed(0),
18822
18823 cellInfo = this.getCellInfo(cell),
18824 rowIndex = cellInfo.rowIndex,
18825 colIndex = cellInfo.colIndex,
18826 results = [];
18827 // 修改Cell的rowSpan
18828 cell.colSpan = 1;
18829 cell.setAttribute("width", backWidth);
18830 results.push(cell);
18831 // 补齐单元格
18832 for (var j = colIndex, endCol = colIndex + cellInfo.colSpan; j < endCol; j++) {
18833 if (j == colIndex)continue;
18834 var tableRow = this.table.rows[rowIndex],
18835 tmpCell = tableRow.insertCell(this.indexTable[rowIndex][j].cellIndex + 1);
18836 tmpCell.rowSpan = cellInfo.rowSpan;
18837 this.setCellContent(tmpCell);
18838 tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign'));
18839 tmpCell.setAttribute('align', cell.getAttribute('align'));
18840 tmpCell.setAttribute('width', backWidth);
18841 if (cell.style.cssText) {
18842 tmpCell.style.cssText = cell.style.cssText;
18843 }
18844 //处理th的情况
18845 if (cell.tagName == 'TH') {
18846 var th = cell.ownerDocument.createElement('th');
18847 th.appendChild(tmpCell.firstChild);
18848 th.setAttribute('vAlign', cell.getAttribute('vAlign'));
18849 th.rowSpan = tmpCell.rowSpan;
18850 tableRow.insertBefore(th, tmpCell);
18851 domUtils.remove(tmpCell);
18852 }
18853 results.push(tmpCell);
18854 }
18855 this.update();
18856 return results;
18857 },
18858 isLastCell:function (cell, rowsNum, colsNum) {
18859 rowsNum = rowsNum || this.rowsNum;
18860 colsNum = colsNum || this.colsNum;
18861 var cellInfo = this.getCellInfo(cell);
18862 return ((cellInfo.rowIndex + cellInfo.rowSpan) == rowsNum) &&
18863 ((cellInfo.colIndex + cellInfo.colSpan) == colsNum);
18864 },
18865 getLastCell:function (cells) {
18866 cells = cells || this.table.getElementsByTagName("td");
18867 var firstInfo = this.getCellInfo(cells[0]);
18868 var me = this, last = cells[0],
18869 tr = last.parentNode,
18870 cellsNum = 0, cols = 0, rows;
18871 utils.each(cells, function (cell) {
18872 if (cell.parentNode == tr)cols += cell.colSpan || 1;
18873 cellsNum += cell.rowSpan * cell.colSpan || 1;
18874 });
18875 rows = cellsNum / cols;
18876 utils.each(cells, function (cell) {
18877 if (me.isLastCell(cell, rows, cols)) {
18878 last = cell;
18879 return false;
18880 }
18881 });
18882 return last;
18883
18884 },
18885 selectRow:function (rowIndex) {
18886 var indexRow = this.indexTable[rowIndex],
18887 start = this.getCell(indexRow[0].rowIndex, indexRow[0].cellIndex),
18888 end = this.getCell(indexRow[this.colsNum - 1].rowIndex, indexRow[this.colsNum - 1].cellIndex),
18889 range = this.getCellsRange(start, end);
18890 this.setSelected(range);
18891 },
18892 selectTable:function () {
18893 var tds = this.table.getElementsByTagName("td"),
18894 range = this.getCellsRange(tds[0], tds[tds.length - 1]);
18895 this.setSelected(range);
18896 },
18897 setBackground:function (cells, value) {
18898 if (typeof value === "string") {
18899 utils.each(cells, function (cell) {
18900 cell.style.backgroundColor = value;
18901 })
18902 } else if (typeof value === "object") {
18903 value = utils.extend({
18904 repeat:true,
18905 colorList:["#ddd", "#fff"]
18906 }, value);
18907 var rowIndex = this.getCellInfo(cells[0]).rowIndex,
18908 count = 0,
18909 colors = value.colorList,
18910 getColor = function (list, index, repeat) {
18911 return list[index] ? list[index] : repeat ? list[index % list.length] : "";
18912 };
18913 for (var i = 0, cell; cell = cells[i++];) {
18914 var cellInfo = this.getCellInfo(cell);
18915 cell.style.backgroundColor = getColor(colors, ((rowIndex + count) == cellInfo.rowIndex) ? count : ++count, value.repeat);
18916 }
18917 }
18918 },
18919 removeBackground:function (cells) {
18920 utils.each(cells, function (cell) {
18921 cell.style.backgroundColor = "";
18922 })
18923 }
18924
18925
18926 };
18927 function showError(e) {
18928 }
18929})();
18930
18931// plugins/table.cmds.js
18932/**
18933 * Created with JetBrains PhpStorm.
18934 * User: taoqili
18935 * Date: 13-2-20
18936 * Time: 下午6:25
18937 * To change this template use File | Settings | File Templates.
18938 */
18939;
18940(function () {
18941 var UT = UE.UETable,
18942 getTableItemsByRange = function (editor) {
18943 return UT.getTableItemsByRange(editor);
18944 },
18945 getUETableBySelected = function (editor) {
18946 return UT.getUETableBySelected(editor)
18947 },
18948 getDefaultValue = function (editor, table) {
18949 return UT.getDefaultValue(editor, table);
18950 },
18951 getUETable = function (tdOrTable) {
18952 return UT.getUETable(tdOrTable);
18953 };
18954
18955
18956 UE.commands['inserttable'] = {
18957 queryCommandState: function () {
18958 return getTableItemsByRange(this).table ? -1 : 0;
18959 },
18960 execCommand: function (cmd, opt) {
18961 function createTable(opt, tdWidth) {
18962 var html = [],
18963 rowsNum = opt.numRows,
18964 colsNum = opt.numCols;
18965 for (var r = 0; r < rowsNum; r++) {
18966 html.push('<tr' + (r == 0 ? ' class="firstRow"':'') + '>');
18967 for (var c = 0; c < colsNum; c++) {
18968 html.push('<td width="' + tdWidth + '" vAlign="' + opt.tdvalign + '" >' + (browser.ie && browser.version < 11 ? domUtils.fillChar : '<br/>') + '</td>')
18969 }
18970 html.push('</tr>')
18971 }
18972 //禁止指定table-width
18973 return '<table><tbody>' + html.join('') + '</tbody></table>'
18974 }
18975
18976 if (!opt) {
18977 opt = utils.extend({}, {
18978 numCols: this.options.defaultCols,
18979 numRows: this.options.defaultRows,
18980 tdvalign: this.options.tdvalign
18981 })
18982 }
18983 var me = this;
18984 var range = this.selection.getRange(),
18985 start = range.startContainer,
18986 firstParentBlock = domUtils.findParent(start, function (node) {
18987 return domUtils.isBlockElm(node);
18988 }, true) || me.body;
18989
18990 var defaultValue = getDefaultValue(me),
18991 tableWidth = firstParentBlock.offsetWidth,
18992 tdWidth = Math.floor(tableWidth / opt.numCols - defaultValue.tdPadding * 2 - defaultValue.tdBorder);
18993
18994 //todo其他属性
18995 !opt.tdvalign && (opt.tdvalign = me.options.tdvalign);
18996 me.execCommand("inserthtml", createTable(opt, tdWidth));
18997 }
18998 };
18999
19000 UE.commands['insertparagraphbeforetable'] = {
19001 queryCommandState: function () {
19002 return getTableItemsByRange(this).cell ? 0 : -1;
19003 },
19004 execCommand: function () {
19005 var table = getTableItemsByRange(this).table;
19006 if (table) {
19007 var p = this.document.createElement("p");
19008 p.innerHTML = browser.ie ? '&nbsp;' : '<br />';
19009 table.parentNode.insertBefore(p, table);
19010 this.selection.getRange().setStart(p, 0).setCursor();
19011 }
19012 }
19013 };
19014
19015 UE.commands['deletetable'] = {
19016 queryCommandState: function () {
19017 var rng = this.selection.getRange();
19018 return domUtils.findParentByTagName(rng.startContainer, 'table', true) ? 0 : -1;
19019 },
19020 execCommand: function (cmd, table) {
19021 var rng = this.selection.getRange();
19022 table = table || domUtils.findParentByTagName(rng.startContainer, 'table', true);
19023 if (table) {
19024 var next = table.nextSibling;
19025 if (!next) {
19026 next = domUtils.createElement(this.document, 'p', {
19027 'innerHTML': browser.ie ? domUtils.fillChar : '<br/>'
19028 });
19029 table.parentNode.insertBefore(next, table);
19030 }
19031 domUtils.remove(table);
19032 rng = this.selection.getRange();
19033 if (next.nodeType == 3) {
19034 rng.setStartBefore(next)
19035 } else {
19036 rng.setStart(next, 0)
19037 }
19038 rng.setCursor(false, true)
19039 this.fireEvent("tablehasdeleted")
19040
19041 }
19042
19043 }
19044 };
19045 UE.commands['cellalign'] = {
19046 queryCommandState: function () {
19047 return getSelectedArr(this).length ? 0 : -1
19048 },
19049 execCommand: function (cmd, align) {
19050 var selectedTds = getSelectedArr(this);
19051 if (selectedTds.length) {
19052 for (var i = 0, ci; ci = selectedTds[i++];) {
19053 ci.setAttribute('align', align);
19054 }
19055 }
19056 }
19057 };
19058 UE.commands['cellvalign'] = {
19059 queryCommandState: function () {
19060 return getSelectedArr(this).length ? 0 : -1;
19061 },
19062 execCommand: function (cmd, valign) {
19063 var selectedTds = getSelectedArr(this);
19064 if (selectedTds.length) {
19065 for (var i = 0, ci; ci = selectedTds[i++];) {
19066 ci.setAttribute('vAlign', valign);
19067 }
19068 }
19069 }
19070 };
19071 UE.commands['insertcaption'] = {
19072 queryCommandState: function () {
19073 var table = getTableItemsByRange(this).table;
19074 if (table) {
19075 return table.getElementsByTagName('caption').length == 0 ? 1 : -1;
19076 }
19077 return -1;
19078 },
19079 execCommand: function () {
19080 var table = getTableItemsByRange(this).table;
19081 if (table) {
19082 var caption = this.document.createElement('caption');
19083 caption.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
19084 table.insertBefore(caption, table.firstChild);
19085 var range = this.selection.getRange();
19086 range.setStart(caption, 0).setCursor();
19087 }
19088
19089 }
19090 };
19091 UE.commands['deletecaption'] = {
19092 queryCommandState: function () {
19093 var rng = this.selection.getRange(),
19094 table = domUtils.findParentByTagName(rng.startContainer, 'table');
19095 if (table) {
19096 return table.getElementsByTagName('caption').length == 0 ? -1 : 1;
19097 }
19098 return -1;
19099 },
19100 execCommand: function () {
19101 var rng = this.selection.getRange(),
19102 table = domUtils.findParentByTagName(rng.startContainer, 'table');
19103 if (table) {
19104 domUtils.remove(table.getElementsByTagName('caption')[0]);
19105 var range = this.selection.getRange();
19106 range.setStart(table.rows[0].cells[0], 0).setCursor();
19107 }
19108
19109 }
19110 };
19111 UE.commands['inserttitle'] = {
19112 queryCommandState: function () {
19113 var table = getTableItemsByRange(this).table;
19114 if (table) {
19115 var firstRow = table.rows[0];
19116 return firstRow.cells[firstRow.cells.length-1].tagName.toLowerCase() != 'th' ? 0 : -1
19117 }
19118 return -1;
19119 },
19120 execCommand: function () {
19121 var table = getTableItemsByRange(this).table;
19122 if (table) {
19123 getUETable(table).insertRow(0, 'th');
19124 }
19125 var th = table.getElementsByTagName('th')[0];
19126 this.selection.getRange().setStart(th, 0).setCursor(false, true);
19127 }
19128 };
19129 UE.commands['deletetitle'] = {
19130 queryCommandState: function () {
19131 var table = getTableItemsByRange(this).table;
19132 if (table) {
19133 var firstRow = table.rows[0];
19134 return firstRow.cells[firstRow.cells.length-1].tagName.toLowerCase() == 'th' ? 0 : -1
19135 }
19136 return -1;
19137 },
19138 execCommand: function () {
19139 var table = getTableItemsByRange(this).table;
19140 if (table) {
19141 domUtils.remove(table.rows[0])
19142 }
19143 var td = table.getElementsByTagName('td')[0];
19144 this.selection.getRange().setStart(td, 0).setCursor(false, true);
19145 }
19146 };
19147 UE.commands['inserttitlecol'] = {
19148 queryCommandState: function () {
19149 var table = getTableItemsByRange(this).table;
19150 if (table) {
19151 var lastRow = table.rows[table.rows.length-1];
19152 return lastRow.getElementsByTagName('th').length ? -1 : 0;
19153 }
19154 return -1;
19155 },
19156 execCommand: function (cmd) {
19157 var table = getTableItemsByRange(this).table;
19158 if (table) {
19159 getUETable(table).insertCol(0, 'th');
19160 }
19161 resetTdWidth(table, this);
19162 var th = table.getElementsByTagName('th')[0];
19163 this.selection.getRange().setStart(th, 0).setCursor(false, true);
19164 }
19165 };
19166 UE.commands['deletetitlecol'] = {
19167 queryCommandState: function () {
19168 var table = getTableItemsByRange(this).table;
19169 if (table) {
19170 var lastRow = table.rows[table.rows.length-1];
19171 return lastRow.getElementsByTagName('th').length ? 0 : -1;
19172 }
19173 return -1;
19174 },
19175 execCommand: function () {
19176 var table = getTableItemsByRange(this).table;
19177 if (table) {
19178 for(var i = 0; i< table.rows.length; i++ ){
19179 domUtils.remove(table.rows[i].children[0])
19180 }
19181 }
19182 resetTdWidth(table, this);
19183 var td = table.getElementsByTagName('td')[0];
19184 this.selection.getRange().setStart(td, 0).setCursor(false, true);
19185 }
19186 };
19187
19188 UE.commands["mergeright"] = {
19189 queryCommandState: function (cmd) {
19190 var tableItems = getTableItemsByRange(this),
19191 table = tableItems.table,
19192 cell = tableItems.cell;
19193
19194 if (!table || !cell) return -1;
19195 var ut = getUETable(table);
19196 if (ut.selectedTds.length) return -1;
19197
19198 var cellInfo = ut.getCellInfo(cell),
19199 rightColIndex = cellInfo.colIndex + cellInfo.colSpan;
19200 if (rightColIndex >= ut.colsNum) return -1; // 如果处于最右边则不能向右合并
19201
19202 var rightCellInfo = ut.indexTable[cellInfo.rowIndex][rightColIndex],
19203 rightCell = table.rows[rightCellInfo.rowIndex].cells[rightCellInfo.cellIndex];
19204 if (!rightCell || cell.tagName != rightCell.tagName) return -1; // TH和TD不能相互合并
19205
19206 // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并
19207 return (rightCellInfo.rowIndex == cellInfo.rowIndex && rightCellInfo.rowSpan == cellInfo.rowSpan) ? 0 : -1;
19208 },
19209 execCommand: function (cmd) {
19210 var rng = this.selection.getRange(),
19211 bk = rng.createBookmark(true);
19212 var cell = getTableItemsByRange(this).cell,
19213 ut = getUETable(cell);
19214 ut.mergeRight(cell);
19215 rng.moveToBookmark(bk).select();
19216 }
19217 };
19218 UE.commands["mergedown"] = {
19219 queryCommandState: function (cmd) {
19220 var tableItems = getTableItemsByRange(this),
19221 table = tableItems.table,
19222 cell = tableItems.cell;
19223
19224 if (!table || !cell) return -1;
19225 var ut = getUETable(table);
19226 if (ut.selectedTds.length)return -1;
19227
19228 var cellInfo = ut.getCellInfo(cell),
19229 downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan;
19230 if (downRowIndex >= ut.rowsNum) return -1; // 如果处于最下边则不能向下合并
19231
19232 var downCellInfo = ut.indexTable[downRowIndex][cellInfo.colIndex],
19233 downCell = table.rows[downCellInfo.rowIndex].cells[downCellInfo.cellIndex];
19234 if (!downCell || cell.tagName != downCell.tagName) return -1; // TH和TD不能相互合并
19235
19236 // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并
19237 return (downCellInfo.colIndex == cellInfo.colIndex && downCellInfo.colSpan == cellInfo.colSpan) ? 0 : -1;
19238 },
19239 execCommand: function () {
19240 var rng = this.selection.getRange(),
19241 bk = rng.createBookmark(true);
19242 var cell = getTableItemsByRange(this).cell,
19243 ut = getUETable(cell);
19244 ut.mergeDown(cell);
19245 rng.moveToBookmark(bk).select();
19246 }
19247 };
19248 UE.commands["mergecells"] = {
19249 queryCommandState: function () {
19250 return getUETableBySelected(this) ? 0 : -1;
19251 },
19252 execCommand: function () {
19253 var ut = getUETableBySelected(this);
19254 if (ut && ut.selectedTds.length) {
19255 var cell = ut.selectedTds[0];
19256 ut.mergeRange();
19257 var rng = this.selection.getRange();
19258 if (domUtils.isEmptyBlock(cell)) {
19259 rng.setStart(cell, 0).collapse(true)
19260 } else {
19261 rng.selectNodeContents(cell)
19262 }
19263 rng.select();
19264 }
19265
19266
19267 }
19268 };
19269 UE.commands["insertrow"] = {
19270 queryCommandState: function () {
19271 var tableItems = getTableItemsByRange(this),
19272 cell = tableItems.cell;
19273 return cell && (cell.tagName == "TD" || (cell.tagName == 'TH' && tableItems.tr !== tableItems.table.rows[0])) &&
19274 getUETable(tableItems.table).rowsNum < this.options.maxRowNum ? 0 : -1;
19275 },
19276 execCommand: function () {
19277 var rng = this.selection.getRange(),
19278 bk = rng.createBookmark(true);
19279 var tableItems = getTableItemsByRange(this),
19280 cell = tableItems.cell,
19281 table = tableItems.table,
19282 ut = getUETable(table),
19283 cellInfo = ut.getCellInfo(cell);
19284 //ut.insertRow(!ut.selectedTds.length ? cellInfo.rowIndex:ut.cellsRange.beginRowIndex,'');
19285 if (!ut.selectedTds.length) {
19286 ut.insertRow(cellInfo.rowIndex, cell);
19287 } else {
19288 var range = ut.cellsRange;
19289 for (var i = 0, len = range.endRowIndex - range.beginRowIndex + 1; i < len; i++) {
19290 ut.insertRow(range.beginRowIndex, cell);
19291 }
19292 }
19293 rng.moveToBookmark(bk).select();
19294 if (table.getAttribute("interlaced") === "enabled")this.fireEvent("interlacetable", table);
19295 }
19296 };
19297 //后插入行
19298 UE.commands["insertrownext"] = {
19299 queryCommandState: function () {
19300 var tableItems = getTableItemsByRange(this),
19301 cell = tableItems.cell;
19302 return cell && (cell.tagName == "TD") && getUETable(tableItems.table).rowsNum < this.options.maxRowNum ? 0 : -1;
19303 },
19304 execCommand: function () {
19305 var rng = this.selection.getRange(),
19306 bk = rng.createBookmark(true);
19307 var tableItems = getTableItemsByRange(this),
19308 cell = tableItems.cell,
19309 table = tableItems.table,
19310 ut = getUETable(table),
19311 cellInfo = ut.getCellInfo(cell);
19312 //ut.insertRow(!ut.selectedTds.length? cellInfo.rowIndex + cellInfo.rowSpan : ut.cellsRange.endRowIndex + 1,'');
19313 if (!ut.selectedTds.length) {
19314 ut.insertRow(cellInfo.rowIndex + cellInfo.rowSpan, cell);
19315 } else {
19316 var range = ut.cellsRange;
19317 for (var i = 0, len = range.endRowIndex - range.beginRowIndex + 1; i < len; i++) {
19318 ut.insertRow(range.endRowIndex + 1, cell);
19319 }
19320 }
19321 rng.moveToBookmark(bk).select();
19322 if (table.getAttribute("interlaced") === "enabled")this.fireEvent("interlacetable", table);
19323 }
19324 };
19325 UE.commands["deleterow"] = {
19326 queryCommandState: function () {
19327 var tableItems = getTableItemsByRange(this);
19328 return tableItems.cell ? 0 : -1;
19329 },
19330 execCommand: function () {
19331 var cell = getTableItemsByRange(this).cell,
19332 ut = getUETable(cell),
19333 cellsRange = ut.cellsRange,
19334 cellInfo = ut.getCellInfo(cell),
19335 preCell = ut.getVSideCell(cell),
19336 nextCell = ut.getVSideCell(cell, true),
19337 rng = this.selection.getRange();
19338 if (utils.isEmptyObject(cellsRange)) {
19339 ut.deleteRow(cellInfo.rowIndex);
19340 } else {
19341 for (var i = cellsRange.beginRowIndex; i < cellsRange.endRowIndex + 1; i++) {
19342 ut.deleteRow(cellsRange.beginRowIndex);
19343 }
19344 }
19345 var table = ut.table;
19346 if (!table.getElementsByTagName('td').length) {
19347 var nextSibling = table.nextSibling;
19348 domUtils.remove(table);
19349 if (nextSibling) {
19350 rng.setStart(nextSibling, 0).setCursor(false, true);
19351 }
19352 } else {
19353 if (cellInfo.rowSpan == 1 || cellInfo.rowSpan == cellsRange.endRowIndex - cellsRange.beginRowIndex + 1) {
19354 if (nextCell || preCell) rng.selectNodeContents(nextCell || preCell).setCursor(false, true);
19355 } else {
19356 var newCell = ut.getCell(cellInfo.rowIndex, ut.indexTable[cellInfo.rowIndex][cellInfo.colIndex].cellIndex);
19357 if (newCell) rng.selectNodeContents(newCell).setCursor(false, true);
19358 }
19359 }
19360 if (table.getAttribute("interlaced") === "enabled")this.fireEvent("interlacetable", table);
19361 }
19362 };
19363 UE.commands["insertcol"] = {
19364 queryCommandState: function (cmd) {
19365 var tableItems = getTableItemsByRange(this),
19366 cell = tableItems.cell;
19367 return cell && (cell.tagName == "TD" || (cell.tagName == 'TH' && cell !== tableItems.tr.cells[0])) &&
19368 getUETable(tableItems.table).colsNum < this.options.maxColNum ? 0 : -1;
19369 },
19370 execCommand: function (cmd) {
19371 var rng = this.selection.getRange(),
19372 bk = rng.createBookmark(true);
19373 if (this.queryCommandState(cmd) == -1)return;
19374 var cell = getTableItemsByRange(this).cell,
19375 ut = getUETable(cell),
19376 cellInfo = ut.getCellInfo(cell);
19377
19378 //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex:ut.cellsRange.beginColIndex);
19379 if (!ut.selectedTds.length) {
19380 ut.insertCol(cellInfo.colIndex, cell);
19381 } else {
19382 var range = ut.cellsRange;
19383 for (var i = 0, len = range.endColIndex - range.beginColIndex + 1; i < len; i++) {
19384 ut.insertCol(range.beginColIndex, cell);
19385 }
19386 }
19387 rng.moveToBookmark(bk).select(true);
19388 }
19389 };
19390 UE.commands["insertcolnext"] = {
19391 queryCommandState: function () {
19392 var tableItems = getTableItemsByRange(this),
19393 cell = tableItems.cell;
19394 return cell && getUETable(tableItems.table).colsNum < this.options.maxColNum ? 0 : -1;
19395 },
19396 execCommand: function () {
19397 var rng = this.selection.getRange(),
19398 bk = rng.createBookmark(true);
19399 var cell = getTableItemsByRange(this).cell,
19400 ut = getUETable(cell),
19401 cellInfo = ut.getCellInfo(cell);
19402 //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex + cellInfo.colSpan:ut.cellsRange.endColIndex +1);
19403 if (!ut.selectedTds.length) {
19404 ut.insertCol(cellInfo.colIndex + cellInfo.colSpan, cell);
19405 } else {
19406 var range = ut.cellsRange;
19407 for (var i = 0, len = range.endColIndex - range.beginColIndex + 1; i < len; i++) {
19408 ut.insertCol(range.endColIndex + 1, cell);
19409 }
19410 }
19411 rng.moveToBookmark(bk).select();
19412 }
19413 };
19414
19415 UE.commands["deletecol"] = {
19416 queryCommandState: function () {
19417 var tableItems = getTableItemsByRange(this);
19418 return tableItems.cell ? 0 : -1;
19419 },
19420 execCommand: function () {
19421 var cell = getTableItemsByRange(this).cell,
19422 ut = getUETable(cell),
19423 range = ut.cellsRange,
19424 cellInfo = ut.getCellInfo(cell),
19425 preCell = ut.getHSideCell(cell),
19426 nextCell = ut.getHSideCell(cell, true);
19427 if (utils.isEmptyObject(range)) {
19428 ut.deleteCol(cellInfo.colIndex);
19429 } else {
19430 for (var i = range.beginColIndex; i < range.endColIndex + 1; i++) {
19431 ut.deleteCol(range.beginColIndex);
19432 }
19433 }
19434 var table = ut.table,
19435 rng = this.selection.getRange();
19436
19437 if (!table.getElementsByTagName('td').length) {
19438 var nextSibling = table.nextSibling;
19439 domUtils.remove(table);
19440 if (nextSibling) {
19441 rng.setStart(nextSibling, 0).setCursor(false, true);
19442 }
19443 } else {
19444 if (domUtils.inDoc(cell, this.document)) {
19445 rng.setStart(cell, 0).setCursor(false, true);
19446 } else {
19447 if (nextCell && domUtils.inDoc(nextCell, this.document)) {
19448 rng.selectNodeContents(nextCell).setCursor(false, true);
19449 } else {
19450 if (preCell && domUtils.inDoc(preCell, this.document)) {
19451 rng.selectNodeContents(preCell).setCursor(true, true);
19452 }
19453 }
19454 }
19455 }
19456 }
19457 };
19458 UE.commands["splittocells"] = {
19459 queryCommandState: function () {
19460 var tableItems = getTableItemsByRange(this),
19461 cell = tableItems.cell;
19462 if (!cell) return -1;
19463 var ut = getUETable(tableItems.table);
19464 if (ut.selectedTds.length > 0) return -1;
19465 return cell && (cell.colSpan > 1 || cell.rowSpan > 1) ? 0 : -1;
19466 },
19467 execCommand: function () {
19468 var rng = this.selection.getRange(),
19469 bk = rng.createBookmark(true);
19470 var cell = getTableItemsByRange(this).cell,
19471 ut = getUETable(cell);
19472 ut.splitToCells(cell);
19473 rng.moveToBookmark(bk).select();
19474 }
19475 };
19476 UE.commands["splittorows"] = {
19477 queryCommandState: function () {
19478 var tableItems = getTableItemsByRange(this),
19479 cell = tableItems.cell;
19480 if (!cell) return -1;
19481 var ut = getUETable(tableItems.table);
19482 if (ut.selectedTds.length > 0) return -1;
19483 return cell && cell.rowSpan > 1 ? 0 : -1;
19484 },
19485 execCommand: function () {
19486 var rng = this.selection.getRange(),
19487 bk = rng.createBookmark(true);
19488 var cell = getTableItemsByRange(this).cell,
19489 ut = getUETable(cell);
19490 ut.splitToRows(cell);
19491 rng.moveToBookmark(bk).select();
19492 }
19493 };
19494 UE.commands["splittocols"] = {
19495 queryCommandState: function () {
19496 var tableItems = getTableItemsByRange(this),
19497 cell = tableItems.cell;
19498 if (!cell) return -1;
19499 var ut = getUETable(tableItems.table);
19500 if (ut.selectedTds.length > 0) return -1;
19501 return cell && cell.colSpan > 1 ? 0 : -1;
19502 },
19503 execCommand: function () {
19504 var rng = this.selection.getRange(),
19505 bk = rng.createBookmark(true);
19506 var cell = getTableItemsByRange(this).cell,
19507 ut = getUETable(cell);
19508 ut.splitToCols(cell);
19509 rng.moveToBookmark(bk).select();
19510
19511 }
19512 };
19513
19514 UE.commands["adaptbytext"] =
19515 UE.commands["adaptbywindow"] = {
19516 queryCommandState: function () {
19517 return getTableItemsByRange(this).table ? 0 : -1
19518 },
19519 execCommand: function (cmd) {
19520 var tableItems = getTableItemsByRange(this),
19521 table = tableItems.table;
19522 if (table) {
19523 if (cmd == 'adaptbywindow') {
19524 resetTdWidth(table, this);
19525 } else {
19526 var cells = domUtils.getElementsByTagName(table, "td th");
19527 utils.each(cells, function (cell) {
19528 cell.removeAttribute("width");
19529 });
19530 table.removeAttribute("width");
19531 }
19532 }
19533 }
19534 };
19535
19536 //平均分配各列
19537 UE.commands['averagedistributecol'] = {
19538 queryCommandState: function () {
19539 var ut = getUETableBySelected(this);
19540 if (!ut) return -1;
19541 return ut.isFullRow() || ut.isFullCol() ? 0 : -1;
19542 },
19543 execCommand: function (cmd) {
19544 var me = this,
19545 ut = getUETableBySelected(me);
19546
19547 function getAverageWidth() {
19548 var tb = ut.table,
19549 averageWidth, sumWidth = 0, colsNum = 0,
19550 tbAttr = getDefaultValue(me, tb);
19551
19552 if (ut.isFullRow()) {
19553 sumWidth = tb.offsetWidth;
19554 colsNum = ut.colsNum;
19555 } else {
19556 var begin = ut.cellsRange.beginColIndex,
19557 end = ut.cellsRange.endColIndex,
19558 node;
19559 for (var i = begin; i <= end;) {
19560 node = ut.selectedTds[i];
19561 sumWidth += node.offsetWidth;
19562 i += node.colSpan;
19563 colsNum += 1;
19564 }
19565 }
19566 averageWidth = Math.ceil(sumWidth / colsNum) - tbAttr.tdBorder * 2 - tbAttr.tdPadding * 2;
19567 return averageWidth;
19568 }
19569
19570 function setAverageWidth(averageWidth) {
19571 utils.each(domUtils.getElementsByTagName(ut.table, "th"), function (node) {
19572 node.setAttribute("width", "");
19573 });
19574 var cells = ut.isFullRow() ? domUtils.getElementsByTagName(ut.table, "td") : ut.selectedTds;
19575
19576 utils.each(cells, function (node) {
19577 if (node.colSpan == 1) {
19578 node.setAttribute("width", averageWidth);
19579 }
19580 });
19581 }
19582
19583 if (ut && ut.selectedTds.length) {
19584 setAverageWidth(getAverageWidth());
19585 }
19586 }
19587 };
19588 //平均分配各行
19589 UE.commands['averagedistributerow'] = {
19590 queryCommandState: function () {
19591 var ut = getUETableBySelected(this);
19592 if (!ut) return -1;
19593 if (ut.selectedTds && /th/ig.test(ut.selectedTds[0].tagName)) return -1;
19594 return ut.isFullRow() || ut.isFullCol() ? 0 : -1;
19595 },
19596 execCommand: function (cmd) {
19597 var me = this,
19598 ut = getUETableBySelected(me);
19599
19600 function getAverageHeight() {
19601 var averageHeight, rowNum, sumHeight = 0,
19602 tb = ut.table,
19603 tbAttr = getDefaultValue(me, tb),
19604 tdpadding = parseInt(domUtils.getComputedStyle(tb.getElementsByTagName('td')[0], "padding-top"));
19605
19606 if (ut.isFullCol()) {
19607 var captionArr = domUtils.getElementsByTagName(tb, "caption"),
19608 thArr = domUtils.getElementsByTagName(tb, "th"),
19609 captionHeight, thHeight;
19610
19611 if (captionArr.length > 0) {
19612 captionHeight = captionArr[0].offsetHeight;
19613 }
19614 if (thArr.length > 0) {
19615 thHeight = thArr[0].offsetHeight;
19616 }
19617
19618 sumHeight = tb.offsetHeight - (captionHeight || 0) - (thHeight || 0);
19619 rowNum = thArr.length == 0 ? ut.rowsNum : (ut.rowsNum - 1);
19620 } else {
19621 var begin = ut.cellsRange.beginRowIndex,
19622 end = ut.cellsRange.endRowIndex,
19623 count = 0,
19624 trs = domUtils.getElementsByTagName(tb, "tr");
19625 for (var i = begin; i <= end; i++) {
19626 sumHeight += trs[i].offsetHeight;
19627 count += 1;
19628 }
19629 rowNum = count;
19630 }
19631 //ie8下是混杂模式
19632 if (browser.ie && browser.version < 9) {
19633 averageHeight = Math.ceil(sumHeight / rowNum);
19634 } else {
19635 averageHeight = Math.ceil(sumHeight / rowNum) - tbAttr.tdBorder * 2 - tdpadding * 2;
19636 }
19637 return averageHeight;
19638 }
19639
19640 function setAverageHeight(averageHeight) {
19641 var cells = ut.isFullCol() ? domUtils.getElementsByTagName(ut.table, "td") : ut.selectedTds;
19642 utils.each(cells, function (node) {
19643 if (node.rowSpan == 1) {
19644 node.setAttribute("height", averageHeight);
19645 }
19646 });
19647 }
19648
19649 if (ut && ut.selectedTds.length) {
19650 setAverageHeight(getAverageHeight());
19651 }
19652 }
19653 };
19654
19655 //单元格对齐方式
19656 UE.commands['cellalignment'] = {
19657 queryCommandState: function () {
19658 return getTableItemsByRange(this).table ? 0 : -1
19659 },
19660 execCommand: function (cmd, data) {
19661 var me = this,
19662 ut = getUETableBySelected(me);
19663
19664 if (!ut) {
19665 var start = me.selection.getStart(),
19666 cell = start && domUtils.findParentByTagName(start, ["td", "th", "caption"], true);
19667 if (!/caption/ig.test(cell.tagName)) {
19668 domUtils.setAttributes(cell, data);
19669 } else {
19670 cell.style.textAlign = data.align;
19671 cell.style.verticalAlign = data.vAlign;
19672 }
19673 me.selection.getRange().setCursor(true);
19674 } else {
19675 utils.each(ut.selectedTds, function (cell) {
19676 domUtils.setAttributes(cell, data);
19677 });
19678 }
19679 },
19680 /**
19681 * 查询当前点击的单元格的对齐状态, 如果当前已经选择了多个单元格, 则会返回所有单元格经过统一协调过后的状态
19682 * @see UE.UETable.getTableCellAlignState
19683 */
19684 queryCommandValue: function (cmd) {
19685
19686 var activeMenuCell = getTableItemsByRange( this).cell;
19687
19688 if( !activeMenuCell ) {
19689 activeMenuCell = getSelectedArr(this)[0];
19690 }
19691
19692 if (!activeMenuCell) {
19693
19694 return null;
19695
19696 } else {
19697
19698 //获取同时选中的其他单元格
19699 var cells = UE.UETable.getUETable(activeMenuCell).selectedTds;
19700
19701 !cells.length && ( cells = activeMenuCell );
19702
19703 return UE.UETable.getTableCellAlignState(cells);
19704
19705 }
19706
19707 }
19708 };
19709 //表格对齐方式
19710 UE.commands['tablealignment'] = {
19711 queryCommandState: function () {
19712 if (browser.ie && browser.version < 8) {
19713 return -1;
19714 }
19715 return getTableItemsByRange(this).table ? 0 : -1
19716 },
19717 execCommand: function (cmd, value) {
19718 var me = this,
19719 start = me.selection.getStart(),
19720 table = start && domUtils.findParentByTagName(start, ["table"], true);
19721
19722 if (table) {
19723 table.setAttribute("align",value);
19724 }
19725 }
19726 };
19727
19728 //表格属性
19729 UE.commands['edittable'] = {
19730 queryCommandState: function () {
19731 return getTableItemsByRange(this).table ? 0 : -1
19732 },
19733 execCommand: function (cmd, color) {
19734 var rng = this.selection.getRange(),
19735 table = domUtils.findParentByTagName(rng.startContainer, 'table');
19736 if (table) {
19737 var arr = domUtils.getElementsByTagName(table, "td").concat(
19738 domUtils.getElementsByTagName(table, "th"),
19739 domUtils.getElementsByTagName(table, "caption")
19740 );
19741 utils.each(arr, function (node) {
19742 node.style.borderColor = color;
19743 });
19744 }
19745 }
19746 };
19747 //单元格属性
19748 UE.commands['edittd'] = {
19749 queryCommandState: function () {
19750 return getTableItemsByRange(this).table ? 0 : -1
19751 },
19752 execCommand: function (cmd, bkColor) {
19753 var me = this,
19754 ut = getUETableBySelected(me);
19755
19756 if (!ut) {
19757 var start = me.selection.getStart(),
19758 cell = start && domUtils.findParentByTagName(start, ["td", "th", "caption"], true);
19759 if (cell) {
19760 cell.style.backgroundColor = bkColor;
19761 }
19762 } else {
19763 utils.each(ut.selectedTds, function (cell) {
19764 cell.style.backgroundColor = bkColor;
19765 });
19766 }
19767 }
19768 };
19769
19770 UE.commands["settablebackground"] = {
19771 queryCommandState: function () {
19772 return getSelectedArr(this).length > 1 ? 0 : -1;
19773 },
19774 execCommand: function (cmd, value) {
19775 var cells, ut;
19776 cells = getSelectedArr(this);
19777 ut = getUETable(cells[0]);
19778 ut.setBackground(cells, value);
19779 }
19780 };
19781
19782 UE.commands["cleartablebackground"] = {
19783 queryCommandState: function () {
19784 var cells = getSelectedArr(this);
19785 if (!cells.length)return -1;
19786 for (var i = 0, cell; cell = cells[i++];) {
19787 if (cell.style.backgroundColor !== "") return 0;
19788 }
19789 return -1;
19790 },
19791 execCommand: function () {
19792 var cells = getSelectedArr(this),
19793 ut = getUETable(cells[0]);
19794 ut.removeBackground(cells);
19795 }
19796 };
19797
19798 UE.commands["interlacetable"] = UE.commands["uninterlacetable"] = {
19799 queryCommandState: function (cmd) {
19800 var table = getTableItemsByRange(this).table;
19801 if (!table) return -1;
19802 var interlaced = table.getAttribute("interlaced");
19803 if (cmd == "interlacetable") {
19804 //TODO 待定
19805 //是否需要待定,如果设置,则命令只能单次执行成功,但反射具备toggle效果;否则可以覆盖前次命令,但反射将不存在toggle效果
19806 return (interlaced === "enabled") ? -1 : 0;
19807 } else {
19808 return (!interlaced || interlaced === "disabled") ? -1 : 0;
19809 }
19810 },
19811 execCommand: function (cmd, classList) {
19812 var table = getTableItemsByRange(this).table;
19813 if (cmd == "interlacetable") {
19814 table.setAttribute("interlaced", "enabled");
19815 this.fireEvent("interlacetable", table, classList);
19816 } else {
19817 table.setAttribute("interlaced", "disabled");
19818 this.fireEvent("uninterlacetable", table);
19819 }
19820 }
19821 };
19822 UE.commands["setbordervisible"] = {
19823 queryCommandState: function (cmd) {
19824 var table = getTableItemsByRange(this).table;
19825 if (!table) return -1;
19826 return 0;
19827 },
19828 execCommand: function () {
19829 var table = getTableItemsByRange(this).table;
19830 utils.each(domUtils.getElementsByTagName(table,'td'),function(td){
19831 td.style.borderWidth = '1px';
19832 td.style.borderStyle = 'solid';
19833 })
19834 }
19835 };
19836 function resetTdWidth(table, editor) {
19837 var tds = domUtils.getElementsByTagName(table,'td th');
19838 utils.each(tds, function (td) {
19839 td.removeAttribute("width");
19840 });
19841 table.setAttribute('width', getTableWidth(editor, true, getDefaultValue(editor, table)));
19842 var tdsWidths = [];
19843 setTimeout(function () {
19844 utils.each(tds, function (td) {
19845 (td.colSpan == 1) && tdsWidths.push(td.offsetWidth)
19846 })
19847 utils.each(tds, function (td,i) {
19848 (td.colSpan == 1) && td.setAttribute("width", tdsWidths[i] + "");
19849 })
19850 }, 0);
19851 }
19852
19853 function getTableWidth(editor, needIEHack, defaultValue) {
19854 var body = editor.body;
19855 return body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (editor.options.offsetWidth || 0);
19856 }
19857
19858 function getSelectedArr(editor) {
19859 var cell = getTableItemsByRange(editor).cell;
19860 if (cell) {
19861 var ut = getUETable(cell);
19862 return ut.selectedTds.length ? ut.selectedTds : [cell];
19863 } else {
19864 return [];
19865 }
19866 }
19867})();
19868
19869
19870// plugins/table.action.js
19871/**
19872 * Created with JetBrains PhpStorm.
19873 * User: taoqili
19874 * Date: 12-10-12
19875 * Time: 上午10:05
19876 * To change this template use File | Settings | File Templates.
19877 */
19878UE.plugins['table'] = function () {
19879 var me = this,
19880 tabTimer = null,
19881 //拖动计时器
19882 tableDragTimer = null,
19883 //双击计时器
19884 tableResizeTimer = null,
19885 //单元格最小宽度
19886 cellMinWidth = 5,
19887 isInResizeBuffer = false,
19888 //单元格边框大小
19889 cellBorderWidth = 5,
19890 //鼠标偏移距离
19891 offsetOfTableCell = 10,
19892 //记录在有限时间内的点击状态, 共有3个取值, 0, 1, 2。 0代表未初始化, 1代表单击了1次,2代表2次
19893 singleClickState = 0,
19894 userActionStatus = null,
19895 //双击允许的时间范围
19896 dblclickTime = 360,
19897 UT = UE.UETable,
19898 getUETable = function (tdOrTable) {
19899 return UT.getUETable(tdOrTable);
19900 },
19901 getUETableBySelected = function (editor) {
19902 return UT.getUETableBySelected(editor);
19903 },
19904 getDefaultValue = function (editor, table) {
19905 return UT.getDefaultValue(editor, table);
19906 },
19907 removeSelectedClass = function (cells) {
19908 return UT.removeSelectedClass(cells);
19909 };
19910
19911 function showError(e) {
19912// throw e;
19913 }
19914 me.ready(function(){
19915 var me = this;
19916 var orgGetText = me.selection.getText;
19917 me.selection.getText = function(){
19918 var table = getUETableBySelected(me);
19919 if(table){
19920 var str = '';
19921 utils.each(table.selectedTds,function(td){
19922 str += td[browser.ie?'innerText':'textContent'];
19923 })
19924 return str;
19925 }else{
19926 return orgGetText.call(me.selection)
19927 }
19928
19929 }
19930 })
19931
19932 //处理拖动及框选相关方法
19933 var startTd = null, //鼠标按下时的锚点td
19934 currentTd = null, //当前鼠标经过时的td
19935 onDrag = "", //指示当前拖动状态,其值可为"","h","v" ,分别表示未拖动状态,横向拖动状态,纵向拖动状态,用于鼠标移动过程中的判断
19936 onBorder = false, //检测鼠标按下时是否处在单元格边缘位置
19937 dragButton = null,
19938 dragOver = false,
19939 dragLine = null, //模拟的拖动线
19940 dragTd = null; //发生拖动的目标td
19941
19942 var mousedown = false,
19943 //todo 判断混乱模式
19944 needIEHack = true;
19945
19946 me.setOpt({
19947 'maxColNum':20,
19948 'maxRowNum':100,
19949 'defaultCols':5,
19950 'defaultRows':5,
19951 'tdvalign':'top',
19952 'cursorpath':me.options.UEDITOR_HOME_URL + "themes/default/images/cursor_",
19953 'tableDragable':false,
19954 'classList':["ue-table-interlace-color-single","ue-table-interlace-color-double"]
19955 });
19956 me.getUETable = getUETable;
19957 var commands = {
19958 'deletetable':1,
19959 'inserttable':1,
19960 'cellvalign':1,
19961 'insertcaption':1,
19962 'deletecaption':1,
19963 'inserttitle':1,
19964 'deletetitle':1,
19965 "mergeright":1,
19966 "mergedown":1,
19967 "mergecells":1,
19968 "insertrow":1,
19969 "insertrownext":1,
19970 "deleterow":1,
19971 "insertcol":1,
19972 "insertcolnext":1,
19973 "deletecol":1,
19974 "splittocells":1,
19975 "splittorows":1,
19976 "splittocols":1,
19977 "adaptbytext":1,
19978 "adaptbywindow":1,
19979 "adaptbycustomer":1,
19980 "insertparagraph":1,
19981 "insertparagraphbeforetable":1,
19982 "averagedistributecol":1,
19983 "averagedistributerow":1
19984 };
19985 me.ready(function () {
19986 utils.cssRule('table',
19987 //选中的td上的样式
19988 '.selectTdClass{background-color:#edf5fa !important}' +
19989 'table.noBorderTable td,table.noBorderTable th,table.noBorderTable caption{border:1px dashed #ddd !important}' +
19990 //插入的表格的默认样式
19991 'table{margin-bottom:10px;border-collapse:collapse;display:table;}' +
19992 'td,th{padding: 5px 10px;border: 1px solid #DDD;}' +
19993 'caption{border:1px dashed #DDD;border-bottom:0;padding:3px;text-align:center;}' +
19994 'th{border-top:1px solid #BBB;background-color:#F7F7F7;}' +
19995 'table tr.firstRow th{border-top-width:2px;}' +
19996 '.ue-table-interlace-color-single{ background-color: #fcfcfc; } .ue-table-interlace-color-double{ background-color: #f7faff; }' +
19997 'td p{margin:0;padding:0;}', me.document);
19998
19999 var tableCopyList, isFullCol, isFullRow;
20000 //注册del/backspace事件
20001 me.addListener('keydown', function (cmd, evt) {
20002 var me = this;
20003 var keyCode = evt.keyCode || evt.which;
20004
20005 if (keyCode == 8) {
20006
20007 var ut = getUETableBySelected(me);
20008 if (ut && ut.selectedTds.length) {
20009
20010 if (ut.isFullCol()) {
20011 me.execCommand('deletecol')
20012 } else if (ut.isFullRow()) {
20013 me.execCommand('deleterow')
20014 } else {
20015 me.fireEvent('delcells');
20016 }
20017 domUtils.preventDefault(evt);
20018 }
20019
20020 var caption = domUtils.findParentByTagName(me.selection.getStart(), 'caption', true),
20021 range = me.selection.getRange();
20022 if (range.collapsed && caption && isEmptyBlock(caption)) {
20023 me.fireEvent('saveScene');
20024 var table = caption.parentNode;
20025 domUtils.remove(caption);
20026 if (table) {
20027 range.setStart(table.rows[0].cells[0], 0).setCursor(false, true);
20028 }
20029 me.fireEvent('saveScene');
20030 }
20031
20032 }
20033
20034 if (keyCode == 46) {
20035
20036 ut = getUETableBySelected(me);
20037 if (ut) {
20038 me.fireEvent('saveScene');
20039 for (var i = 0, ci; ci = ut.selectedTds[i++];) {
20040 domUtils.fillNode(me.document, ci)
20041 }
20042 me.fireEvent('saveScene');
20043 domUtils.preventDefault(evt);
20044
20045 }
20046
20047 }
20048 if (keyCode == 13) {
20049
20050 var rng = me.selection.getRange(),
20051 caption = domUtils.findParentByTagName(rng.startContainer, 'caption', true);
20052 if (caption) {
20053 var table = domUtils.findParentByTagName(caption, 'table');
20054 if (!rng.collapsed) {
20055
20056 rng.deleteContents();
20057 me.fireEvent('saveScene');
20058 } else {
20059 if (caption) {
20060 rng.setStart(table.rows[0].cells[0], 0).setCursor(false, true);
20061 }
20062 }
20063 domUtils.preventDefault(evt);
20064 return;
20065 }
20066 if (rng.collapsed) {
20067 var table = domUtils.findParentByTagName(rng.startContainer, 'table');
20068 if (table) {
20069 var cell = table.rows[0].cells[0],
20070 start = domUtils.findParentByTagName(me.selection.getStart(), ['td', 'th'], true),
20071 preNode = table.previousSibling;
20072 if (cell === start && (!preNode || preNode.nodeType == 1 && preNode.tagName == 'TABLE' ) && domUtils.isStartInblock(rng)) {
20073 var first = domUtils.findParent(me.selection.getStart(), function(n){return domUtils.isBlockElm(n)}, true);
20074 if(first && ( /t(h|d)/i.test(first.tagName) || first === start.firstChild )){
20075 me.execCommand('insertparagraphbeforetable');
20076 domUtils.preventDefault(evt);
20077 }
20078
20079 }
20080 }
20081 }
20082 }
20083
20084 if ((evt.ctrlKey || evt.metaKey) && evt.keyCode == '67') {
20085 tableCopyList = null;
20086 var ut = getUETableBySelected(me);
20087 if (ut) {
20088 var tds = ut.selectedTds;
20089 isFullCol = ut.isFullCol();
20090 isFullRow = ut.isFullRow();
20091 tableCopyList = [
20092 [ut.cloneCell(tds[0],null,true)]
20093 ];
20094 for (var i = 1, ci; ci = tds[i]; i++) {
20095 if (ci.parentNode !== tds[i - 1].parentNode) {
20096 tableCopyList.push([ut.cloneCell(ci,null,true)]);
20097 } else {
20098 tableCopyList[tableCopyList.length - 1].push(ut.cloneCell(ci,null,true));
20099 }
20100
20101 }
20102 }
20103 }
20104 });
20105 me.addListener("tablehasdeleted",function(){
20106 toggleDraggableState(this, false, "", null);
20107 if (dragButton)domUtils.remove(dragButton);
20108 });
20109
20110 me.addListener('beforepaste', function (cmd, html) {
20111 var me = this;
20112 var rng = me.selection.getRange();
20113 if (domUtils.findParentByTagName(rng.startContainer, 'caption', true)) {
20114 var div = me.document.createElement("div");
20115 div.innerHTML = html.html;
20116 //trace:3729
20117 html.html = div[browser.ie9below ? 'innerText' : 'textContent'];
20118 return;
20119 }
20120 var table = getUETableBySelected(me);
20121 if (tableCopyList) {
20122 me.fireEvent('saveScene');
20123 var rng = me.selection.getRange();
20124 var td = domUtils.findParentByTagName(rng.startContainer, ['td', 'th'], true), tmpNode, preNode;
20125 if (td) {
20126 var ut = getUETable(td);
20127 if (isFullRow) {
20128 var rowIndex = ut.getCellInfo(td).rowIndex;
20129 if (td.tagName == 'TH') {
20130 rowIndex++;
20131 }
20132 for (var i = 0, ci; ci = tableCopyList[i++];) {
20133 var tr = ut.insertRow(rowIndex++, "td");
20134 for (var j = 0, cj; cj = ci[j]; j++) {
20135 var cell = tr.cells[j];
20136 if (!cell) {
20137 cell = tr.insertCell(j)
20138 }
20139 cell.innerHTML = cj.innerHTML;
20140 cj.getAttribute('width') && cell.setAttribute('width', cj.getAttribute('width'));
20141 cj.getAttribute('vAlign') && cell.setAttribute('vAlign', cj.getAttribute('vAlign'));
20142 cj.getAttribute('align') && cell.setAttribute('align', cj.getAttribute('align'));
20143 cj.style.cssText && (cell.style.cssText = cj.style.cssText)
20144 }
20145 for (var j = 0, cj; cj = tr.cells[j]; j++) {
20146 if (!ci[j])
20147 break;
20148 cj.innerHTML = ci[j].innerHTML;
20149 ci[j].getAttribute('width') && cj.setAttribute('width', ci[j].getAttribute('width'));
20150 ci[j].getAttribute('vAlign') && cj.setAttribute('vAlign', ci[j].getAttribute('vAlign'));
20151 ci[j].getAttribute('align') && cj.setAttribute('align', ci[j].getAttribute('align'));
20152 ci[j].style.cssText && (cj.style.cssText = ci[j].style.cssText)
20153 }
20154 }
20155 } else {
20156 if (isFullCol) {
20157 cellInfo = ut.getCellInfo(td);
20158 var maxColNum = 0;
20159 for (var j = 0, ci = tableCopyList[0], cj; cj = ci[j++];) {
20160 maxColNum += cj.colSpan || 1;
20161 }
20162 me.__hasEnterExecCommand = true;
20163 for (i = 0; i < maxColNum; i++) {
20164 me.execCommand('insertcol');
20165 }
20166 me.__hasEnterExecCommand = false;
20167 td = ut.table.rows[0].cells[cellInfo.cellIndex];
20168 if (td.tagName == 'TH') {
20169 td = ut.table.rows[1].cells[cellInfo.cellIndex];
20170 }
20171 }
20172 for (var i = 0, ci; ci = tableCopyList[i++];) {
20173 tmpNode = td;
20174 for (var j = 0, cj; cj = ci[j++];) {
20175 if (td) {
20176 td.innerHTML = cj.innerHTML;
20177 //todo 定制处理
20178 cj.getAttribute('width') && td.setAttribute('width', cj.getAttribute('width'));
20179 cj.getAttribute('vAlign') && td.setAttribute('vAlign', cj.getAttribute('vAlign'));
20180 cj.getAttribute('align') && td.setAttribute('align', cj.getAttribute('align'));
20181 cj.style.cssText && (td.style.cssText = cj.style.cssText);
20182 preNode = td;
20183 td = td.nextSibling;
20184 } else {
20185 var cloneTd = cj.cloneNode(true);
20186 domUtils.removeAttributes(cloneTd, ['class', 'rowSpan', 'colSpan']);
20187
20188 preNode.parentNode.appendChild(cloneTd)
20189 }
20190 }
20191 td = ut.getNextCell(tmpNode, true, true);
20192 if (!tableCopyList[i])
20193 break;
20194 if (!td) {
20195 var cellInfo = ut.getCellInfo(tmpNode);
20196 ut.table.insertRow(ut.table.rows.length);
20197 ut.update();
20198 td = ut.getVSideCell(tmpNode, true);
20199 }
20200 }
20201 }
20202 ut.update();
20203 } else {
20204 table = me.document.createElement('table');
20205 for (var i = 0, ci; ci = tableCopyList[i++];) {
20206 var tr = table.insertRow(table.rows.length);
20207 for (var j = 0, cj; cj = ci[j++];) {
20208 cloneTd = UT.cloneCell(cj,null,true);
20209 domUtils.removeAttributes(cloneTd, ['class']);
20210 tr.appendChild(cloneTd)
20211 }
20212 if (j == 2 && cloneTd.rowSpan > 1) {
20213 cloneTd.rowSpan = 1;
20214 }
20215 }
20216
20217 var defaultValue = getDefaultValue(me),
20218 width = me.body.offsetWidth -
20219 (needIEHack ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (me.options.offsetWidth || 0);
20220 me.execCommand('insertHTML', '<table ' +
20221 ( isFullCol && isFullRow ? 'width="' + width + '"' : '') +
20222 '>' + table.innerHTML.replace(/>\s*</g, '><').replace(/\bth\b/gi, "td") + '</table>')
20223 }
20224 me.fireEvent('contentchange');
20225 me.fireEvent('saveScene');
20226 html.html = '';
20227 return true;
20228 } else {
20229 var div = me.document.createElement("div"), tables;
20230 div.innerHTML = html.html;
20231 tables = div.getElementsByTagName("table");
20232 if (domUtils.findParentByTagName(me.selection.getStart(), 'table')) {
20233 utils.each(tables, function (t) {
20234 domUtils.remove(t)
20235 });
20236 if (domUtils.findParentByTagName(me.selection.getStart(), 'caption', true)) {
20237 div.innerHTML = div[browser.ie ? 'innerText' : 'textContent'];
20238 }
20239 } else {
20240 utils.each(tables, function (table) {
20241 removeStyleSize(table, true);
20242 domUtils.removeAttributes(table, ['style', 'border']);
20243 utils.each(domUtils.getElementsByTagName(table, "td"), function (td) {
20244 if (isEmptyBlock(td)) {
20245 domUtils.fillNode(me.document, td);
20246 }
20247 removeStyleSize(td, true);
20248// domUtils.removeAttributes(td, ['style'])
20249 });
20250 });
20251 }
20252 html.html = div.innerHTML;
20253 }
20254 });
20255
20256 me.addListener('afterpaste', function () {
20257 utils.each(domUtils.getElementsByTagName(me.body, "table"), function (table) {
20258 if (table.offsetWidth > me.body.offsetWidth) {
20259 var defaultValue = getDefaultValue(me, table);
20260 table.style.width = me.body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (me.options.offsetWidth || 0) + 'px'
20261 }
20262 })
20263 });
20264 me.addListener('blur', function () {
20265 tableCopyList = null;
20266 });
20267 var timer;
20268 me.addListener('keydown', function () {
20269 clearTimeout(timer);
20270 timer = setTimeout(function () {
20271 var rng = me.selection.getRange(),
20272 cell = domUtils.findParentByTagName(rng.startContainer, ['th', 'td'], true);
20273 if (cell) {
20274 var table = cell.parentNode.parentNode.parentNode;
20275 if (table.offsetWidth > table.getAttribute("width")) {
20276 cell.style.wordBreak = "break-all";
20277 }
20278 }
20279
20280 }, 100);
20281 });
20282 me.addListener("selectionchange", function () {
20283 toggleDraggableState(me, false, "", null);
20284 });
20285
20286
20287 //内容变化时触发索引更新
20288 //todo 可否考虑标记检测,如果不涉及表格的变化就不进行索引重建和更新
20289 me.addListener("contentchange", function () {
20290 var me = this;
20291 //尽可能排除一些不需要更新的状况
20292 hideDragLine(me);
20293 if (getUETableBySelected(me))return;
20294 var rng = me.selection.getRange();
20295 var start = rng.startContainer;
20296 start = domUtils.findParentByTagName(start, ['td', 'th'], true);
20297 utils.each(domUtils.getElementsByTagName(me.document, 'table'), function (table) {
20298 if (me.fireEvent("excludetable", table) === true) return;
20299 table.ueTable = new UT(table);
20300 //trace:3742
20301// utils.each(domUtils.getElementsByTagName(me.document, 'td'), function (td) {
20302//
20303// if (domUtils.isEmptyBlock(td) && td !== start) {
20304// domUtils.fillNode(me.document, td);
20305// if (browser.ie && browser.version == 6) {
20306// td.innerHTML = '&nbsp;'
20307// }
20308// }
20309// });
20310// utils.each(domUtils.getElementsByTagName(me.document, 'th'), function (th) {
20311// if (domUtils.isEmptyBlock(th) && th !== start) {
20312// domUtils.fillNode(me.document, th);
20313// if (browser.ie && browser.version == 6) {
20314// th.innerHTML = '&nbsp;'
20315// }
20316// }
20317// });
20318 table.onmouseover = function () {
20319 me.fireEvent('tablemouseover', table);
20320 };
20321 table.onmousemove = function () {
20322 me.fireEvent('tablemousemove', table);
20323 me.options.tableDragable && toggleDragButton(true, this, me);
20324 utils.defer(function(){
20325 me.fireEvent('contentchange',50)
20326 },true)
20327 };
20328 table.onmouseout = function () {
20329 me.fireEvent('tablemouseout', table);
20330 toggleDraggableState(me, false, "", null);
20331 hideDragLine(me);
20332 };
20333 table.onclick = function (evt) {
20334 evt = me.window.event || evt;
20335 var target = getParentTdOrTh(evt.target || evt.srcElement);
20336 if (!target)return;
20337 var ut = getUETable(target),
20338 table = ut.table,
20339 cellInfo = ut.getCellInfo(target),
20340 cellsRange,
20341 rng = me.selection.getRange();
20342// if ("topLeft" == inPosition(table, mouseCoords(evt))) {
20343// cellsRange = ut.getCellsRange(ut.table.rows[0].cells[0], ut.getLastCell());
20344// ut.setSelected(cellsRange);
20345// return;
20346// }
20347// if ("bottomRight" == inPosition(table, mouseCoords(evt))) {
20348//
20349// return;
20350// }
20351 if (inTableSide(table, target, evt, true)) {
20352 var endTdCol = ut.getCell(ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].rowIndex, ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].cellIndex);
20353 if (evt.shiftKey && ut.selectedTds.length) {
20354 if (ut.selectedTds[0] !== endTdCol) {
20355 cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdCol);
20356 ut.setSelected(cellsRange);
20357 } else {
20358 rng && rng.selectNodeContents(endTdCol).select();
20359 }
20360 } else {
20361 if (target !== endTdCol) {
20362 cellsRange = ut.getCellsRange(target, endTdCol);
20363 ut.setSelected(cellsRange);
20364 } else {
20365 rng && rng.selectNodeContents(endTdCol).select();
20366 }
20367 }
20368 return;
20369 }
20370 if (inTableSide(table, target, evt)) {
20371 var endTdRow = ut.getCell(ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].rowIndex, ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].cellIndex);
20372 if (evt.shiftKey && ut.selectedTds.length) {
20373 if (ut.selectedTds[0] !== endTdRow) {
20374 cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdRow);
20375 ut.setSelected(cellsRange);
20376 } else {
20377 rng && rng.selectNodeContents(endTdRow).select();
20378 }
20379 } else {
20380 if (target !== endTdRow) {
20381 cellsRange = ut.getCellsRange(target, endTdRow);
20382 ut.setSelected(cellsRange);
20383 } else {
20384 rng && rng.selectNodeContents(endTdRow).select();
20385 }
20386 }
20387 }
20388 };
20389 });
20390
20391 switchBorderColor(me, true);
20392 });
20393
20394 domUtils.on(me.document, "mousemove", mouseMoveEvent);
20395
20396 domUtils.on(me.document, "mouseout", function (evt) {
20397 var target = evt.target || evt.srcElement;
20398 if (target.tagName == "TABLE") {
20399 toggleDraggableState(me, false, "", null);
20400 }
20401 });
20402 /**
20403 * 表格隔行变色
20404 */
20405 me.addListener("interlacetable",function(type,table,classList){
20406 if(!table) return;
20407 var me = this,
20408 rows = table.rows,
20409 len = rows.length,
20410 getClass = function(list,index,repeat){
20411 return list[index] ? list[index] : repeat ? list[index % list.length]: "";
20412 };
20413 for(var i = 0;i<len;i++){
20414 rows[i].className = getClass( classList|| me.options.classList,i,true);
20415 }
20416 });
20417 me.addListener("uninterlacetable",function(type,table){
20418 if(!table) return;
20419 var me = this,
20420 rows = table.rows,
20421 classList = me.options.classList,
20422 len = rows.length;
20423 for(var i = 0;i<len;i++){
20424 domUtils.removeClasses( rows[i], classList );
20425 }
20426 });
20427
20428 me.addListener("mousedown", mouseDownEvent);
20429 me.addListener("mouseup", mouseUpEvent);
20430 //拖动的时候触发mouseup
20431 domUtils.on( me.body, 'dragstart', function( evt ){
20432 mouseUpEvent.call( me, 'dragstart', evt );
20433 });
20434 me.addOutputRule(function(root){
20435 utils.each(root.getNodesByTagName('div'),function(n){
20436 if (n.getAttr('id') == 'ue_tableDragLine') {
20437 n.parentNode.removeChild(n);
20438 }
20439 });
20440 });
20441
20442 var currentRowIndex = 0;
20443 me.addListener("mousedown", function () {
20444 currentRowIndex = 0;
20445 });
20446 me.addListener('tabkeydown', function () {
20447 var range = this.selection.getRange(),
20448 common = range.getCommonAncestor(true, true),
20449 table = domUtils.findParentByTagName(common, 'table');
20450 if (table) {
20451 if (domUtils.findParentByTagName(common, 'caption', true)) {
20452 var cell = domUtils.getElementsByTagName(table, 'th td');
20453 if (cell && cell.length) {
20454 range.setStart(cell[0], 0).setCursor(false, true)
20455 }
20456 } else {
20457 var cell = domUtils.findParentByTagName(common, ['td', 'th'], true),
20458 ua = getUETable(cell);
20459 currentRowIndex = cell.rowSpan > 1 ? currentRowIndex : ua.getCellInfo(cell).rowIndex;
20460 var nextCell = ua.getTabNextCell(cell, currentRowIndex);
20461 if (nextCell) {
20462 if (isEmptyBlock(nextCell)) {
20463 range.setStart(nextCell, 0).setCursor(false, true)
20464 } else {
20465 range.selectNodeContents(nextCell).select()
20466 }
20467 } else {
20468 me.fireEvent('saveScene');
20469 me.__hasEnterExecCommand = true;
20470 this.execCommand('insertrownext');
20471 me.__hasEnterExecCommand = false;
20472 range = this.selection.getRange();
20473 range.setStart(table.rows[table.rows.length - 1].cells[0], 0).setCursor();
20474 me.fireEvent('saveScene');
20475 }
20476 }
20477 return true;
20478 }
20479
20480 });
20481 browser.ie && me.addListener('selectionchange', function () {
20482 toggleDraggableState(this, false, "", null);
20483 });
20484 me.addListener("keydown", function (type, evt) {
20485 var me = this;
20486 //处理在表格的最后一个输入tab产生新的表格
20487 var keyCode = evt.keyCode || evt.which;
20488 if (keyCode == 8 || keyCode == 46) {
20489 return;
20490 }
20491 var notCtrlKey = !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey;
20492 notCtrlKey && removeSelectedClass(domUtils.getElementsByTagName(me.body, "td"));
20493 var ut = getUETableBySelected(me);
20494 if (!ut) return;
20495 notCtrlKey && ut.clearSelected();
20496 });
20497
20498 me.addListener("beforegetcontent", function () {
20499 switchBorderColor(this, false);
20500 browser.ie && utils.each(this.document.getElementsByTagName('caption'), function (ci) {
20501 if (domUtils.isEmptyNode(ci)) {
20502 ci.innerHTML = '&nbsp;'
20503 }
20504 });
20505 });
20506 me.addListener("aftergetcontent", function () {
20507 switchBorderColor(this, true);
20508 });
20509 me.addListener("getAllHtml", function () {
20510 removeSelectedClass(me.document.getElementsByTagName("td"));
20511 });
20512 //修正全屏状态下插入的表格宽度在非全屏状态下撑开编辑器的情况
20513 me.addListener("fullscreenchanged", function (type, fullscreen) {
20514 if (!fullscreen) {
20515 var ratio = this.body.offsetWidth / document.body.offsetWidth,
20516 tables = domUtils.getElementsByTagName(this.body, "table");
20517 utils.each(tables, function (table) {
20518 if (table.offsetWidth < me.body.offsetWidth) return false;
20519 var tds = domUtils.getElementsByTagName(table, "td"),
20520 backWidths = [];
20521 utils.each(tds, function (td) {
20522 backWidths.push(td.offsetWidth);
20523 });
20524 for (var i = 0, td; td = tds[i]; i++) {
20525 td.setAttribute("width", Math.floor(backWidths[i] * ratio));
20526 }
20527 table.setAttribute("width", Math.floor(getTableWidth(me, needIEHack, getDefaultValue(me))))
20528 });
20529 }
20530 });
20531
20532 //重写execCommand命令,用于处理框选时的处理
20533 var oldExecCommand = me.execCommand;
20534 me.execCommand = function (cmd, datatat) {
20535
20536 var me = this,
20537 args = arguments;
20538
20539 cmd = cmd.toLowerCase();
20540 var ut = getUETableBySelected(me), tds,
20541 range = new dom.Range(me.document),
20542 cmdFun = me.commands[cmd] || UE.commands[cmd],
20543 result;
20544 if (!cmdFun) return;
20545 if (ut && !commands[cmd] && !cmdFun.notNeedUndo && !me.__hasEnterExecCommand) {
20546 me.__hasEnterExecCommand = true;
20547 me.fireEvent("beforeexeccommand", cmd);
20548 tds = ut.selectedTds;
20549 var lastState = -2, lastValue = -2, value, state;
20550 for (var i = 0, td; td = tds[i]; i++) {
20551 if (isEmptyBlock(td)) {
20552 range.setStart(td, 0).setCursor(false, true)
20553 } else {
20554 range.selectNode(td).select(true);
20555 }
20556 state = me.queryCommandState(cmd);
20557 value = me.queryCommandValue(cmd);
20558 if (state != -1) {
20559 if (lastState !== state || lastValue !== value) {
20560 me._ignoreContentChange = true;
20561 result = oldExecCommand.apply(me, arguments);
20562 me._ignoreContentChange = false;
20563
20564 }
20565 lastState = me.queryCommandState(cmd);
20566 lastValue = me.queryCommandValue(cmd);
20567 if (domUtils.isEmptyBlock(td)) {
20568 domUtils.fillNode(me.document, td)
20569 }
20570 }
20571 }
20572 range.setStart(tds[0], 0).shrinkBoundary(true).setCursor(false, true);
20573 me.fireEvent('contentchange');
20574 me.fireEvent("afterexeccommand", cmd);
20575 me.__hasEnterExecCommand = false;
20576 me._selectionChange();
20577 } else {
20578 result = oldExecCommand.apply(me, arguments);
20579 }
20580 return result;
20581 };
20582
20583
20584 });
20585 /**
20586 * 删除obj的宽高style,改成属性宽高
20587 * @param obj
20588 * @param replaceToProperty
20589 */
20590 function removeStyleSize(obj, replaceToProperty) {
20591 removeStyle(obj, "width", true);
20592 removeStyle(obj, "height", true);
20593 }
20594
20595 function removeStyle(obj, styleName, replaceToProperty) {
20596 if (obj.style[styleName]) {
20597 replaceToProperty && obj.setAttribute(styleName, parseInt(obj.style[styleName], 10));
20598 obj.style[styleName] = "";
20599 }
20600 }
20601
20602 function getParentTdOrTh(ele) {
20603 if (ele.tagName == "TD" || ele.tagName == "TH") return ele;
20604 var td;
20605 if (td = domUtils.findParentByTagName(ele, "td", true) || domUtils.findParentByTagName(ele, "th", true)) return td;
20606 return null;
20607 }
20608
20609 function isEmptyBlock(node) {
20610 var reg = new RegExp(domUtils.fillChar, 'g');
20611 if (node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '').length > 0) {
20612 return 0;
20613 }
20614 for (var n in dtd.$isNotEmpty) {
20615 if (node.getElementsByTagName(n).length) {
20616 return 0;
20617 }
20618 }
20619 return 1;
20620 }
20621
20622
20623 function mouseCoords(evt) {
20624 if (evt.pageX || evt.pageY) {
20625 return { x:evt.pageX, y:evt.pageY };
20626 }
20627 return {
20628 x:evt.clientX + me.document.body.scrollLeft - me.document.body.clientLeft,
20629 y:evt.clientY + me.document.body.scrollTop - me.document.body.clientTop
20630 };
20631 }
20632
20633 function mouseMoveEvent(evt) {
20634
20635 if( isEditorDisabled() ) {
20636 return;
20637 }
20638
20639 try {
20640
20641 //普通状态下鼠标移动
20642 var target = getParentTdOrTh(evt.target || evt.srcElement),
20643 pos;
20644
20645 //区分用户的行为是拖动还是双击
20646 if( isInResizeBuffer ) {
20647
20648 me.body.style.webkitUserSelect = 'none';
20649
20650 if( Math.abs( userActionStatus.x - evt.clientX ) > offsetOfTableCell || Math.abs( userActionStatus.y - evt.clientY ) > offsetOfTableCell ) {
20651 clearTableDragTimer();
20652 isInResizeBuffer = false;
20653 singleClickState = 0;
20654 //drag action
20655 tableBorderDrag(evt);
20656 }
20657 }
20658
20659 //修改单元格大小时的鼠标移动
20660 if (onDrag && dragTd) {
20661 singleClickState = 0;
20662 me.body.style.webkitUserSelect = 'none';
20663 me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
20664 pos = mouseCoords(evt);
20665 toggleDraggableState(me, true, onDrag, pos, target);
20666 if (onDrag == "h") {
20667 dragLine.style.left = getPermissionX(dragTd, evt) + "px";
20668 } else if (onDrag == "v") {
20669 dragLine.style.top = getPermissionY(dragTd, evt) + "px";
20670 }
20671 return;
20672 }
20673 //当鼠标处于table上时,修改移动过程中的光标状态
20674 if (target) {
20675 //针对使用table作为容器的组件不触发拖拽效果
20676 if (me.fireEvent('excludetable', target) === true)
20677 return;
20678 pos = mouseCoords(evt);
20679 var state = getRelation(target, pos),
20680 table = domUtils.findParentByTagName(target, "table", true);
20681
20682 if (inTableSide(table, target, evt, true)) {
20683 if (me.fireEvent("excludetable", table) === true) return;
20684 me.body.style.cursor = "url(" + me.options.cursorpath + "h.png),pointer";
20685 } else if (inTableSide(table, target, evt)) {
20686 if (me.fireEvent("excludetable", table) === true) return;
20687 me.body.style.cursor = "url(" + me.options.cursorpath + "v.png),pointer";
20688 } else {
20689 me.body.style.cursor = "text";
20690 var curCell = target;
20691 if (/\d/.test(state)) {
20692 state = state.replace(/\d/, '');
20693 target = getUETable(target).getPreviewCell(target, state == "v");
20694 }
20695 //位于第一行的顶部或者第一列的左边时不可拖动
20696 toggleDraggableState(me, target ? !!state : false, target ? state : '', pos, target);
20697
20698 }
20699 } else {
20700 toggleDragButton(false, table, me);
20701 }
20702
20703 } catch (e) {
20704 showError(e);
20705 }
20706 }
20707
20708 var dragButtonTimer;
20709
20710 function toggleDragButton(show, table, editor) {
20711 if (!show) {
20712 if (dragOver)return;
20713 dragButtonTimer = setTimeout(function () {
20714 !dragOver && dragButton && dragButton.parentNode && dragButton.parentNode.removeChild(dragButton);
20715 }, 2000);
20716 } else {
20717 createDragButton(table, editor);
20718 }
20719 }
20720
20721 function createDragButton(table, editor) {
20722 var pos = domUtils.getXY(table),
20723 doc = table.ownerDocument;
20724 if (dragButton && dragButton.parentNode)return dragButton;
20725 dragButton = doc.createElement("div");
20726 dragButton.contentEditable = false;
20727 dragButton.innerHTML = "";
20728 dragButton.style.cssText = "width:15px;height:15px;background-image:url(" + editor.options.UEDITOR_HOME_URL + "dialogs/table/dragicon.png);position: absolute;cursor:move;top:" + (pos.y - 15) + "px;left:" + (pos.x) + "px;";
20729 domUtils.unSelectable(dragButton);
20730 dragButton.onmouseover = function (evt) {
20731 dragOver = true;
20732 };
20733 dragButton.onmouseout = function (evt) {
20734 dragOver = false;
20735 };
20736 domUtils.on(dragButton, 'click', function (type, evt) {
20737 doClick(evt, this);
20738 });
20739 domUtils.on(dragButton, 'dblclick', function (type, evt) {
20740 doDblClick(evt);
20741 });
20742 domUtils.on(dragButton, 'dragstart', function (type, evt) {
20743 domUtils.preventDefault(evt);
20744 });
20745 var timer;
20746
20747 function doClick(evt, button) {
20748 // 部分浏览器下需要清理
20749 clearTimeout(timer);
20750 timer = setTimeout(function () {
20751 editor.fireEvent("tableClicked", table, button);
20752 }, 300);
20753 }
20754
20755 function doDblClick(evt) {
20756 clearTimeout(timer);
20757 var ut = getUETable(table),
20758 start = table.rows[0].cells[0],
20759 end = ut.getLastCell(),
20760 range = ut.getCellsRange(start, end);
20761 editor.selection.getRange().setStart(start, 0).setCursor(false, true);
20762 ut.setSelected(range);
20763 }
20764
20765 doc.body.appendChild(dragButton);
20766 }
20767
20768
20769// function inPosition(table, pos) {
20770// var tablePos = domUtils.getXY(table),
20771// width = table.offsetWidth,
20772// height = table.offsetHeight;
20773// if (pos.x - tablePos.x < 5 && pos.y - tablePos.y < 5) {
20774// return "topLeft";
20775// } else if (tablePos.x + width - pos.x < 5 && tablePos.y + height - pos.y < 5) {
20776// return "bottomRight";
20777// }
20778// }
20779
20780 function inTableSide(table, cell, evt, top) {
20781 var pos = mouseCoords(evt),
20782 state = getRelation(cell, pos);
20783
20784 if (top) {
20785 var caption = table.getElementsByTagName("caption")[0],
20786 capHeight = caption ? caption.offsetHeight : 0;
20787 return (state == "v1") && ((pos.y - domUtils.getXY(table).y - capHeight) < 8);
20788 } else {
20789 return (state == "h1") && ((pos.x - domUtils.getXY(table).x) < 8);
20790 }
20791 }
20792
20793 /**
20794 * 获取拖动时允许的X轴坐标
20795 * @param dragTd
20796 * @param evt
20797 */
20798 function getPermissionX(dragTd, evt) {
20799 var ut = getUETable(dragTd);
20800 if (ut) {
20801 var preTd = ut.getSameEndPosCells(dragTd, "x")[0],
20802 nextTd = ut.getSameStartPosXCells(dragTd)[0],
20803 mouseX = mouseCoords(evt).x,
20804 left = (preTd ? domUtils.getXY(preTd).x : domUtils.getXY(ut.table).x) + 20 ,
20805 right = nextTd ? domUtils.getXY(nextTd).x + nextTd.offsetWidth - 20 : (me.body.offsetWidth + 5 || parseInt(domUtils.getComputedStyle(me.body, "width"), 10));
20806
20807 left += cellMinWidth;
20808 right -= cellMinWidth;
20809
20810 return mouseX < left ? left : mouseX > right ? right : mouseX;
20811 }
20812 }
20813
20814 /**
20815 * 获取拖动时允许的Y轴坐标
20816 */
20817 function getPermissionY(dragTd, evt) {
20818 try {
20819 var top = domUtils.getXY(dragTd).y,
20820 mousePosY = mouseCoords(evt).y;
20821 return mousePosY < top ? top : mousePosY;
20822 } catch (e) {
20823 showError(e);
20824 }
20825 }
20826
20827 /**
20828 * 移动状态切换
20829 */
20830 function toggleDraggableState(editor, draggable, dir, mousePos, cell) {
20831 try {
20832 editor.body.style.cursor = dir == "h" ? "col-resize" : dir == "v" ? "row-resize" : "text";
20833 if (browser.ie) {
20834 if (dir && !mousedown && !getUETableBySelected(editor)) {
20835 getDragLine(editor, editor.document);
20836 showDragLineAt(dir, cell);
20837 } else {
20838 hideDragLine(editor)
20839 }
20840 }
20841 onBorder = draggable;
20842 } catch (e) {
20843 showError(e);
20844 }
20845 }
20846
20847 /**
20848 * 获取与UETable相关的resize line
20849 * @param uetable UETable对象
20850 */
20851 function getResizeLineByUETable() {
20852
20853 var lineId = '_UETableResizeLine',
20854 line = this.document.getElementById( lineId );
20855
20856 if( !line ) {
20857 line = this.document.createElement("div");
20858 line.id = lineId;
20859 line.contnetEditable = false;
20860 line.setAttribute("unselectable", "on");
20861
20862 var styles = {
20863 width: 2*cellBorderWidth + 1 + 'px',
20864 position: 'absolute',
20865 'z-index': 100000,
20866 cursor: 'col-resize',
20867 background: 'red',
20868 display: 'none'
20869 };
20870
20871 //切换状态
20872 line.onmouseout = function(){
20873 this.style.display = 'none';
20874 };
20875
20876 utils.extend( line.style, styles );
20877
20878 this.document.body.appendChild( line );
20879
20880 }
20881
20882 return line;
20883
20884 }
20885
20886 /**
20887 * 更新resize-line
20888 */
20889 function updateResizeLine( cell, uetable ) {
20890
20891 var line = getResizeLineByUETable.call( this ),
20892 table = uetable.table,
20893 styles = {
20894 top: domUtils.getXY( table ).y + 'px',
20895 left: domUtils.getXY( cell).x + cell.offsetWidth - cellBorderWidth + 'px',
20896 display: 'block',
20897 height: table.offsetHeight + 'px'
20898 };
20899
20900 utils.extend( line.style, styles );
20901
20902 }
20903
20904 /**
20905 * 显示resize-line
20906 */
20907 function showResizeLine( cell ) {
20908
20909 var uetable = getUETable( cell );
20910
20911 updateResizeLine.call( this, cell, uetable );
20912
20913 }
20914
20915 /**
20916 * 获取鼠标与当前单元格的相对位置
20917 * @param ele
20918 * @param mousePos
20919 */
20920 function getRelation(ele, mousePos) {
20921 var elePos = domUtils.getXY(ele);
20922
20923 if( !elePos ) {
20924 return '';
20925 }
20926
20927 if (elePos.x + ele.offsetWidth - mousePos.x < cellBorderWidth) {
20928 return "h";
20929 }
20930 if (mousePos.x - elePos.x < cellBorderWidth) {
20931 return 'h1'
20932 }
20933 if (elePos.y + ele.offsetHeight - mousePos.y < cellBorderWidth) {
20934 return "v";
20935 }
20936 if (mousePos.y - elePos.y < cellBorderWidth) {
20937 return 'v1'
20938 }
20939 return '';
20940 }
20941
20942 function mouseDownEvent(type, evt) {
20943
20944 if( isEditorDisabled() ) {
20945 return ;
20946 }
20947
20948 userActionStatus = {
20949 x: evt.clientX,
20950 y: evt.clientY
20951 };
20952
20953 //右键菜单单独处理
20954 if (evt.button == 2) {
20955 var ut = getUETableBySelected(me),
20956 flag = false;
20957
20958 if (ut) {
20959 var td = getTargetTd(me, evt);
20960 utils.each(ut.selectedTds, function (ti) {
20961 if (ti === td) {
20962 flag = true;
20963 }
20964 });
20965 if (!flag) {
20966 removeSelectedClass(domUtils.getElementsByTagName(me.body, "th td"));
20967 ut.clearSelected()
20968 } else {
20969 td = ut.selectedTds[0];
20970 setTimeout(function () {
20971 me.selection.getRange().setStart(td, 0).setCursor(false, true);
20972 }, 0);
20973
20974 }
20975 }
20976 } else {
20977 tableClickHander( evt );
20978 }
20979
20980 }
20981
20982 //清除表格的计时器
20983 function clearTableTimer() {
20984 tabTimer && clearTimeout( tabTimer );
20985 tabTimer = null;
20986 }
20987
20988 //双击收缩
20989 function tableDbclickHandler(evt) {
20990 singleClickState = 0;
20991 evt = evt || me.window.event;
20992 var target = getParentTdOrTh(evt.target || evt.srcElement);
20993 if (target) {
20994 var h;
20995 if (h = getRelation(target, mouseCoords(evt))) {
20996
20997 hideDragLine( me );
20998
20999 if (h == 'h1') {
21000 h = 'h';
21001 if (inTableSide(domUtils.findParentByTagName(target, "table"), target, evt)) {
21002 me.execCommand('adaptbywindow');
21003 } else {
21004 target = getUETable(target).getPreviewCell(target);
21005 if (target) {
21006 var rng = me.selection.getRange();
21007 rng.selectNodeContents(target).setCursor(true, true)
21008 }
21009 }
21010 }
21011 if (h == 'h') {
21012 var ut = getUETable(target),
21013 table = ut.table,
21014 cells = getCellsByMoveBorder( target, table, true );
21015
21016 cells = extractArray( cells, 'left' );
21017
21018 ut.width = ut.offsetWidth;
21019
21020 var oldWidth = [],
21021 newWidth = [];
21022
21023 utils.each( cells, function( cell ){
21024
21025 oldWidth.push( cell.offsetWidth );
21026
21027 } );
21028
21029 utils.each( cells, function( cell ){
21030
21031 cell.removeAttribute("width");
21032
21033 } );
21034
21035 window.setTimeout( function(){
21036
21037 //是否允许改变
21038 var changeable = true;
21039
21040 utils.each( cells, function( cell, index ){
21041
21042 var width = cell.offsetWidth;
21043
21044 if( width > oldWidth[index] ) {
21045 changeable = false;
21046 return false;
21047 }
21048
21049 newWidth.push( width );
21050
21051 } );
21052
21053 var change = changeable ? newWidth : oldWidth;
21054
21055 utils.each( cells, function( cell, index ){
21056
21057 cell.width = change[index] - getTabcellSpace();
21058
21059 } );
21060
21061
21062 }, 0 );
21063
21064// minWidth -= cellMinWidth;
21065//
21066// table.removeAttribute("width");
21067// utils.each(cells, function (cell) {
21068// cell.style.width = "";
21069// cell.width -= minWidth;
21070// });
21071
21072 }
21073 }
21074 }
21075 }
21076
21077 function tableClickHander( evt ) {
21078
21079 removeSelectedClass(domUtils.getElementsByTagName(me.body, "td th"));
21080 //trace:3113
21081 //选中单元格,点击table外部,不会清掉table上挂的ueTable,会引起getUETableBySelected方法返回值
21082 utils.each(me.document.getElementsByTagName('table'), function (t) {
21083 t.ueTable = null;
21084 });
21085 startTd = getTargetTd(me, evt);
21086 if( !startTd ) return;
21087 var table = domUtils.findParentByTagName(startTd, "table", true);
21088 ut = getUETable(table);
21089 ut && ut.clearSelected();
21090
21091 //判断当前鼠标状态
21092 if (!onBorder) {
21093 me.document.body.style.webkitUserSelect = '';
21094 mousedown = true;
21095 me.addListener('mouseover', mouseOverEvent);
21096 } else {
21097 //边框上的动作处理
21098 borderActionHandler( evt );
21099 }
21100
21101
21102 }
21103
21104 //处理表格边框上的动作, 这里做延时处理,避免两种动作互相影响
21105 function borderActionHandler( evt ) {
21106
21107 if ( browser.ie ) {
21108 evt = reconstruct(evt );
21109 }
21110
21111 clearTableDragTimer();
21112
21113 //是否正在等待resize的缓冲中
21114 isInResizeBuffer = true;
21115
21116 tableDragTimer = setTimeout(function(){
21117 tableBorderDrag( evt );
21118 }, dblclickTime);
21119
21120 }
21121
21122 function extractArray( originArr, key ) {
21123
21124 var result = [],
21125 tmp = null;
21126
21127 for( var i = 0, len = originArr.length; i<len; i++ ) {
21128
21129 tmp = originArr[ i ][ key ];
21130
21131 if( tmp ) {
21132 result.push( tmp );
21133 }
21134
21135 }
21136
21137 return result;
21138
21139 }
21140
21141 function clearTableDragTimer() {
21142 tableDragTimer && clearTimeout(tableDragTimer);
21143 tableDragTimer = null;
21144 }
21145
21146 function reconstruct( obj ) {
21147
21148 var attrs = ['pageX', 'pageY', 'clientX', 'clientY', 'srcElement', 'target'],
21149 newObj = {};
21150
21151 if( obj ) {
21152
21153 for( var i = 0, key, val; key = attrs[i]; i++ ) {
21154 val=obj[ key ];
21155 val && (newObj[ key ] = val);
21156 }
21157
21158 }
21159
21160 return newObj;
21161
21162 }
21163
21164 //边框拖动
21165 function tableBorderDrag( evt ) {
21166
21167 isInResizeBuffer = false;
21168
21169 startTd = evt.target || evt.srcElement;
21170 if( !startTd ) return;
21171 var state = getRelation(startTd, mouseCoords(evt));
21172 if (/\d/.test(state)) {
21173 state = state.replace(/\d/, '');
21174 startTd = getUETable(startTd).getPreviewCell(startTd, state == 'v');
21175 }
21176 hideDragLine(me);
21177 getDragLine(me, me.document);
21178 me.fireEvent('saveScene');
21179 showDragLineAt(state, startTd);
21180 mousedown = true;
21181 //拖动开始
21182 onDrag = state;
21183 dragTd = startTd;
21184 }
21185
21186 function mouseUpEvent(type, evt) {
21187
21188 if( isEditorDisabled() ) {
21189 return ;
21190 }
21191
21192 clearTableDragTimer();
21193
21194 isInResizeBuffer = false;
21195
21196 if( onBorder ) {
21197 singleClickState = ++singleClickState % 3;
21198
21199 userActionStatus = {
21200 x: evt.clientX,
21201 y: evt.clientY
21202 };
21203
21204 tableResizeTimer = setTimeout(function(){
21205 singleClickState > 0 && singleClickState--;
21206 }, dblclickTime );
21207
21208 if( singleClickState === 2 ) {
21209
21210 singleClickState = 0;
21211 tableDbclickHandler(evt);
21212 return;
21213
21214 }
21215
21216 }
21217
21218 if (evt.button == 2)return;
21219 var me = this;
21220 //清除表格上原生跨选问题
21221 var range = me.selection.getRange(),
21222 start = domUtils.findParentByTagName(range.startContainer, 'table', true),
21223 end = domUtils.findParentByTagName(range.endContainer, 'table', true);
21224
21225 if (start || end) {
21226 if (start === end) {
21227 start = domUtils.findParentByTagName(range.startContainer, ['td', 'th', 'caption'], true);
21228 end = domUtils.findParentByTagName(range.endContainer, ['td', 'th', 'caption'], true);
21229 if (start !== end) {
21230 me.selection.clearRange()
21231 }
21232 } else {
21233 me.selection.clearRange()
21234 }
21235 }
21236 mousedown = false;
21237 me.document.body.style.webkitUserSelect = '';
21238 //拖拽状态下的mouseUP
21239 if ( onDrag && dragTd ) {
21240
21241 me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
21242
21243 singleClickState = 0;
21244 dragLine = me.document.getElementById('ue_tableDragLine');
21245
21246 // trace 3973
21247 if (dragLine) {
21248 var dragTdPos = domUtils.getXY(dragTd),
21249 dragLinePos = domUtils.getXY(dragLine);
21250
21251 switch (onDrag) {
21252 case "h":
21253 changeColWidth(dragTd, dragLinePos.x - dragTdPos.x);
21254 break;
21255 case "v":
21256 changeRowHeight(dragTd, dragLinePos.y - dragTdPos.y - dragTd.offsetHeight);
21257 break;
21258 default:
21259 }
21260 onDrag = "";
21261 dragTd = null;
21262
21263 hideDragLine(me);
21264 me.fireEvent('saveScene');
21265 return;
21266 }
21267 }
21268 //正常状态下的mouseup
21269 if (!startTd) {
21270 var target = domUtils.findParentByTagName(evt.target || evt.srcElement, "td", true);
21271 if (!target) target = domUtils.findParentByTagName(evt.target || evt.srcElement, "th", true);
21272 if (target && (target.tagName == "TD" || target.tagName == "TH")) {
21273 if (me.fireEvent("excludetable", target) === true) return;
21274 range = new dom.Range(me.document);
21275 range.setStart(target, 0).setCursor(false, true);
21276 }
21277 } else {
21278 var ut = getUETable(startTd),
21279 cell = ut ? ut.selectedTds[0] : null;
21280 if (cell) {
21281 range = new dom.Range(me.document);
21282 if (domUtils.isEmptyBlock(cell)) {
21283 range.setStart(cell, 0).setCursor(false, true);
21284 } else {
21285 range.selectNodeContents(cell).shrinkBoundary().setCursor(false, true);
21286 }
21287 } else {
21288 range = me.selection.getRange().shrinkBoundary();
21289 if (!range.collapsed) {
21290 var start = domUtils.findParentByTagName(range.startContainer, ['td', 'th'], true),
21291 end = domUtils.findParentByTagName(range.endContainer, ['td', 'th'], true);
21292 //在table里边的不能清除
21293 if (start && !end || !start && end || start && end && start !== end) {
21294 range.setCursor(false, true);
21295 }
21296 }
21297 }
21298 startTd = null;
21299 me.removeListener('mouseover', mouseOverEvent);
21300 }
21301 me._selectionChange(250, evt);
21302 }
21303
21304 function mouseOverEvent(type, evt) {
21305
21306 if( isEditorDisabled() ) {
21307 return;
21308 }
21309
21310 var me = this,
21311 tar = evt.target || evt.srcElement;
21312 currentTd = domUtils.findParentByTagName(tar, "td", true) || domUtils.findParentByTagName(tar, "th", true);
21313 //需要判断两个TD是否位于同一个表格内
21314 if (startTd && currentTd &&
21315 ((startTd.tagName == "TD" && currentTd.tagName == "TD") || (startTd.tagName == "TH" && currentTd.tagName == "TH")) &&
21316 domUtils.findParentByTagName(startTd, 'table') == domUtils.findParentByTagName(currentTd, 'table')) {
21317 var ut = getUETable(currentTd);
21318 if (startTd != currentTd) {
21319 me.document.body.style.webkitUserSelect = 'none';
21320 me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
21321 var range = ut.getCellsRange(startTd, currentTd);
21322 ut.setSelected(range);
21323 } else {
21324 me.document.body.style.webkitUserSelect = '';
21325 ut.clearSelected();
21326 }
21327
21328 }
21329 evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
21330 }
21331
21332 function setCellHeight(cell, height, backHeight) {
21333 var lineHight = parseInt(domUtils.getComputedStyle(cell, "line-height"), 10),
21334 tmpHeight = backHeight + height;
21335 height = tmpHeight < lineHight ? lineHight : tmpHeight;
21336 if (cell.style.height) cell.style.height = "";
21337 cell.rowSpan == 1 ? cell.setAttribute("height", height) : (cell.removeAttribute && cell.removeAttribute("height"));
21338 }
21339
21340 function getWidth(cell) {
21341 if (!cell)return 0;
21342 return parseInt(domUtils.getComputedStyle(cell, "width"), 10);
21343 }
21344
21345 function changeColWidth(cell, changeValue) {
21346
21347 var ut = getUETable(cell);
21348 if (ut) {
21349
21350 //根据当前移动的边框获取相关的单元格
21351 var table = ut.table,
21352 cells = getCellsByMoveBorder( cell, table );
21353
21354 table.style.width = "";
21355 table.removeAttribute("width");
21356
21357 //修正改变量
21358 changeValue = correctChangeValue( changeValue, cell, cells );
21359
21360 if (cell.nextSibling) {
21361
21362 var i=0;
21363
21364 utils.each( cells, function( cellGroup ){
21365
21366 cellGroup.left.width = (+cellGroup.left.width)+changeValue;
21367 cellGroup.right && ( cellGroup.right.width = (+cellGroup.right.width)-changeValue );
21368
21369 } );
21370
21371 } else {
21372
21373 utils.each( cells, function( cellGroup ){
21374 cellGroup.left.width -= -changeValue;
21375 } );
21376
21377 }
21378 }
21379
21380 }
21381
21382 function isEditorDisabled() {
21383 return me.body.contentEditable === "false";
21384 }
21385
21386 function changeRowHeight(td, changeValue) {
21387 if (Math.abs(changeValue) < 10) return;
21388 var ut = getUETable(td);
21389 if (ut) {
21390 var cells = ut.getSameEndPosCells(td, "y"),
21391 //备份需要连带变化的td的原始高度,否则后期无法获取正确的值
21392 backHeight = cells[0] ? cells[0].offsetHeight : 0;
21393 for (var i = 0, cell; cell = cells[i++];) {
21394 setCellHeight(cell, changeValue, backHeight);
21395 }
21396 }
21397
21398 }
21399
21400 /**
21401 * 获取调整单元格大小的相关单元格
21402 * @isContainMergeCell 返回的结果中是否包含发生合并后的单元格
21403 */
21404 function getCellsByMoveBorder( cell, table, isContainMergeCell ) {
21405
21406 if( !table ) {
21407 table = domUtils.findParentByTagName( cell, 'table' );
21408 }
21409
21410 if( !table ) {
21411 return null;
21412 }
21413
21414 //获取到该单元格所在行的序列号
21415 var index = domUtils.getNodeIndex( cell ),
21416 temp = cell,
21417 rows = table.rows,
21418 colIndex = 0;
21419
21420 while( temp ) {
21421 //获取到当前单元格在未发生单元格合并时的序列
21422 if( temp.nodeType === 1 ) {
21423 colIndex += (temp.colSpan || 1);
21424 }
21425 temp = temp.previousSibling;
21426 }
21427
21428 temp = null;
21429
21430 //记录想关的单元格
21431 var borderCells = [];
21432
21433 utils.each(rows, function( tabRow ){
21434
21435 var cells = tabRow.cells,
21436 currIndex = 0;
21437
21438 utils.each( cells, function( tabCell ){
21439
21440 currIndex += (tabCell.colSpan || 1);
21441
21442 if( currIndex === colIndex ) {
21443
21444 borderCells.push({
21445 left: tabCell,
21446 right: tabCell.nextSibling || null
21447 });
21448
21449 return false;
21450
21451 } else if( currIndex > colIndex ) {
21452
21453 if( isContainMergeCell ) {
21454 borderCells.push({
21455 left: tabCell
21456 });
21457 }
21458
21459 return false;
21460 }
21461
21462
21463 } );
21464
21465 });
21466
21467 return borderCells;
21468
21469 }
21470
21471
21472 /**
21473 * 通过给定的单元格集合获取最小的单元格width
21474 */
21475 function getMinWidthByTableCells( cells ) {
21476
21477 var minWidth = Number.MAX_VALUE;
21478
21479 for( var i = 0, curCell; curCell = cells[ i ] ; i++ ) {
21480
21481 minWidth = Math.min( minWidth, curCell.width || getTableCellWidth( curCell ) );
21482
21483 }
21484
21485 return minWidth;
21486
21487 }
21488
21489 function correctChangeValue( changeValue, relatedCell, cells ) {
21490
21491 //为单元格的paading预留空间
21492 changeValue -= getTabcellSpace();
21493
21494 if( changeValue < 0 ) {
21495 return 0;
21496 }
21497
21498 changeValue -= getTableCellWidth( relatedCell );
21499
21500 //确定方向
21501 var direction = changeValue < 0 ? 'left':'right';
21502
21503 changeValue = Math.abs(changeValue);
21504
21505 //只关心非最后一个单元格就可以
21506 utils.each( cells, function( cellGroup ){
21507
21508 var curCell = cellGroup[direction];
21509
21510 //为单元格保留最小空间
21511 if( curCell ) {
21512 changeValue = Math.min( changeValue, getTableCellWidth( curCell )-cellMinWidth );
21513 }
21514
21515
21516 } );
21517
21518
21519 //修正越界
21520 changeValue = changeValue < 0 ? 0 : changeValue;
21521
21522 return direction === 'left' ? -changeValue : changeValue;
21523
21524 }
21525
21526 function getTableCellWidth( cell ) {
21527
21528 var width = 0,
21529 //偏移纠正量
21530 offset = 0,
21531 width = cell.offsetWidth - getTabcellSpace();
21532
21533 //最后一个节点纠正一下
21534 if( !cell.nextSibling ) {
21535
21536 width -= getTableCellOffset( cell );
21537
21538 }
21539
21540 width = width < 0 ? 0 : width;
21541
21542 try {
21543 cell.width = width;
21544 } catch(e) {
21545 }
21546
21547 return width;
21548
21549 }
21550
21551 /**
21552 * 获取单元格所在表格的最末单元格的偏移量
21553 */
21554 function getTableCellOffset( cell ) {
21555
21556 tab = domUtils.findParentByTagName( cell, "table", false);
21557
21558 if( tab.offsetVal === undefined ) {
21559
21560 var prev = cell.previousSibling;
21561
21562 if( prev ) {
21563
21564 //最后一个单元格和前一个单元格的width diff结果 如果恰好为一个border width, 则条件成立
21565 tab.offsetVal = cell.offsetWidth - prev.offsetWidth === UT.borderWidth ? UT.borderWidth : 0;
21566
21567 } else {
21568 tab.offsetVal = 0;
21569 }
21570
21571 }
21572
21573 return tab.offsetVal;
21574
21575 }
21576
21577 function getTabcellSpace() {
21578
21579 if( UT.tabcellSpace === undefined ) {
21580
21581 var cell = null,
21582 tab = me.document.createElement("table"),
21583 tbody = me.document.createElement("tbody"),
21584 trow = me.document.createElement("tr"),
21585 tabcell = me.document.createElement("td"),
21586 mirror = null;
21587
21588 tabcell.style.cssText = 'border: 0;';
21589 tabcell.width = 1;
21590
21591 trow.appendChild( tabcell );
21592 trow.appendChild( mirror = tabcell.cloneNode( false ) );
21593
21594 tbody.appendChild( trow );
21595
21596 tab.appendChild( tbody );
21597
21598 tab.style.cssText = "visibility: hidden;";
21599
21600 me.body.appendChild( tab );
21601
21602 UT.paddingSpace = tabcell.offsetWidth - 1;
21603
21604 var tmpTabWidth = tab.offsetWidth;
21605
21606 tabcell.style.cssText = '';
21607 mirror.style.cssText = '';
21608
21609 UT.borderWidth = ( tab.offsetWidth - tmpTabWidth ) / 3;
21610
21611 UT.tabcellSpace = UT.paddingSpace + UT.borderWidth;
21612
21613 me.body.removeChild( tab );
21614
21615 }
21616
21617 getTabcellSpace = function(){ return UT.tabcellSpace; };
21618
21619 return UT.tabcellSpace;
21620
21621 }
21622
21623 function getDragLine(editor, doc) {
21624 if (mousedown)return;
21625 dragLine = editor.document.createElement("div");
21626 domUtils.setAttributes(dragLine, {
21627 id:"ue_tableDragLine",
21628 unselectable:'on',
21629 contenteditable:false,
21630 'onresizestart':'return false',
21631 'ondragstart':'return false',
21632 'onselectstart':'return false',
21633 style:"background-color:blue;position:absolute;padding:0;margin:0;background-image:none;border:0px none;opacity:0;filter:alpha(opacity=0)"
21634 });
21635 editor.body.appendChild(dragLine);
21636 }
21637
21638 function hideDragLine(editor) {
21639 if (mousedown)return;
21640 var line;
21641 while (line = editor.document.getElementById('ue_tableDragLine')) {
21642 domUtils.remove(line)
21643 }
21644 }
21645
21646 /**
21647 * 依据state(v|h)在cell位置显示横线
21648 * @param state
21649 * @param cell
21650 */
21651 function showDragLineAt(state, cell) {
21652 if (!cell) return;
21653 var table = domUtils.findParentByTagName(cell, "table"),
21654 caption = table.getElementsByTagName('caption'),
21655 width = table.offsetWidth,
21656 height = table.offsetHeight - (caption.length > 0 ? caption[0].offsetHeight : 0),
21657 tablePos = domUtils.getXY(table),
21658 cellPos = domUtils.getXY(cell), css;
21659 switch (state) {
21660 case "h":
21661 css = 'height:' + height + 'px;top:' + (tablePos.y + (caption.length > 0 ? caption[0].offsetHeight : 0)) + 'px;left:' + (cellPos.x + cell.offsetWidth);
21662 dragLine.style.cssText = css + 'px;position: absolute;display:block;background-color:blue;width:1px;border:0; color:blue;opacity:.3;filter:alpha(opacity=30)';
21663 break;
21664 case "v":
21665 css = 'width:' + width + 'px;left:' + tablePos.x + 'px;top:' + (cellPos.y + cell.offsetHeight );
21666 //必须加上border:0和color:blue,否则低版ie不支持背景色显示
21667 dragLine.style.cssText = css + 'px;overflow:hidden;position: absolute;display:block;background-color:blue;height:1px;border:0;color:blue;opacity:.2;filter:alpha(opacity=20)';
21668 break;
21669 default:
21670 }
21671 }
21672
21673 /**
21674 * 当表格边框颜色为白色时设置为虚线,true为添加虚线
21675 * @param editor
21676 * @param flag
21677 */
21678 function switchBorderColor(editor, flag) {
21679 var tableArr = domUtils.getElementsByTagName(editor.body, "table"), color;
21680 for (var i = 0, node; node = tableArr[i++];) {
21681 var td = domUtils.getElementsByTagName(node, "td");
21682 if (td[0]) {
21683 if (flag) {
21684 color = (td[0].style.borderColor).replace(/\s/g, "");
21685 if (/(#ffffff)|(rgb\(255,255,255\))/ig.test(color))
21686 domUtils.addClass(node, "noBorderTable")
21687 } else {
21688 domUtils.removeClasses(node, "noBorderTable")
21689 }
21690 }
21691
21692 }
21693 }
21694
21695 function getTableWidth(editor, needIEHack, defaultValue) {
21696 var body = editor.body;
21697 return body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (editor.options.offsetWidth || 0);
21698 }
21699
21700 /**
21701 * 获取当前拖动的单元格
21702 */
21703 function getTargetTd(editor, evt) {
21704
21705 var target = domUtils.findParentByTagName(evt.target || evt.srcElement, ["td", "th"], true),
21706 dir = null;
21707
21708 if( !target ) {
21709 return null;
21710 }
21711
21712 dir = getRelation( target, mouseCoords( evt ) );
21713
21714 //如果有前一个节点, 需要做一个修正, 否则可能会得到一个错误的td
21715
21716 if( !target ) {
21717 return null;
21718 }
21719
21720 if( dir === 'h1' && target.previousSibling ) {
21721
21722 var position = domUtils.getXY( target),
21723 cellWidth = target.offsetWidth;
21724
21725 if( Math.abs( position.x + cellWidth - evt.clientX ) > cellWidth / 3 ) {
21726 target = target.previousSibling;
21727 }
21728
21729 } else if( dir === 'v1' && target.parentNode.previousSibling ) {
21730
21731 var position = domUtils.getXY( target),
21732 cellHeight = target.offsetHeight;
21733
21734 if( Math.abs( position.y + cellHeight - evt.clientY ) > cellHeight / 3 ) {
21735 target = target.parentNode.previousSibling.firstChild;
21736 }
21737
21738 }
21739
21740
21741 //排除了非td内部以及用于代码高亮部分的td
21742 return target && !(editor.fireEvent("excludetable", target) === true) ? target : null;
21743 }
21744
21745};
21746
21747
21748// plugins/table.sort.js
21749/**
21750 * Created with JetBrains PhpStorm.
21751 * User: Jinqn
21752 * Date: 13-10-12
21753 * Time: 上午10:20
21754 * To change this template use File | Settings | File Templates.
21755 */
21756
21757UE.UETable.prototype.sortTable = function (sortByCellIndex, compareFn) {
21758 var table = this.table,
21759 rows = table.rows,
21760 trArray = [],
21761 flag = rows[0].cells[0].tagName === "TH",
21762 lastRowIndex = 0;
21763 if(this.selectedTds.length){
21764 var range = this.cellsRange,
21765 len = range.endRowIndex + 1;
21766 for (var i = range.beginRowIndex; i < len; i++) {
21767 trArray[i] = rows[i];
21768 }
21769 trArray.splice(0,range.beginRowIndex);
21770 lastRowIndex = (range.endRowIndex +1) === this.rowsNum ? 0 : range.endRowIndex +1;
21771 }else{
21772 for (var i = 0,len = rows.length; i < len; i++) {
21773 trArray[i] = rows[i];
21774 }
21775 }
21776
21777 var Fn = {
21778 'reversecurrent': function(td1,td2){
21779 return 1;
21780 },
21781 'orderbyasc': function(td1,td2){
21782 var value1 = td1.innerText||td1.textContent,
21783 value2 = td2.innerText||td2.textContent;
21784 return value1.localeCompare(value2);
21785 },
21786 'reversebyasc': function(td1,td2){
21787 var value1 = td1.innerHTML,
21788 value2 = td2.innerHTML;
21789 return value2.localeCompare(value1);
21790 },
21791 'orderbynum': function(td1,td2){
21792 var value1 = td1[browser.ie ? 'innerText':'textContent'].match(/\d+/),
21793 value2 = td2[browser.ie ? 'innerText':'textContent'].match(/\d+/);
21794 if(value1) value1 = +value1[0];
21795 if(value2) value2 = +value2[0];
21796 return (value1||0) - (value2||0);
21797 },
21798 'reversebynum': function(td1,td2){
21799 var value1 = td1[browser.ie ? 'innerText':'textContent'].match(/\d+/),
21800 value2 = td2[browser.ie ? 'innerText':'textContent'].match(/\d+/);
21801 if(value1) value1 = +value1[0];
21802 if(value2) value2 = +value2[0];
21803 return (value2||0) - (value1||0);
21804 }
21805 };
21806
21807 //对表格设置排序的标记data-sort-type
21808 table.setAttribute('data-sort-type', compareFn && typeof compareFn === "string" && Fn[compareFn] ? compareFn:'');
21809
21810 //th不参与排序
21811 flag && trArray.splice(0, 1);
21812 trArray = utils.sort(trArray,function (tr1, tr2) {
21813 var result;
21814 if (compareFn && typeof compareFn === "function") {
21815 result = compareFn.call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]);
21816 } else if (compareFn && typeof compareFn === "number") {
21817 result = 1;
21818 } else if (compareFn && typeof compareFn === "string" && Fn[compareFn]) {
21819 result = Fn[compareFn].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]);
21820 } else {
21821 result = Fn['orderbyasc'].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]);
21822 }
21823 return result;
21824 });
21825 var fragment = table.ownerDocument.createDocumentFragment();
21826 for (var j = 0, len = trArray.length; j < len; j++) {
21827 fragment.appendChild(trArray[j]);
21828 }
21829 var tbody = table.getElementsByTagName("tbody")[0];
21830 if(!lastRowIndex){
21831 tbody.appendChild(fragment);
21832 }else{
21833 tbody.insertBefore(fragment,rows[lastRowIndex- range.endRowIndex + range.beginRowIndex - 1])
21834 }
21835};
21836
21837UE.plugins['tablesort'] = function () {
21838 var me = this,
21839 UT = UE.UETable,
21840 getUETable = function (tdOrTable) {
21841 return UT.getUETable(tdOrTable);
21842 },
21843 getTableItemsByRange = function (editor) {
21844 return UT.getTableItemsByRange(editor);
21845 };
21846
21847
21848 me.ready(function () {
21849 //添加表格可排序的样式
21850 utils.cssRule('tablesort',
21851 'table.sortEnabled tr.firstRow th,table.sortEnabled tr.firstRow td{padding-right:20px;background-repeat: no-repeat;background-position: center right;' +
21852 ' background-image:url(' + me.options.themePath + me.options.theme + '/images/sortable.png);}',
21853 me.document);
21854
21855 //做单元格合并操作时,清除可排序标识
21856 me.addListener("afterexeccommand", function (type, cmd) {
21857 if( cmd == 'mergeright' || cmd == 'mergedown' || cmd == 'mergecells') {
21858 this.execCommand('disablesort');
21859 }
21860 });
21861 });
21862
21863
21864
21865 //表格排序
21866 UE.commands['sorttable'] = {
21867 queryCommandState: function () {
21868 var me = this,
21869 tableItems = getTableItemsByRange(me);
21870 if (!tableItems.cell) return -1;
21871 var table = tableItems.table,
21872 cells = table.getElementsByTagName("td");
21873 for (var i = 0, cell; cell = cells[i++];) {
21874 if (cell.rowSpan != 1 || cell.colSpan != 1) return -1;
21875 }
21876 return 0;
21877 },
21878 execCommand: function (cmd, fn) {
21879 var me = this,
21880 range = me.selection.getRange(),
21881 bk = range.createBookmark(true),
21882 tableItems = getTableItemsByRange(me),
21883 cell = tableItems.cell,
21884 ut = getUETable(tableItems.table),
21885 cellInfo = ut.getCellInfo(cell);
21886 ut.sortTable(cellInfo.cellIndex, fn);
21887 range.moveToBookmark(bk);
21888 try{
21889 range.select();
21890 }catch(e){}
21891 }
21892 };
21893
21894 //设置表格可排序,清除表格可排序
21895 UE.commands["enablesort"] = UE.commands["disablesort"] = {
21896 queryCommandState: function (cmd) {
21897 var table = getTableItemsByRange(this).table;
21898 if(table && cmd=='enablesort') {
21899 var cells = domUtils.getElementsByTagName(table, 'th td');
21900 for(var i = 0; i<cells.length; i++) {
21901 if(cells[i].getAttribute('colspan')>1 || cells[i].getAttribute('rowspan')>1) return -1;
21902 }
21903 }
21904
21905 return !table ? -1: cmd=='enablesort' ^ table.getAttribute('data-sort')!='sortEnabled' ? -1:0;
21906 },
21907 execCommand: function (cmd) {
21908 var table = getTableItemsByRange(this).table;
21909 table.setAttribute("data-sort", cmd == "enablesort" ? "sortEnabled" : "sortDisabled");
21910 cmd == "enablesort" ? domUtils.addClass(table,"sortEnabled"):domUtils.removeClasses(table,"sortEnabled");
21911 }
21912 };
21913};
21914
21915
21916// plugins/contextmenu.js
21917///import core
21918///commands 右键菜单
21919///commandsName ContextMenu
21920///commandsTitle 右键菜单
21921/**
21922 * 右键菜单
21923 * @function
21924 * @name baidu.editor.plugins.contextmenu
21925 * @author zhanyi
21926 */
21927
21928UE.plugins['contextmenu'] = function () {
21929 var me = this;
21930 me.setOpt('enableContextMenu',true);
21931 if(me.getOpt('enableContextMenu') === false){
21932 return;
21933 }
21934 var lang = me.getLang( "contextMenu" ),
21935 menu,
21936 items = me.options.contextMenu || [
21937 {label:lang['selectall'], cmdName:'selectall'},
21938 {
21939 label:lang.cleardoc,
21940 cmdName:'cleardoc',
21941 exec:function () {
21942 if ( confirm( lang.confirmclear ) ) {
21943 this.execCommand( 'cleardoc' );
21944 }
21945 }
21946 },
21947 '-',
21948 {
21949 label:lang.unlink,
21950 cmdName:'unlink'
21951 },
21952 '-',
21953 {
21954 group:lang.paragraph,
21955 icon:'justifyjustify',
21956 subMenu:[
21957 {
21958 label:lang.justifyleft,
21959 cmdName:'justify',
21960 value:'left'
21961 },
21962 {
21963 label:lang.justifyright,
21964 cmdName:'justify',
21965 value:'right'
21966 },
21967 {
21968 label:lang.justifycenter,
21969 cmdName:'justify',
21970 value:'center'
21971 },
21972 {
21973 label:lang.justifyjustify,
21974 cmdName:'justify',
21975 value:'justify'
21976 }
21977 ]
21978 },
21979 '-',
21980 {
21981 group:lang.table,
21982 icon:'table',
21983 subMenu:[
21984 {
21985 label:lang.inserttable,
21986 cmdName:'inserttable'
21987 },
21988 {
21989 label:lang.deletetable,
21990 cmdName:'deletetable'
21991 },
21992 '-',
21993 {
21994 label:lang.deleterow,
21995 cmdName:'deleterow'
21996 },
21997 {
21998 label:lang.deletecol,
21999 cmdName:'deletecol'
22000 },
22001 {
22002 label:lang.insertcol,
22003 cmdName:'insertcol'
22004 },
22005 {
22006 label:lang.insertcolnext,
22007 cmdName:'insertcolnext'
22008 },
22009 {
22010 label:lang.insertrow,
22011 cmdName:'insertrow'
22012 },
22013 {
22014 label:lang.insertrownext,
22015 cmdName:'insertrownext'
22016 },
22017 '-',
22018 {
22019 label:lang.insertcaption,
22020 cmdName:'insertcaption'
22021 },
22022 {
22023 label:lang.deletecaption,
22024 cmdName:'deletecaption'
22025 },
22026 {
22027 label:lang.inserttitle,
22028 cmdName:'inserttitle'
22029 },
22030 {
22031 label:lang.deletetitle,
22032 cmdName:'deletetitle'
22033 },
22034 {
22035 label:lang.inserttitlecol,
22036 cmdName:'inserttitlecol'
22037 },
22038 {
22039 label:lang.deletetitlecol,
22040 cmdName:'deletetitlecol'
22041 },
22042 '-',
22043 {
22044 label:lang.mergecells,
22045 cmdName:'mergecells'
22046 },
22047 {
22048 label:lang.mergeright,
22049 cmdName:'mergeright'
22050 },
22051 {
22052 label:lang.mergedown,
22053 cmdName:'mergedown'
22054 },
22055 '-',
22056 {
22057 label:lang.splittorows,
22058 cmdName:'splittorows'
22059 },
22060 {
22061 label:lang.splittocols,
22062 cmdName:'splittocols'
22063 },
22064 {
22065 label:lang.splittocells,
22066 cmdName:'splittocells'
22067 },
22068 '-',
22069 {
22070 label:lang.averageDiseRow,
22071 cmdName:'averagedistributerow'
22072 },
22073 {
22074 label:lang.averageDisCol,
22075 cmdName:'averagedistributecol'
22076 },
22077 '-',
22078 {
22079 label:lang.edittd,
22080 cmdName:'edittd',
22081 exec:function () {
22082 if ( UE.ui['edittd'] ) {
22083 new UE.ui['edittd']( this );
22084 }
22085 this.getDialog('edittd').open();
22086 }
22087 },
22088 {
22089 label:lang.edittable,
22090 cmdName:'edittable',
22091 exec:function () {
22092 if ( UE.ui['edittable'] ) {
22093 new UE.ui['edittable']( this );
22094 }
22095 this.getDialog('edittable').open();
22096 }
22097 },
22098 {
22099 label:lang.setbordervisible,
22100 cmdName:'setbordervisible'
22101 }
22102 ]
22103 },
22104 {
22105 group:lang.tablesort,
22106 icon:'tablesort',
22107 subMenu:[
22108 {
22109 label:lang.enablesort,
22110 cmdName:'enablesort'
22111 },
22112 {
22113 label:lang.disablesort,
22114 cmdName:'disablesort'
22115 },
22116 '-',
22117 {
22118 label:lang.reversecurrent,
22119 cmdName:'sorttable',
22120 value:'reversecurrent'
22121 },
22122 {
22123 label:lang.orderbyasc,
22124 cmdName:'sorttable',
22125 value:'orderbyasc'
22126 },
22127 {
22128 label:lang.reversebyasc,
22129 cmdName:'sorttable',
22130 value:'reversebyasc'
22131 },
22132 {
22133 label:lang.orderbynum,
22134 cmdName:'sorttable',
22135 value:'orderbynum'
22136 },
22137 {
22138 label:lang.reversebynum,
22139 cmdName:'sorttable',
22140 value:'reversebynum'
22141 }
22142 ]
22143 },
22144 {
22145 group:lang.borderbk,
22146 icon:'borderBack',
22147 subMenu:[
22148 {
22149 label:lang.setcolor,
22150 cmdName:"interlacetable",
22151 exec:function(){
22152 this.execCommand("interlacetable");
22153 }
22154 },
22155 {
22156 label:lang.unsetcolor,
22157 cmdName:"uninterlacetable",
22158 exec:function(){
22159 this.execCommand("uninterlacetable");
22160 }
22161 },
22162 {
22163 label:lang.setbackground,
22164 cmdName:"settablebackground",
22165 exec:function(){
22166 this.execCommand("settablebackground",{repeat:true,colorList:["#bbb","#ccc"]});
22167 }
22168 },
22169 {
22170 label:lang.unsetbackground,
22171 cmdName:"cleartablebackground",
22172 exec:function(){
22173 this.execCommand("cleartablebackground");
22174 }
22175 },
22176 {
22177 label:lang.redandblue,
22178 cmdName:"settablebackground",
22179 exec:function(){
22180 this.execCommand("settablebackground",{repeat:true,colorList:["red","blue"]});
22181 }
22182 },
22183 {
22184 label:lang.threecolorgradient,
22185 cmdName:"settablebackground",
22186 exec:function(){
22187 this.execCommand("settablebackground",{repeat:true,colorList:["#aaa","#bbb","#ccc"]});
22188 }
22189 }
22190 ]
22191 },
22192 {
22193 group:lang.aligntd,
22194 icon:'aligntd',
22195 subMenu:[
22196 {
22197 cmdName:'cellalignment',
22198 value:{align:'left',vAlign:'top'}
22199 },
22200 {
22201 cmdName:'cellalignment',
22202 value:{align:'center',vAlign:'top'}
22203 },
22204 {
22205 cmdName:'cellalignment',
22206 value:{align:'right',vAlign:'top'}
22207 },
22208 {
22209 cmdName:'cellalignment',
22210 value:{align:'left',vAlign:'middle'}
22211 },
22212 {
22213 cmdName:'cellalignment',
22214 value:{align:'center',vAlign:'middle'}
22215 },
22216 {
22217 cmdName:'cellalignment',
22218 value:{align:'right',vAlign:'middle'}
22219 },
22220 {
22221 cmdName:'cellalignment',
22222 value:{align:'left',vAlign:'bottom'}
22223 },
22224 {
22225 cmdName:'cellalignment',
22226 value:{align:'center',vAlign:'bottom'}
22227 },
22228 {
22229 cmdName:'cellalignment',
22230 value:{align:'right',vAlign:'bottom'}
22231 }
22232 ]
22233 },
22234 {
22235 group:lang.aligntable,
22236 icon:'aligntable',
22237 subMenu:[
22238 {
22239 cmdName:'tablealignment',
22240 className: 'left',
22241 label:lang.tableleft,
22242 value:"left"
22243 },
22244 {
22245 cmdName:'tablealignment',
22246 className: 'center',
22247 label:lang.tablecenter,
22248 value:"center"
22249 },
22250 {
22251 cmdName:'tablealignment',
22252 className: 'right',
22253 label:lang.tableright,
22254 value:"right"
22255 }
22256 ]
22257 },
22258 '-',
22259 {
22260 label:lang.insertparagraphbefore,
22261 cmdName:'insertparagraph',
22262 value:true
22263 },
22264 {
22265 label:lang.insertparagraphafter,
22266 cmdName:'insertparagraph'
22267 },
22268 {
22269 label:lang['copy'],
22270 cmdName:'copy'
22271 },
22272 {
22273 label:lang['paste'],
22274 cmdName:'paste'
22275 }
22276 ];
22277 if ( !items.length ) {
22278 return;
22279 }
22280 var uiUtils = UE.ui.uiUtils;
22281
22282 me.addListener( 'contextmenu', function ( type, evt ) {
22283
22284 var offset = uiUtils.getViewportOffsetByEvent( evt );
22285 me.fireEvent( 'beforeselectionchange' );
22286 if ( menu ) {
22287 menu.destroy();
22288 }
22289 for ( var i = 0, ti, contextItems = []; ti = items[i]; i++ ) {
22290 var last;
22291 (function ( item ) {
22292 if ( item == '-' ) {
22293 if ( (last = contextItems[contextItems.length - 1 ] ) && last !== '-' ) {
22294 contextItems.push( '-' );
22295 }
22296 } else if ( item.hasOwnProperty( "group" ) ) {
22297 for ( var j = 0, cj, subMenu = []; cj = item.subMenu[j]; j++ ) {
22298 (function ( subItem ) {
22299 if ( subItem == '-' ) {
22300 if ( (last = subMenu[subMenu.length - 1 ] ) && last !== '-' ) {
22301 subMenu.push( '-' );
22302 }else{
22303 subMenu.splice(subMenu.length-1);
22304 }
22305 } else {
22306 if ( (me.commands[subItem.cmdName] || UE.commands[subItem.cmdName] || subItem.query) &&
22307 (subItem.query ? subItem.query() : me.queryCommandState( subItem.cmdName )) > -1 ) {
22308 subMenu.push( {
22309 'label':subItem.label || me.getLang( "contextMenu." + subItem.cmdName + (subItem.value || '') )||"",
22310 'className':'edui-for-' +subItem.cmdName + ( subItem.className ? ( ' edui-for-' + subItem.cmdName + '-' + subItem.className ) : '' ),
22311 onclick:subItem.exec ? function () {
22312 subItem.exec.call( me );
22313 } : function () {
22314 me.execCommand( subItem.cmdName, subItem.value );
22315 }
22316 } );
22317 }
22318 }
22319 })( cj );
22320 }
22321 if ( subMenu.length ) {
22322 function getLabel(){
22323 switch (item.icon){
22324 case "table":
22325 return me.getLang( "contextMenu.table" );
22326 case "justifyjustify":
22327 return me.getLang( "contextMenu.paragraph" );
22328 case "aligntd":
22329 return me.getLang("contextMenu.aligntd");
22330 case "aligntable":
22331 return me.getLang("contextMenu.aligntable");
22332 case "tablesort":
22333 return lang.tablesort;
22334 case "borderBack":
22335 return lang.borderbk;
22336 default :
22337 return '';
22338 }
22339 }
22340 contextItems.push( {
22341 //todo 修正成自动获取方式
22342 'label':getLabel(),
22343 className:'edui-for-' + item.icon,
22344 'subMenu':{
22345 items:subMenu,
22346 editor:me
22347 }
22348 } );
22349 }
22350
22351 } else {
22352 //有可能commmand没有加载右键不能出来,或者没有command也想能展示出来添加query方法
22353 if ( (me.commands[item.cmdName] || UE.commands[item.cmdName] || item.query) &&
22354 (item.query ? item.query.call(me) : me.queryCommandState( item.cmdName )) > -1 ) {
22355
22356 contextItems.push( {
22357 'label':item.label || me.getLang( "contextMenu." + item.cmdName ),
22358 className:'edui-for-' + (item.icon ? item.icon : item.cmdName + (item.value || '')),
22359 onclick:item.exec ? function () {
22360 item.exec.call( me );
22361 } : function () {
22362 me.execCommand( item.cmdName, item.value );
22363 }
22364 } );
22365 }
22366
22367 }
22368
22369 })( ti );
22370 }
22371 if ( contextItems[contextItems.length - 1] == '-' ) {
22372 contextItems.pop();
22373 }
22374
22375 menu = new UE.ui.Menu( {
22376 items:contextItems,
22377 className:"edui-contextmenu",
22378 editor:me
22379 } );
22380 menu.render();
22381 menu.showAt( offset );
22382
22383 me.fireEvent("aftershowcontextmenu",menu);
22384
22385 domUtils.preventDefault( evt );
22386 if ( browser.ie ) {
22387 var ieRange;
22388 try {
22389 ieRange = me.selection.getNative().createRange();
22390 } catch ( e ) {
22391 return;
22392 }
22393 if ( ieRange.item ) {
22394 var range = new dom.Range( me.document );
22395 range.selectNode( ieRange.item( 0 ) ).select( true, true );
22396 }
22397 }
22398 });
22399
22400 // 添加复制的flash按钮
22401 me.addListener('aftershowcontextmenu', function(type, menu) {
22402 if (me.zeroclipboard) {
22403 var items = menu.items;
22404 for (var key in items) {
22405 if (items[key].className == 'edui-for-copy') {
22406 me.zeroclipboard.clip(items[key].getDom());
22407 }
22408 }
22409 }
22410 });
22411
22412};
22413
22414
22415// plugins/shortcutmenu.js
22416///import core
22417///commands 弹出菜单
22418// commandsName popupmenu
22419///commandsTitle 弹出菜单
22420/**
22421 * 弹出菜单
22422 * @function
22423 * @name baidu.editor.plugins.popupmenu
22424 * @author xuheng
22425 */
22426
22427UE.plugins['shortcutmenu'] = function () {
22428 var me = this,
22429 menu,
22430 items = me.options.shortcutMenu || [];
22431
22432 if (!items.length) {
22433 return;
22434 }
22435
22436 me.addListener ('contextmenu mouseup' , function (type , e) {
22437 var me = this,
22438 customEvt = {
22439 type : type ,
22440 target : e.target || e.srcElement ,
22441 screenX : e.screenX ,
22442 screenY : e.screenY ,
22443 clientX : e.clientX ,
22444 clientY : e.clientY
22445 };
22446
22447 setTimeout (function () {
22448 var rng = me.selection.getRange ();
22449 if (rng.collapsed === false || type == "contextmenu") {
22450
22451 if (!menu) {
22452 menu = new baidu.editor.ui.ShortCutMenu ({
22453 editor : me ,
22454 items : items ,
22455 theme : me.options.theme ,
22456 className : 'edui-shortcutmenu'
22457 });
22458
22459 menu.render ();
22460 me.fireEvent ("afterrendershortcutmenu" , menu);
22461 }
22462
22463 menu.show (customEvt , !!UE.plugins['contextmenu']);
22464 }
22465 });
22466
22467 if (type == 'contextmenu') {
22468 domUtils.preventDefault (e);
22469 if (browser.ie9below) {
22470 var ieRange;
22471 try {
22472 ieRange = me.selection.getNative().createRange();
22473 } catch (e) {
22474 return;
22475 }
22476 if (ieRange.item) {
22477 var range = new dom.Range (me.document);
22478 range.selectNode (ieRange.item (0)).select (true , true);
22479
22480 }
22481 }
22482 }
22483 });
22484
22485 me.addListener ('keydown' , function (type) {
22486 if (type == "keydown") {
22487 menu && !menu.isHidden && menu.hide ();
22488 }
22489
22490 });
22491
22492};
22493
22494
22495
22496
22497// plugins/basestyle.js
22498/**
22499 * B、I、sub、super命令支持
22500 * @file
22501 * @since 1.2.6.1
22502 */
22503
22504UE.plugins['basestyle'] = function(){
22505
22506 /**
22507 * 字体加粗
22508 * @command bold
22509 * @param { String } cmd 命令字符串
22510 * @remind 对已加粗的文本内容执行该命令, 将取消加粗
22511 * @method execCommand
22512 * @example
22513 * ```javascript
22514 * //editor是编辑器实例
22515 * //对当前选中的文本内容执行加粗操作
22516 * //第一次执行, 文本内容加粗
22517 * editor.execCommand( 'bold' );
22518 *
22519 * //第二次执行, 文本内容取消加粗
22520 * editor.execCommand( 'bold' );
22521 * ```
22522 */
22523
22524
22525 /**
22526 * 字体倾斜
22527 * @command italic
22528 * @method execCommand
22529 * @param { String } cmd 命令字符串
22530 * @remind 对已倾斜的文本内容执行该命令, 将取消倾斜
22531 * @example
22532 * ```javascript
22533 * //editor是编辑器实例
22534 * //对当前选中的文本内容执行斜体操作
22535 * //第一次操作, 文本内容将变成斜体
22536 * editor.execCommand( 'italic' );
22537 *
22538 * //再次对同一文本内容执行, 则文本内容将恢复正常
22539 * editor.execCommand( 'italic' );
22540 * ```
22541 */
22542
22543 /**
22544 * 下标文本,与“superscript”命令互斥
22545 * @command subscript
22546 * @method execCommand
22547 * @remind 把选中的文本内容切换成下标文本, 如果当前选中的文本已经是下标, 则该操作会把文本内容还原成正常文本
22548 * @param { String } cmd 命令字符串
22549 * @example
22550 * ```javascript
22551 * //editor是编辑器实例
22552 * //对当前选中的文本内容执行下标操作
22553 * //第一次操作, 文本内容将变成下标文本
22554 * editor.execCommand( 'subscript' );
22555 *
22556 * //再次对同一文本内容执行, 则文本内容将恢复正常
22557 * editor.execCommand( 'subscript' );
22558 * ```
22559 */
22560
22561 /**
22562 * 上标文本,与“subscript”命令互斥
22563 * @command superscript
22564 * @method execCommand
22565 * @remind 把选中的文本内容切换成上标文本, 如果当前选中的文本已经是上标, 则该操作会把文本内容还原成正常文本
22566 * @param { String } cmd 命令字符串
22567 * @example
22568 * ```javascript
22569 * //editor是编辑器实例
22570 * //对当前选中的文本内容执行上标操作
22571 * //第一次操作, 文本内容将变成上标文本
22572 * editor.execCommand( 'superscript' );
22573 *
22574 * //再次对同一文本内容执行, 则文本内容将恢复正常
22575 * editor.execCommand( 'superscript' );
22576 * ```
22577 */
22578 var basestyles = {
22579 'bold':['strong','b'],
22580 'italic':['em','i'],
22581 'subscript':['sub'],
22582 'superscript':['sup']
22583 },
22584 getObj = function(editor,tagNames){
22585 return domUtils.filterNodeList(editor.selection.getStartElementPath(),tagNames);
22586 },
22587 me = this;
22588 //添加快捷键
22589 me.addshortcutkey({
22590 "Bold" : "ctrl+66",//^B
22591 "Italic" : "ctrl+73", //^I
22592 "Underline" : "ctrl+85"//^U
22593 });
22594 me.addInputRule(function(root){
22595 utils.each(root.getNodesByTagName('b i'),function(node){
22596 switch (node.tagName){
22597 case 'b':
22598 node.tagName = 'strong';
22599 break;
22600 case 'i':
22601 node.tagName = 'em';
22602 }
22603 });
22604 });
22605 for ( var style in basestyles ) {
22606 (function( cmd, tagNames ) {
22607 me.commands[cmd] = {
22608 execCommand : function( cmdName ) {
22609 var range = me.selection.getRange(),obj = getObj(this,tagNames);
22610 if ( range.collapsed ) {
22611 if ( obj ) {
22612 var tmpText = me.document.createTextNode('');
22613 range.insertNode( tmpText ).removeInlineStyle( tagNames );
22614 range.setStartBefore(tmpText);
22615 domUtils.remove(tmpText);
22616 } else {
22617 var tmpNode = range.document.createElement( tagNames[0] );
22618 if(cmdName == 'superscript' || cmdName == 'subscript'){
22619 tmpText = me.document.createTextNode('');
22620 range.insertNode(tmpText)
22621 .removeInlineStyle(['sub','sup'])
22622 .setStartBefore(tmpText)
22623 .collapse(true);
22624 }
22625 range.insertNode( tmpNode ).setStart( tmpNode, 0 );
22626 }
22627 range.collapse( true );
22628 } else {
22629 if(cmdName == 'superscript' || cmdName == 'subscript'){
22630 if(!obj || obj.tagName.toLowerCase() != cmdName){
22631 range.removeInlineStyle(['sub','sup']);
22632 }
22633 }
22634 obj ? range.removeInlineStyle( tagNames ) : range.applyInlineStyle( tagNames[0] );
22635 }
22636 range.select();
22637 },
22638 queryCommandState : function() {
22639 return getObj(this,tagNames) ? 1 : 0;
22640 }
22641 };
22642 })( style, basestyles[style] );
22643 }
22644};
22645
22646
22647
22648// plugins/elementpath.js
22649/**
22650 * 选取路径命令
22651 * @file
22652 */
22653UE.plugins['elementpath'] = function(){
22654 var currentLevel,
22655 tagNames,
22656 me = this;
22657 me.setOpt('elementPathEnabled',true);
22658 if(!me.options.elementPathEnabled){
22659 return;
22660 }
22661 me.commands['elementpath'] = {
22662 execCommand : function( cmdName, level ) {
22663 var start = tagNames[level],
22664 range = me.selection.getRange();
22665 currentLevel = level*1;
22666 range.selectNode(start).select();
22667 },
22668 queryCommandValue : function() {
22669 //产生一个副本,不能修改原来的startElementPath;
22670 var parents = [].concat(this.selection.getStartElementPath()).reverse(),
22671 names = [];
22672 tagNames = parents;
22673 for(var i=0,ci;ci=parents[i];i++){
22674 if(ci.nodeType == 3) {
22675 continue;
22676 }
22677 var name = ci.tagName.toLowerCase();
22678 if(name == 'img' && ci.getAttribute('anchorname')){
22679 name = 'anchor';
22680 }
22681 names[i] = name;
22682 if(currentLevel == i){
22683 currentLevel = -1;
22684 break;
22685 }
22686 }
22687 return names;
22688 }
22689 };
22690};
22691
22692
22693
22694// plugins/formatmatch.js
22695/**
22696 * 格式刷,只格式inline的
22697 * @file
22698 * @since 1.2.6.1
22699 */
22700
22701/**
22702 * 格式刷
22703 * @command formatmatch
22704 * @method execCommand
22705 * @remind 该操作不能复制段落格式
22706 * @param { String } cmd 命令字符串
22707 * @example
22708 * ```javascript
22709 * //editor是编辑器实例
22710 * //获取格式刷
22711 * editor.execCommand( 'formatmatch' );
22712 * ```
22713 */
22714UE.plugins['formatmatch'] = function(){
22715
22716 var me = this,
22717 list = [],img,
22718 flag = 0;
22719
22720 me.addListener('reset',function(){
22721 list = [];
22722 flag = 0;
22723 });
22724
22725 function addList(type,evt){
22726
22727 if(browser.webkit){
22728 var target = evt.target.tagName == 'IMG' ? evt.target : null;
22729 }
22730
22731 function addFormat(range){
22732
22733 if(text){
22734 range.selectNode(text);
22735 }
22736 return range.applyInlineStyle(list[list.length-1].tagName,null,list);
22737
22738 }
22739
22740 me.undoManger && me.undoManger.save();
22741
22742 var range = me.selection.getRange(),
22743 imgT = target || range.getClosedNode();
22744 if(img && imgT && imgT.tagName == 'IMG'){
22745 //trace:964
22746
22747 imgT.style.cssText += ';float:' + (img.style.cssFloat || img.style.styleFloat ||'none') + ';display:' + (img.style.display||'inline');
22748
22749 img = null;
22750 }else{
22751 if(!img){
22752 var collapsed = range.collapsed;
22753 if(collapsed){
22754 var text = me.document.createTextNode('match');
22755 range.insertNode(text).select();
22756
22757
22758 }
22759 me.__hasEnterExecCommand = true;
22760 //不能把block上的属性干掉
22761 //trace:1553
22762 var removeFormatAttributes = me.options.removeFormatAttributes;
22763 me.options.removeFormatAttributes = '';
22764 me.execCommand('removeformat');
22765 me.options.removeFormatAttributes = removeFormatAttributes;
22766 me.__hasEnterExecCommand = false;
22767 //trace:969
22768 range = me.selection.getRange();
22769 if(list.length){
22770 addFormat(range);
22771 }
22772 if(text){
22773 range.setStartBefore(text).collapse(true);
22774
22775 }
22776 range.select();
22777 text && domUtils.remove(text);
22778 }
22779
22780 }
22781
22782
22783
22784
22785 me.undoManger && me.undoManger.save();
22786 me.removeListener('mouseup',addList);
22787 flag = 0;
22788 }
22789
22790 me.commands['formatmatch'] = {
22791 execCommand : function( cmdName ) {
22792
22793 if(flag){
22794 flag = 0;
22795 list = [];
22796 me.removeListener('mouseup',addList);
22797 return;
22798 }
22799
22800
22801
22802 var range = me.selection.getRange();
22803 img = range.getClosedNode();
22804 if(!img || img.tagName != 'IMG'){
22805 range.collapse(true).shrinkBoundary();
22806 var start = range.startContainer;
22807 list = domUtils.findParents(start,true,function(node){
22808 return !domUtils.isBlockElm(node) && node.nodeType == 1;
22809 });
22810 //a不能加入格式刷, 并且克隆节点
22811 for(var i=0,ci;ci=list[i];i++){
22812 if(ci.tagName == 'A'){
22813 list.splice(i,1);
22814 break;
22815 }
22816 }
22817
22818 }
22819
22820 me.addListener('mouseup',addList);
22821 flag = 1;
22822
22823
22824 },
22825 queryCommandState : function() {
22826 return flag;
22827 },
22828 notNeedUndo : 1
22829 };
22830};
22831
22832
22833
22834// plugins/searchreplace.js
22835///import core
22836///commands 查找替换
22837///commandsName SearchReplace
22838///commandsTitle 查询替换
22839///commandsDialog dialogs\searchreplace
22840/**
22841 * @description 查找替换
22842 * @author zhanyi
22843 */
22844
22845UE.plugin.register('searchreplace',function(){
22846 var me = this;
22847
22848 var _blockElm = {'table':1,'tbody':1,'tr':1,'ol':1,'ul':1};
22849
22850 function findTextInString(textContent,opt,currentIndex){
22851 var str = opt.searchStr;
22852 if(opt.dir == -1){
22853 textContent = textContent.split('').reverse().join('');
22854 str = str.split('').reverse().join('');
22855 currentIndex = textContent.length - currentIndex;
22856
22857 }
22858 var reg = new RegExp(str,'g' + (opt.casesensitive ? '' : 'i')),match;
22859
22860 while(match = reg.exec(textContent)){
22861 if(match.index >= currentIndex){
22862 return opt.dir == -1 ? textContent.length - match.index - opt.searchStr.length : match.index;
22863 }
22864 }
22865 return -1
22866 }
22867 function findTextBlockElm(node,currentIndex,opt){
22868 var textContent,index,methodName = opt.all || opt.dir == 1 ? 'getNextDomNode' : 'getPreDomNode';
22869 if(domUtils.isBody(node)){
22870 node = node.firstChild;
22871 }
22872 var first = 1;
22873 while(node){
22874 textContent = node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent'];
22875 index = findTextInString(textContent,opt,currentIndex );
22876 first = 0;
22877 if(index!=-1){
22878 return {
22879 'node':node,
22880 'index':index
22881 }
22882 }
22883 node = domUtils[methodName](node);
22884 while(node && _blockElm[node.nodeName.toLowerCase()]){
22885 node = domUtils[methodName](node,true);
22886 }
22887 if(node){
22888 currentIndex = opt.dir == -1 ? (node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent']).length : 0;
22889 }
22890
22891 }
22892 }
22893 function findNTextInBlockElm(node,index,str){
22894 var currentIndex = 0,
22895 currentNode = node.firstChild,
22896 currentNodeLength = 0,
22897 result;
22898 while(currentNode){
22899 if(currentNode.nodeType == 3){
22900 currentNodeLength = currentNode.nodeValue.replace(/(^[\t\r\n]+)|([\t\r\n]+$)/,'').length;
22901 currentIndex += currentNodeLength;
22902 if(currentIndex >= index){
22903 return {
22904 'node':currentNode,
22905 'index': currentNodeLength - (currentIndex - index)
22906 }
22907 }
22908 }else if(!dtd.$empty[currentNode.tagName]){
22909 currentNodeLength = currentNode[browser.ie ? 'innerText' : 'textContent'].replace(/(^[\t\r\n]+)|([\t\r\n]+$)/,'').length
22910 currentIndex += currentNodeLength;
22911 if(currentIndex >= index){
22912 result = findNTextInBlockElm(currentNode,currentNodeLength - (currentIndex - index),str);
22913 if(result){
22914 return result;
22915 }
22916 }
22917 }
22918 currentNode = domUtils.getNextDomNode(currentNode);
22919
22920 }
22921 }
22922
22923 function searchReplace(me,opt){
22924
22925 var rng = me.selection.getRange(),
22926 startBlockNode,
22927 searchStr = opt.searchStr,
22928 span = me.document.createElement('span');
22929 span.innerHTML = '$$ueditor_searchreplace_key$$';
22930
22931 rng.shrinkBoundary(true);
22932
22933 //判断是不是第一次选中
22934 if(!rng.collapsed){
22935 rng.select();
22936 var rngText = me.selection.getText();
22937 if(new RegExp('^' + opt.searchStr + '$',(opt.casesensitive ? '' : 'i')).test(rngText)){
22938 if(opt.replaceStr != undefined){
22939 replaceText(rng,opt.replaceStr);
22940 rng.select();
22941 return true;
22942 }else{
22943 rng.collapse(opt.dir == -1)
22944 }
22945
22946 }
22947 }
22948
22949
22950 rng.insertNode(span);
22951 rng.enlargeToBlockElm(true);
22952 startBlockNode = rng.startContainer;
22953 var currentIndex = startBlockNode[browser.ie ? 'innerText' : 'textContent'].indexOf('$$ueditor_searchreplace_key$$');
22954 rng.setStartBefore(span);
22955 domUtils.remove(span);
22956 var result = findTextBlockElm(startBlockNode,currentIndex,opt);
22957 if(result){
22958 var rngStart = findNTextInBlockElm(result.node,result.index,searchStr);
22959 var rngEnd = findNTextInBlockElm(result.node,result.index + searchStr.length,searchStr);
22960 rng.setStart(rngStart.node,rngStart.index).setEnd(rngEnd.node,rngEnd.index);
22961
22962 if(opt.replaceStr !== undefined){
22963 replaceText(rng,opt.replaceStr)
22964 }
22965 rng.select();
22966 return true;
22967 }else{
22968 rng.setCursor()
22969 }
22970
22971 }
22972 function replaceText(rng,str){
22973
22974 str = me.document.createTextNode(str);
22975 rng.deleteContents().insertNode(str);
22976
22977 }
22978 return {
22979 commands:{
22980 'searchreplace':{
22981 execCommand:function(cmdName,opt){
22982 utils.extend(opt,{
22983 all : false,
22984 casesensitive : false,
22985 dir : 1
22986 },true);
22987 var num = 0;
22988 if(opt.all){
22989
22990 var rng = me.selection.getRange(),
22991 first = me.body.firstChild;
22992 if(first && first.nodeType == 1){
22993 rng.setStart(first,0);
22994 rng.shrinkBoundary(true);
22995 }else if(first.nodeType == 3){
22996 rng.setStartBefore(first)
22997 }
22998 rng.collapse(true).select(true);
22999 if(opt.replaceStr !== undefined){
23000 me.fireEvent('saveScene');
23001 }
23002 while(searchReplace(this,opt)){
23003 num++;
23004 }
23005 if(num){
23006 me.fireEvent('saveScene');
23007 }
23008 }else{
23009 if(opt.replaceStr !== undefined){
23010 me.fireEvent('saveScene');
23011 }
23012 if(searchReplace(this,opt)){
23013 num++
23014 }
23015 if(num){
23016 me.fireEvent('saveScene');
23017 }
23018
23019 }
23020
23021 return num;
23022 },
23023 notNeedUndo:1
23024 }
23025 }
23026 }
23027});
23028
23029// plugins/customstyle.js
23030/**
23031 * 自定义样式
23032 * @file
23033 * @since 1.2.6.1
23034 */
23035
23036/**
23037 * 根据config配置文件里“customstyle”选项的值对匹配的标签执行样式替换。
23038 * @command customstyle
23039 * @method execCommand
23040 * @param { String } cmd 命令字符串
23041 * @example
23042 * ```javascript
23043 * editor.execCommand( 'customstyle' );
23044 * ```
23045 */
23046UE.plugins['customstyle'] = function() {
23047 var me = this;
23048 me.setOpt({ 'customstyle':[
23049 {tag:'h1',name:'tc', style:'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;margin:0 0 20px 0;'},
23050 {tag:'h1',name:'tl', style:'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:left;margin:0 0 10px 0;'},
23051 {tag:'span',name:'im', style:'font-size:16px;font-style:italic;font-weight:bold;line-height:18px;'},
23052 {tag:'span',name:'hi', style:'font-size:16px;font-style:italic;font-weight:bold;color:rgb(51, 153, 204);line-height:18px;'}
23053 ]});
23054 me.commands['customstyle'] = {
23055 execCommand : function(cmdName, obj) {
23056 var me = this,
23057 tagName = obj.tag,
23058 node = domUtils.findParent(me.selection.getStart(), function(node) {
23059 return node.getAttribute('label');
23060 }, true),
23061 range,bk,tmpObj = {};
23062 for (var p in obj) {
23063 if(obj[p]!==undefined)
23064 tmpObj[p] = obj[p];
23065 }
23066 delete tmpObj.tag;
23067 if (node && node.getAttribute('label') == obj.label) {
23068 range = this.selection.getRange();
23069 bk = range.createBookmark();
23070 if (range.collapsed) {
23071 //trace:1732 删掉自定义标签,要有p来回填站位
23072 if(dtd.$block[node.tagName]){
23073 var fillNode = me.document.createElement('p');
23074 domUtils.moveChild(node, fillNode);
23075 node.parentNode.insertBefore(fillNode, node);
23076 domUtils.remove(node);
23077 }else{
23078 domUtils.remove(node,true);
23079 }
23080
23081 } else {
23082
23083 var common = domUtils.getCommonAncestor(bk.start, bk.end),
23084 nodes = domUtils.getElementsByTagName(common, tagName);
23085 if(new RegExp(tagName,'i').test(common.tagName)){
23086 nodes.push(common);
23087 }
23088 for (var i = 0,ni; ni = nodes[i++];) {
23089 if (ni.getAttribute('label') == obj.label) {
23090 var ps = domUtils.getPosition(ni, bk.start),pe = domUtils.getPosition(ni, bk.end);
23091 if ((ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS)
23092 &&
23093 (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
23094 )
23095 if (dtd.$block[tagName]) {
23096 var fillNode = me.document.createElement('p');
23097 domUtils.moveChild(ni, fillNode);
23098 ni.parentNode.insertBefore(fillNode, ni);
23099 }
23100 domUtils.remove(ni, true);
23101 }
23102 }
23103 node = domUtils.findParent(common, function(node) {
23104 return node.getAttribute('label') == obj.label;
23105 }, true);
23106 if (node) {
23107
23108 domUtils.remove(node, true);
23109
23110 }
23111
23112 }
23113 range.moveToBookmark(bk).select();
23114 } else {
23115 if (dtd.$block[tagName]) {
23116 this.execCommand('paragraph', tagName, tmpObj,'customstyle');
23117 range = me.selection.getRange();
23118 if (!range.collapsed) {
23119 range.collapse();
23120 node = domUtils.findParent(me.selection.getStart(), function(node) {
23121 return node.getAttribute('label') == obj.label;
23122 }, true);
23123 var pNode = me.document.createElement('p');
23124 domUtils.insertAfter(node, pNode);
23125 domUtils.fillNode(me.document, pNode);
23126 range.setStart(pNode, 0).setCursor();
23127 }
23128 } else {
23129
23130 range = me.selection.getRange();
23131 if (range.collapsed) {
23132 node = me.document.createElement(tagName);
23133 domUtils.setAttributes(node, tmpObj);
23134 range.insertNode(node).setStart(node, 0).setCursor();
23135
23136 return;
23137 }
23138
23139 bk = range.createBookmark();
23140 range.applyInlineStyle(tagName, tmpObj).moveToBookmark(bk).select();
23141 }
23142 }
23143
23144 },
23145 queryCommandValue : function() {
23146 var parent = domUtils.filterNodeList(
23147 this.selection.getStartElementPath(),
23148 function(node){return node.getAttribute('label')}
23149 );
23150 return parent ? parent.getAttribute('label') : '';
23151 }
23152 };
23153 //当去掉customstyle是,如果是块元素,用p代替
23154 me.addListener('keyup', function(type, evt) {
23155 var keyCode = evt.keyCode || evt.which;
23156
23157 if (keyCode == 32 || keyCode == 13) {
23158 var range = me.selection.getRange();
23159 if (range.collapsed) {
23160 var node = domUtils.findParent(me.selection.getStart(), function(node) {
23161 return node.getAttribute('label');
23162 }, true);
23163 if (node && dtd.$block[node.tagName] && domUtils.isEmptyNode(node)) {
23164 var p = me.document.createElement('p');
23165 domUtils.insertAfter(node, p);
23166 domUtils.fillNode(me.document, p);
23167 domUtils.remove(node);
23168 range.setStart(p, 0).setCursor();
23169
23170
23171 }
23172 }
23173 }
23174 });
23175};
23176
23177// plugins/catchremoteimage.js
23178///import core
23179///commands 远程图片抓取
23180///commandsName catchRemoteImage,catchremoteimageenable
23181///commandsTitle 远程图片抓取
23182/**
23183 * 远程图片抓取,当开启本插件时所有不符合本地域名的图片都将被抓取成为本地服务器上的图片
23184 */
23185UE.plugins['catchremoteimage'] = function () {
23186 var me = this,
23187 ajax = UE.ajax;
23188
23189 /* 设置默认值 */
23190 if (me.options.catchRemoteImageEnable === false) return;
23191 me.setOpt({
23192 catchRemoteImageEnable: false
23193 });
23194
23195 me.addListener("afterpaste", function () {
23196 me.fireEvent("catchRemoteImage");
23197 });
23198
23199 me.addListener("catchRemoteImage", function () {
23200
23201 var catcherLocalDomain = me.getOpt('catcherLocalDomain'),
23202 catcherActionUrl = me.getActionUrl(me.getOpt('catcherActionName')),
23203 catcherUrlPrefix = me.getOpt('catcherUrlPrefix'),
23204 catcherFieldName = me.getOpt('catcherFieldName');
23205
23206 var remoteImages = [],
23207 imgs = domUtils.getElementsByTagName(me.document, "img"),
23208 test = function (src, urls) {
23209 if (src.indexOf(location.host) != -1 || /(^\.)|(^\/)/.test(src)) {
23210 return true;
23211 }
23212 if (urls) {
23213 for (var j = 0, url; url = urls[j++];) {
23214 if (src.indexOf(url) !== -1) {
23215 return true;
23216 }
23217 }
23218 }
23219 return false;
23220 };
23221
23222 for (var i = 0, ci; ci = imgs[i++];) {
23223 if (ci.getAttribute("word_img")) {
23224 continue;
23225 }
23226 var src = ci.getAttribute("_src") || ci.src || "";
23227 if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) {
23228 remoteImages.push(src);
23229 }
23230 }
23231
23232 if (remoteImages.length) {
23233 catchremoteimage(remoteImages, {
23234 //成功抓取
23235 success: function (r) {
23236 try {
23237 var info = r.state !== undefined ? r:eval("(" + r.responseText + ")");
23238 } catch (e) {
23239 return;
23240 }
23241
23242 /* 获取源路径和新路径 */
23243 var i, j, ci, cj, oldSrc, newSrc, list = info.list;
23244
23245 for (i = 0; ci = imgs[i++];) {
23246 oldSrc = ci.getAttribute("_src") || ci.src || "";
23247 for (j = 0; cj = list[j++];) {
23248 if (oldSrc == cj.source && cj.state == "SUCCESS") { //抓取失败时不做替换处理
23249 newSrc = catcherUrlPrefix + cj.url;
23250 domUtils.setAttributes(ci, {
23251 "src": newSrc,
23252 "_src": newSrc
23253 });
23254 break;
23255 }
23256 }
23257 }
23258 me.fireEvent('catchremotesuccess')
23259 },
23260 //回调失败,本次请求超时
23261 error: function () {
23262 me.fireEvent("catchremoteerror");
23263 }
23264 });
23265 }
23266
23267 function catchremoteimage(imgs, callbacks) {
23268 var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '',
23269 url = utils.formatUrl(catcherActionUrl + (catcherActionUrl.indexOf('?') == -1 ? '?':'&') + params),
23270 isJsonp = utils.isCrossDomainUrl(url),
23271 opt = {
23272 'method': 'POST',
23273 'dataType': isJsonp ? 'jsonp':'',
23274 'timeout': 60000, //单位:毫秒,回调请求超时设置。目标用户如果网速不是很快的话此处建议设置一个较大的数值
23275 'onsuccess': callbacks["success"],
23276 'onerror': callbacks["error"]
23277 };
23278 opt[catcherFieldName] = imgs;
23279 ajax.request(url, opt);
23280 }
23281
23282 });
23283};
23284
23285// plugins/snapscreen.js
23286/**
23287 * 截屏插件,为UEditor提供插入支持
23288 * @file
23289 * @since 1.4.2
23290 */
23291UE.plugin.register('snapscreen', function (){
23292
23293 var me = this;
23294 var snapplugin;
23295
23296 function getLocation(url){
23297 var search,
23298 a = document.createElement('a'),
23299 params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';
23300
23301 a.href = url;
23302 if (browser.ie) {
23303 a.href = a.href;
23304 }
23305
23306
23307 search = a.search;
23308 if (params) {
23309 search = search + (search.indexOf('?') == -1 ? '?':'&')+ params;
23310 search = search.replace(/[&]+/ig, '&');
23311 }
23312 return {
23313 'port': a.port,
23314 'hostname': a.hostname,
23315 'path': a.pathname + search || + a.hash
23316 }
23317 }
23318
23319 return {
23320 commands:{
23321 /**
23322 * 字体背景颜色
23323 * @command snapscreen
23324 * @method execCommand
23325 * @param { String } cmd 命令字符串
23326 * @example
23327 * ```javascript
23328 * editor.execCommand('snapscreen');
23329 * ```
23330 */
23331 'snapscreen':{
23332 execCommand:function (cmd) {
23333 var url, local, res;
23334 var lang = me.getLang("snapScreen_plugin");
23335
23336 if(!snapplugin){
23337 var container = me.container;
23338 var doc = me.container.ownerDocument || me.container.document;
23339 snapplugin = doc.createElement("object");
23340 try{snapplugin.type = "application/x-pluginbaidusnap";}catch(e){
23341 return;
23342 }
23343 snapplugin.style.cssText = "position:absolute;left:-9999px;width:0;height:0;";
23344 snapplugin.setAttribute("width","0");
23345 snapplugin.setAttribute("height","0");
23346 container.appendChild(snapplugin);
23347 }
23348
23349 function onSuccess(rs){
23350 try{
23351 rs = eval("("+ rs +")");
23352 if(rs.state == 'SUCCESS'){
23353 var opt = me.options;
23354 me.execCommand('insertimage', {
23355 src: opt.snapscreenUrlPrefix + rs.url,
23356 _src: opt.snapscreenUrlPrefix + rs.url,
23357 alt: rs.title || '',
23358 floatStyle: opt.snapscreenImgAlign
23359 });
23360 } else {
23361 alert(rs.state);
23362 }
23363 }catch(e){
23364 alert(lang.callBackErrorMsg);
23365 }
23366 }
23367 url = me.getActionUrl(me.getOpt('snapscreenActionName'));
23368 local = getLocation(url);
23369 setTimeout(function () {
23370 try{
23371 res =snapplugin.saveSnapshot(local.hostname, local.path, local.port);
23372 }catch(e){
23373 me.ui._dialogs['snapscreenDialog'].open();
23374 return;
23375 }
23376
23377 onSuccess(res);
23378 }, 50);
23379 },
23380 queryCommandState: function(){
23381 return (navigator.userAgent.indexOf("Windows",0) != -1) ? 0:-1;
23382 }
23383 }
23384 }
23385 }
23386});
23387
23388
23389// plugins/insertparagraph.js
23390/**
23391 * 插入段落
23392 * @file
23393 * @since 1.2.6.1
23394 */
23395
23396
23397/**
23398 * 插入段落
23399 * @command insertparagraph
23400 * @method execCommand
23401 * @param { String } cmd 命令字符串
23402 * @example
23403 * ```javascript
23404 * //editor是编辑器实例
23405 * editor.execCommand( 'insertparagraph' );
23406 * ```
23407 */
23408
23409UE.commands['insertparagraph'] = {
23410 execCommand : function( cmdName,front) {
23411 var me = this,
23412 range = me.selection.getRange(),
23413 start = range.startContainer,tmpNode;
23414 while(start ){
23415 if(domUtils.isBody(start)){
23416 break;
23417 }
23418 tmpNode = start;
23419 start = start.parentNode;
23420 }
23421 if(tmpNode){
23422 var p = me.document.createElement('p');
23423 if(front){
23424 tmpNode.parentNode.insertBefore(p,tmpNode)
23425 }else{
23426 tmpNode.parentNode.insertBefore(p,tmpNode.nextSibling)
23427 }
23428 domUtils.fillNode(me.document,p);
23429 range.setStart(p,0).setCursor(false,true);
23430 }
23431 }
23432};
23433
23434
23435
23436// plugins/webapp.js
23437/**
23438 * 百度应用
23439 * @file
23440 * @since 1.2.6.1
23441 */
23442
23443
23444/**
23445 * 插入百度应用
23446 * @command webapp
23447 * @method execCommand
23448 * @remind 需要百度APPKey
23449 * @remind 百度应用主页: <a href="http://app.baidu.com/" target="_blank">http://app.baidu.com/</a>
23450 * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度,
23451 * height=>应用容器高度,logo=>应用logo,url=>应用地址
23452 * @example
23453 * ```javascript
23454 * //editor是编辑器实例
23455 * //在编辑器里插入一个“植物大战僵尸”的APP
23456 * editor.execCommand( 'webapp' , {
23457 * title: '植物大战僵尸',
23458 * width: 560,
23459 * height: 465,
23460 * logo: '应用展示的图片',
23461 * url: '百度应用的地址'
23462 * } );
23463 * ```
23464 */
23465
23466//UE.plugins['webapp'] = function () {
23467// var me = this;
23468// function createInsertStr( obj, toIframe, addParagraph ) {
23469// return !toIframe ?
23470// (addParagraph ? '<p>' : '') + '<img title="'+obj.title+'" width="' + obj.width + '" height="' + obj.height + '"' +
23471// ' src="' + me.options.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif" style="background:url(' + obj.logo+') no-repeat center center; border:1px solid gray;" class="edui-faked-webapp" _url="' + obj.url + '" />' +
23472// (addParagraph ? '</p>' : '')
23473// :
23474// '<iframe class="edui-faked-webapp" title="'+obj.title+'" width="' + obj.width + '" height="' + obj.height + '" scrolling="no" frameborder="0" src="' + obj.url + '" logo_url = '+obj.logo+'></iframe>';
23475// }
23476//
23477// function switchImgAndIframe( img2frame ) {
23478// var tmpdiv,
23479// nodes = domUtils.getElementsByTagName( me.document, !img2frame ? "iframe" : "img" );
23480// for ( var i = 0, node; node = nodes[i++]; ) {
23481// if ( node.className != "edui-faked-webapp" ){
23482// continue;
23483// }
23484// tmpdiv = me.document.createElement( "div" );
23485// tmpdiv.innerHTML = createInsertStr( img2frame ? {url:node.getAttribute( "_url" ), width:node.width, height:node.height,title:node.title,logo:node.style.backgroundImage.replace("url(","").replace(")","")} : {url:node.getAttribute( "src", 2 ),title:node.title, width:node.width, height:node.height,logo:node.getAttribute("logo_url")}, img2frame ? true : false,false );
23486// node.parentNode.replaceChild( tmpdiv.firstChild, node );
23487// }
23488// }
23489//
23490// me.addListener( "beforegetcontent", function () {
23491// switchImgAndIframe( true );
23492// } );
23493// me.addListener( 'aftersetcontent', function () {
23494// switchImgAndIframe( false );
23495// } );
23496// me.addListener( 'aftergetcontent', function ( cmdName ) {
23497// if ( cmdName == 'aftergetcontent' && me.queryCommandState( 'source' ) ){
23498// return;
23499// }
23500// switchImgAndIframe( false );
23501// } );
23502//
23503// me.commands['webapp'] = {
23504// execCommand:function ( cmd, obj ) {
23505// me.execCommand( "inserthtml", createInsertStr( obj, false,true ) );
23506// }
23507// };
23508//};
23509
23510UE.plugin.register('webapp', function (){
23511 var me = this;
23512 function createInsertStr(obj,toEmbed){
23513 return !toEmbed ?
23514 '<img title="'+obj.title+'" width="' + obj.width + '" height="' + obj.height + '"' +
23515 ' src="' + me.options.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif" _logo_url="'+obj.logo+'" style="background:url(' + obj.logo
23516 +') no-repeat center center; border:1px solid gray;" class="edui-faked-webapp" _url="' + obj.url + '" ' +
23517 (obj.align && !obj.cssfloat? 'align="' + obj.align + '"' : '') +
23518 (obj.cssfloat ? 'style="float:' + obj.cssfloat + '"' : '') +
23519 '/>'
23520 :
23521 '<iframe class="edui-faked-webapp" title="'+obj.title+'" ' +
23522 (obj.align && !obj.cssfloat? 'align="' + obj.align + '"' : '') +
23523 (obj.cssfloat ? 'style="float:' + obj.cssfloat + '"' : '') +
23524 'width="' + obj.width + '" height="' + obj.height + '" scrolling="no" frameborder="0" src="' + obj.url + '" logo_url = "'+obj.logo+'"></iframe>'
23525
23526 }
23527 return {
23528 outputRule: function(root){
23529 utils.each(root.getNodesByTagName('img'),function(node){
23530 var html;
23531 if(node.getAttr('class') == 'edui-faked-webapp'){
23532 html = createInsertStr({
23533 title:node.getAttr('title'),
23534 'width':node.getAttr('width'),
23535 'height':node.getAttr('height'),
23536 'align':node.getAttr('align'),
23537 'cssfloat':node.getStyle('float'),
23538 'url':node.getAttr("_url"),
23539 'logo':node.getAttr('_logo_url')
23540 },true);
23541 var embed = UE.uNode.createElement(html);
23542 node.parentNode.replaceChild(embed,node);
23543 }
23544 })
23545 },
23546 inputRule:function(root){
23547 utils.each(root.getNodesByTagName('iframe'),function(node){
23548 if(node.getAttr('class') == 'edui-faked-webapp'){
23549 var img = UE.uNode.createElement(createInsertStr({
23550 title:node.getAttr('title'),
23551 'width':node.getAttr('width'),
23552 'height':node.getAttr('height'),
23553 'align':node.getAttr('align'),
23554 'cssfloat':node.getStyle('float'),
23555 'url':node.getAttr("src"),
23556 'logo':node.getAttr('logo_url')
23557 }));
23558 node.parentNode.replaceChild(img,node);
23559 }
23560 })
23561
23562 },
23563 commands:{
23564 /**
23565 * 插入百度应用
23566 * @command webapp
23567 * @method execCommand
23568 * @remind 需要百度APPKey
23569 * @remind 百度应用主页: <a href="http://app.baidu.com/" target="_blank">http://app.baidu.com/</a>
23570 * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度,
23571 * height=>应用容器高度,logo=>应用logo,url=>应用地址
23572 * @example
23573 * ```javascript
23574 * //editor是编辑器实例
23575 * //在编辑器里插入一个“植物大战僵尸”的APP
23576 * editor.execCommand( 'webapp' , {
23577 * title: '植物大战僵尸',
23578 * width: 560,
23579 * height: 465,
23580 * logo: '应用展示的图片',
23581 * url: '百度应用的地址'
23582 * } );
23583 * ```
23584 */
23585 'webapp':{
23586 execCommand:function (cmd, obj) {
23587
23588 var me = this,
23589 str = createInsertStr(utils.extend(obj,{
23590 align:'none'
23591 }), false);
23592 me.execCommand("inserthtml",str);
23593 },
23594 queryCommandState:function () {
23595 var me = this,
23596 img = me.selection.getRange().getClosedNode(),
23597 flag = img && (img.className == "edui-faked-webapp");
23598 return flag ? 1 : 0;
23599 }
23600 }
23601 }
23602 }
23603});
23604
23605// plugins/template.js
23606///import core
23607///import plugins\inserthtml.js
23608///import plugins\cleardoc.js
23609///commands 模板
23610///commandsName template
23611///commandsTitle 模板
23612///commandsDialog dialogs\template
23613UE.plugins['template'] = function () {
23614 UE.commands['template'] = {
23615 execCommand:function (cmd, obj) {
23616 obj.html && this.execCommand("inserthtml", obj.html);
23617 }
23618 };
23619 this.addListener("click", function (type, evt) {
23620 var el = evt.target || evt.srcElement,
23621 range = this.selection.getRange();
23622 var tnode = domUtils.findParent(el, function (node) {
23623 if (node.className && domUtils.hasClass(node, "ue_t")) {
23624 return node;
23625 }
23626 }, true);
23627 tnode && range.selectNode(tnode).shrinkBoundary().select();
23628 });
23629 this.addListener("keydown", function (type, evt) {
23630 var range = this.selection.getRange();
23631 if (!range.collapsed) {
23632 if (!evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
23633 var tnode = domUtils.findParent(range.startContainer, function (node) {
23634 if (node.className && domUtils.hasClass(node, "ue_t")) {
23635 return node;
23636 }
23637 }, true);
23638 if (tnode) {
23639 domUtils.removeClasses(tnode, ["ue_t"]);
23640 }
23641 }
23642 }
23643 });
23644};
23645
23646
23647// plugins/music.js
23648/**
23649 * 插入音乐命令
23650 * @file
23651 */
23652UE.plugin.register('music', function (){
23653 var me = this;
23654 function creatInsertStr(url,width,height,align,cssfloat,toEmbed){
23655 return !toEmbed ?
23656 '<img ' +
23657 (align && !cssfloat? 'align="' + align + '"' : '') +
23658 (cssfloat ? 'style="float:' + cssfloat + '"' : '') +
23659 ' width="'+ width +'" height="' + height + '" _url="'+url+'" class="edui-faked-music"' +
23660 ' src="'+me.options.langPath+me.options.lang+'/images/music.png" />'
23661 :
23662 '<embed type="application/x-shockwave-flash" class="edui-faked-music" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
23663 ' src="' + url + '" width="' + width + '" height="' + height + '" '+ (align && !cssfloat? 'align="' + align + '"' : '') +
23664 (cssfloat ? 'style="float:' + cssfloat + '"' : '') +
23665 ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
23666 }
23667 return {
23668 outputRule: function(root){
23669 utils.each(root.getNodesByTagName('img'),function(node){
23670 var html;
23671 if(node.getAttr('class') == 'edui-faked-music'){
23672 var cssfloat = node.getStyle('float');
23673 var align = node.getAttr('align');
23674 html = creatInsertStr(node.getAttr("_url"), node.getAttr('width'), node.getAttr('height'), align, cssfloat, true);
23675 var embed = UE.uNode.createElement(html);
23676 node.parentNode.replaceChild(embed,node);
23677 }
23678 })
23679 },
23680 inputRule:function(root){
23681 utils.each(root.getNodesByTagName('embed'),function(node){
23682 if(node.getAttr('class') == 'edui-faked-music'){
23683 var cssfloat = node.getStyle('float');
23684 var align = node.getAttr('align');
23685 html = creatInsertStr(node.getAttr("src"), node.getAttr('width'), node.getAttr('height'), align, cssfloat,false);
23686 var img = UE.uNode.createElement(html);
23687 node.parentNode.replaceChild(img,node);
23688 }
23689 })
23690
23691 },
23692 commands:{
23693 /**
23694 * 插入音乐
23695 * @command music
23696 * @method execCommand
23697 * @param { Object } musicOptions 插入音乐的参数项, 支持的key有: url=>音乐地址;
23698 * width=>音乐容器宽度;height=>音乐容器高度;align=>音乐文件的对齐方式, 可选值有: left, center, right, none
23699 * @example
23700 * ```javascript
23701 * //editor是编辑器实例
23702 * //在编辑器里插入一个“植物大战僵尸”的APP
23703 * editor.execCommand( 'music' , {
23704 * width: 400,
23705 * height: 95,
23706 * align: "center",
23707 * url: "音乐地址"
23708 * } );
23709 * ```
23710 */
23711 'music':{
23712 execCommand:function (cmd, musicObj) {
23713 var me = this,
23714 str = creatInsertStr(musicObj.url, musicObj.width || 400, musicObj.height || 95, "none", false);
23715 me.execCommand("inserthtml",str);
23716 },
23717 queryCommandState:function () {
23718 var me = this,
23719 img = me.selection.getRange().getClosedNode(),
23720 flag = img && (img.className == "edui-faked-music");
23721 return flag ? 1 : 0;
23722 }
23723 }
23724 }
23725 }
23726});
23727
23728// plugins/autoupload.js
23729/**
23730 * @description
23731 * 1.拖放文件到编辑区域,自动上传并插入到选区
23732 * 2.插入粘贴板的图片,自动上传并插入到选区
23733 * @author Jinqn
23734 * @date 2013-10-14
23735 */
23736UE.plugin.register('autoupload', function (){
23737
23738 function sendAndInsertFile(file, editor) {
23739 var me = editor;
23740 //模拟数据
23741 var fieldName, urlPrefix, maxSize, allowFiles, actionUrl,
23742 loadingHtml, errorHandler, successHandler,
23743 filetype = /image\/\w+/i.test(file.type) ? 'image':'file',
23744 loadingId = 'loading_' + (+new Date()).toString(36);
23745
23746 fieldName = me.getOpt(filetype + 'FieldName');
23747 urlPrefix = me.getOpt(filetype + 'UrlPrefix');
23748 maxSize = me.getOpt(filetype + 'MaxSize');
23749 allowFiles = me.getOpt(filetype + 'AllowFiles');
23750 actionUrl = me.getActionUrl(me.getOpt(filetype + 'ActionName'));
23751 errorHandler = function(title) {
23752 var loader = me.document.getElementById(loadingId);
23753 loader && domUtils.remove(loader);
23754 me.fireEvent('showmessage', {
23755 'id': loadingId,
23756 'content': title,
23757 'type': 'error',
23758 'timeout': 4000
23759 });
23760 };
23761
23762 if (filetype == 'image') {
23763 loadingHtml = '<img class="loadingclass" id="' + loadingId + '" src="' +
23764 me.options.themePath + me.options.theme +
23765 '/images/spacer.gif" title="' + (me.getLang('autoupload.loading') || '') + '" >';
23766 successHandler = function(data) {
23767 var link = urlPrefix + data.url,
23768 loader = me.document.getElementById(loadingId);
23769 if (loader) {
23770 loader.setAttribute('src', link);
23771 loader.setAttribute('_src', link);
23772 loader.setAttribute('title', data.title || '');
23773 loader.setAttribute('alt', data.original || '');
23774 loader.removeAttribute('id');
23775 domUtils.removeClasses(loader, 'loadingclass');
23776 }
23777 };
23778 } else {
23779 loadingHtml = '<p>' +
23780 '<img class="loadingclass" id="' + loadingId + '" src="' +
23781 me.options.themePath + me.options.theme +
23782 '/images/spacer.gif" title="' + (me.getLang('autoupload.loading') || '') + '" >' +
23783 '</p>';
23784 successHandler = function(data) {
23785 var link = urlPrefix + data.url,
23786 loader = me.document.getElementById(loadingId);
23787
23788 var rng = me.selection.getRange(),
23789 bk = rng.createBookmark();
23790 rng.selectNode(loader).select();
23791 me.execCommand('insertfile', {'url': link});
23792 rng.moveToBookmark(bk).select();
23793 };
23794 }
23795
23796 /* 插入loading的占位符 */
23797 me.execCommand('inserthtml', loadingHtml);
23798
23799 /* 判断后端配置是否没有加载成功 */
23800 if (!me.getOpt(filetype + 'ActionName')) {
23801 errorHandler(me.getLang('autoupload.errorLoadConfig'));
23802 return;
23803 }
23804 /* 判断文件大小是否超出限制 */
23805 if(file.size > maxSize) {
23806 errorHandler(me.getLang('autoupload.exceedSizeError'));
23807 return;
23808 }
23809 /* 判断文件格式是否超出允许 */
23810 var fileext = file.name ? file.name.substr(file.name.lastIndexOf('.')):'';
23811 if ((fileext && filetype != 'image') || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
23812 errorHandler(me.getLang('autoupload.exceedTypeError'));
23813 return;
23814 }
23815
23816 /* 创建Ajax并提交 */
23817 var xhr = new XMLHttpRequest(),
23818 fd = new FormData(),
23819 params = utils.serializeParam(me.queryCommandValue('serverparam')) || '',
23820 url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + params);
23821
23822 fd.append(fieldName, file, file.name || ('blob.' + file.type.substr('image/'.length)));
23823 fd.append('type', 'ajax');
23824 xhr.open("post", url, true);
23825 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
23826 xhr.addEventListener('load', function (e) {
23827 try{
23828 var json = (new Function("return " + utils.trim(e.target.response)))();
23829 if (json.state == 'SUCCESS' && json.url) {
23830 successHandler(json);
23831 } else {
23832 errorHandler(json.state);
23833 }
23834 }catch(er){
23835 errorHandler(me.getLang('autoupload.loadError'));
23836 }
23837 });
23838 xhr.send(fd);
23839 }
23840
23841 function getPasteImage(e){
23842 return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items:null;
23843 }
23844 function getDropImage(e){
23845 return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files:null;
23846 }
23847
23848 return {
23849 outputRule: function(root){
23850 utils.each(root.getNodesByTagName('img'),function(n){
23851 if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
23852 n.parentNode.removeChild(n);
23853 }
23854 });
23855 utils.each(root.getNodesByTagName('p'),function(n){
23856 if (/\bloadpara\b/.test(n.getAttr('class'))) {
23857 n.parentNode.removeChild(n);
23858 }
23859 });
23860 },
23861 bindEvents:{
23862 //插入粘贴板的图片,拖放插入图片
23863 'ready':function(e){
23864 var me = this;
23865 if(window.FormData && window.FileReader) {
23866 domUtils.on(me.body, 'paste drop', function(e){
23867 var hasImg = false,
23868 items;
23869 //获取粘贴板文件列表或者拖放文件列表
23870 items = e.type == 'paste' ? getPasteImage(e):getDropImage(e);
23871 if(items){
23872 var len = items.length,
23873 file;
23874 while (len--){
23875 file = items[len];
23876 if(file.getAsFile) file = file.getAsFile();
23877 if(file && file.size > 0) {
23878 sendAndInsertFile(file, me);
23879 hasImg = true;
23880 }
23881 }
23882 hasImg && e.preventDefault();
23883 }
23884
23885 });
23886 //取消拖放图片时出现的文字光标位置提示
23887 domUtils.on(me.body, 'dragover', function (e) {
23888 if(e.dataTransfer.types[0] == 'Files') {
23889 e.preventDefault();
23890 }
23891 });
23892
23893 //设置loading的样式
23894 utils.cssRule('loading',
23895 '.loadingclass{display:inline-block;cursor:default;background: url(\''
23896 + this.options.themePath
23897 + this.options.theme +'/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-left:1px;height: 22px;width: 22px;}\n' +
23898 '.loaderrorclass{display:inline-block;cursor:default;background: url(\''
23899 + this.options.themePath
23900 + this.options.theme +'/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
23901 '}',
23902 this.document);
23903 }
23904 }
23905 }
23906 }
23907});
23908
23909// plugins/autosave.js
23910UE.plugin.register('autosave', function (){
23911
23912 var me = this,
23913 //无限循环保护
23914 lastSaveTime = new Date(),
23915 //最小保存间隔时间
23916 MIN_TIME = 20,
23917 //auto save key
23918 saveKey = null;
23919
23920 function save ( editor ) {
23921
23922 var saveData;
23923
23924 if ( new Date() - lastSaveTime < MIN_TIME ) {
23925 return;
23926 }
23927
23928 if ( !editor.hasContents() ) {
23929 //这里不能调用命令来删除, 会造成事件死循环
23930 saveKey && me.removePreferences( saveKey );
23931 return;
23932 }
23933
23934 lastSaveTime = new Date();
23935
23936 editor._saveFlag = null;
23937
23938 saveData = me.body.innerHTML;
23939
23940 if ( editor.fireEvent( "beforeautosave", {
23941 content: saveData
23942 } ) === false ) {
23943 return;
23944 }
23945
23946 me.setPreferences( saveKey, saveData );
23947
23948 editor.fireEvent( "afterautosave", {
23949 content: saveData
23950 } );
23951
23952 }
23953
23954 return {
23955 defaultOptions: {
23956 //默认间隔时间
23957 saveInterval: 500
23958 },
23959 bindEvents:{
23960 'ready':function(){
23961
23962 var _suffix = "-drafts-data",
23963 key = null;
23964
23965 if ( me.key ) {
23966 key = me.key + _suffix;
23967 } else {
23968 key = ( me.container.parentNode.id || 'ue-common' ) + _suffix;
23969 }
23970
23971 //页面地址+编辑器ID 保持唯一
23972 saveKey = ( location.protocol + location.host + location.pathname ).replace( /[.:\/]/g, '_' ) + key;
23973
23974 },
23975
23976 'contentchange': function () {
23977
23978 if ( !saveKey ) {
23979 return;
23980 }
23981
23982 if ( me._saveFlag ) {
23983 window.clearTimeout( me._saveFlag );
23984 }
23985
23986 if ( me.options.saveInterval > 0 ) {
23987
23988 me._saveFlag = window.setTimeout( function () {
23989
23990 save( me );
23991
23992 }, me.options.saveInterval );
23993
23994 } else {
23995
23996 save(me);
23997
23998 }
23999
24000
24001 }
24002 },
24003 commands:{
24004 'clearlocaldata':{
24005 execCommand:function (cmd, name) {
24006 if ( saveKey && me.getPreferences( saveKey ) ) {
24007 me.removePreferences( saveKey )
24008 }
24009 },
24010 notNeedUndo: true,
24011 ignoreContentChange:true
24012 },
24013
24014 'getlocaldata':{
24015 execCommand:function (cmd, name) {
24016 return saveKey ? me.getPreferences( saveKey ) || '' : '';
24017 },
24018 notNeedUndo: true,
24019 ignoreContentChange:true
24020 },
24021
24022 'drafts':{
24023 execCommand:function (cmd, name) {
24024 if ( saveKey ) {
24025 me.body.innerHTML = me.getPreferences( saveKey ) || '<p>'+domUtils.fillHtml+'</p>';
24026 me.focus(true);
24027 }
24028 },
24029 queryCommandState: function () {
24030 return saveKey ? ( me.getPreferences( saveKey ) === null ? -1 : 0 ) : -1;
24031 },
24032 notNeedUndo: true,
24033 ignoreContentChange:true
24034 }
24035 }
24036 }
24037
24038});
24039
24040// plugins/charts.js
24041UE.plugin.register('charts', function (){
24042
24043 var me = this;
24044
24045 return {
24046 bindEvents: {
24047 'chartserror': function () {
24048 }
24049 },
24050 commands:{
24051 'charts': {
24052 execCommand: function ( cmd, data ) {
24053
24054 var tableNode = domUtils.findParentByTagName(this.selection.getRange().startContainer, 'table', true),
24055 flagText = [],
24056 config = {};
24057
24058 if ( !tableNode ) {
24059 return false;
24060 }
24061
24062 if ( !validData( tableNode ) ) {
24063 me.fireEvent( "chartserror" );
24064 return false;
24065 }
24066
24067 config.title = data.title || '';
24068 config.subTitle = data.subTitle || '';
24069 config.xTitle = data.xTitle || '';
24070 config.yTitle = data.yTitle || '';
24071 config.suffix = data.suffix || '';
24072 config.tip = data.tip || '';
24073 //数据对齐方式
24074 config.dataFormat = data.tableDataFormat || '';
24075 //图表类型
24076 config.chartType = data.chartType || 0;
24077
24078 for ( var key in config ) {
24079
24080 if ( !config.hasOwnProperty( key ) ) {
24081 continue;
24082 }
24083
24084 flagText.push( key+":"+config[ key ] );
24085
24086 }
24087
24088 tableNode.setAttribute( "data-chart", flagText.join( ";" ) );
24089 domUtils.addClass( tableNode, "edui-charts-table" );
24090
24091
24092
24093 },
24094 queryCommandState: function ( cmd, name ) {
24095
24096 var tableNode = domUtils.findParentByTagName(this.selection.getRange().startContainer, 'table', true);
24097 return tableNode && validData( tableNode ) ? 0 : -1;
24098
24099 }
24100 }
24101 },
24102 inputRule:function(root){
24103 utils.each(root.getNodesByTagName('table'),function( tableNode ){
24104
24105 if ( tableNode.getAttr("data-chart") !== undefined ) {
24106 tableNode.setAttr("style");
24107 }
24108
24109 })
24110
24111 },
24112 outputRule:function(root){
24113 utils.each(root.getNodesByTagName('table'),function( tableNode ){
24114
24115 if ( tableNode.getAttr("data-chart") !== undefined ) {
24116 tableNode.setAttr("style", "display: none;");
24117 }
24118
24119 })
24120
24121 }
24122 }
24123
24124 function validData ( table ) {
24125
24126 var firstRows = null,
24127 cellCount = 0;
24128
24129 //行数不够
24130 if ( table.rows.length < 2 ) {
24131 return false;
24132 }
24133
24134 //列数不够
24135 if ( table.rows[0].cells.length < 2 ) {
24136 return false;
24137 }
24138
24139 //第一行所有cell必须是th
24140 firstRows = table.rows[ 0 ].cells;
24141 cellCount = firstRows.length;
24142
24143 for ( var i = 0, cell; cell = firstRows[ i ]; i++ ) {
24144
24145 if ( cell.tagName.toLowerCase() !== 'th' ) {
24146 return false;
24147 }
24148
24149 }
24150
24151 for ( var i = 1, row; row = table.rows[ i ]; i++ ) {
24152
24153 //每行单元格数不匹配, 返回false
24154 if ( row.cells.length != cellCount ) {
24155 return false;
24156 }
24157
24158 //第一列不是th也返回false
24159 if ( row.cells[0].tagName.toLowerCase() !== 'th' ) {
24160 return false;
24161 }
24162
24163 for ( var j = 1, cell; cell = row.cells[ j ]; j++ ) {
24164
24165 var value = utils.trim( ( cell.innerText || cell.textContent || '' ) );
24166
24167 value = value.replace( new RegExp( UE.dom.domUtils.fillChar, 'g' ), '' ).replace( /^\s+|\s+$/g, '' );
24168
24169 //必须是数字
24170 if ( !/^\d*\.?\d+$/.test( value ) ) {
24171 return false;
24172 }
24173
24174 }
24175
24176 }
24177
24178 return true;
24179
24180 }
24181
24182});
24183
24184// plugins/section.js
24185/**
24186 * 目录大纲支持插件
24187 * @file
24188 * @since 1.3.0
24189 */
24190UE.plugin.register('section', function (){
24191 /* 目录节点对象 */
24192 function Section(option){
24193 this.tag = '';
24194 this.level = -1,
24195 this.dom = null;
24196 this.nextSection = null;
24197 this.previousSection = null;
24198 this.parentSection = null;
24199 this.startAddress = [];
24200 this.endAddress = [];
24201 this.children = [];
24202 }
24203 function getSection(option) {
24204 var section = new Section();
24205 return utils.extend(section, option);
24206 }
24207 function getNodeFromAddress(startAddress, root) {
24208 var current = root;
24209 for(var i = 0;i < startAddress.length; i++) {
24210 if(!current.childNodes) return null;
24211 current = current.childNodes[startAddress[i]];
24212 }
24213 return current;
24214 }
24215
24216 var me = this;
24217
24218 return {
24219 bindMultiEvents:{
24220 type: 'aftersetcontent afterscencerestore',
24221 handler: function(){
24222 me.fireEvent('updateSections');
24223 }
24224 },
24225 bindEvents:{
24226 /* 初始化、拖拽、粘贴、执行setcontent之后 */
24227 'ready': function (){
24228 me.fireEvent('updateSections');
24229 domUtils.on(me.body, 'drop paste', function(){
24230 me.fireEvent('updateSections');
24231 });
24232 },
24233 /* 执行paragraph命令之后 */
24234 'afterexeccommand': function (type, cmd) {
24235 if(cmd == 'paragraph') {
24236 me.fireEvent('updateSections');
24237 }
24238 },
24239 /* 部分键盘操作,触发updateSections事件 */
24240 'keyup': function (type, e) {
24241 var me = this,
24242 range = me.selection.getRange();
24243 if(range.collapsed != true) {
24244 me.fireEvent('updateSections');
24245 } else {
24246 var keyCode = e.keyCode || e.which;
24247 if(keyCode == 13 || keyCode == 8 || keyCode == 46) {
24248 me.fireEvent('updateSections');
24249 }
24250 }
24251 }
24252 },
24253 commands:{
24254 'getsections': {
24255 execCommand: function (cmd, levels) {
24256 var levelFn = levels || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
24257
24258 for (var i = 0; i < levelFn.length; i++) {
24259 if (typeof levelFn[i] == 'string') {
24260 levelFn[i] = function(fn){
24261 return function(node){
24262 return node.tagName == fn.toUpperCase()
24263 };
24264 }(levelFn[i]);
24265 } else if (typeof levelFn[i] != 'function') {
24266 levelFn[i] = function (node) {
24267 return null;
24268 }
24269 }
24270 }
24271 function getSectionLevel(node) {
24272 for (var i = 0; i < levelFn.length; i++) {
24273 if (levelFn[i](node)) return i;
24274 }
24275 return -1;
24276 }
24277
24278 var me = this,
24279 Directory = getSection({'level':-1, 'title':'root'}),
24280 previous = Directory;
24281
24282 function traversal(node, Directory) {
24283 var level,
24284 tmpSection = null,
24285 parent,
24286 child,
24287 children = node.childNodes;
24288 for (var i = 0, len = children.length; i < len; i++) {
24289 child = children[i];
24290 level = getSectionLevel(child);
24291 if (level >= 0) {
24292 var address = me.selection.getRange().selectNode(child).createAddress(true).startAddress,
24293 current = getSection({
24294 'tag': child.tagName,
24295 'title': child.innerText || child.textContent || '',
24296 'level': level,
24297 'dom': child,
24298 'startAddress': utils.clone(address, []),
24299 'endAddress': utils.clone(address, []),
24300 'children': []
24301 });
24302 previous.nextSection = current;
24303 current.previousSection = previous;
24304 parent = previous;
24305 while(level <= parent.level){
24306 parent = parent.parentSection;
24307 }
24308 current.parentSection = parent;
24309 parent.children.push(current);
24310 tmpSection = previous = current;
24311 } else {
24312 child.nodeType === 1 && traversal(child, Directory);
24313 tmpSection && tmpSection.endAddress[tmpSection.endAddress.length - 1] ++;
24314 }
24315 }
24316 }
24317 traversal(me.body, Directory);
24318 return Directory;
24319 },
24320 notNeedUndo: true
24321 },
24322 'movesection': {
24323 execCommand: function (cmd, sourceSection, targetSection, isAfter) {
24324
24325 var me = this,
24326 targetAddress,
24327 target;
24328
24329 if(!sourceSection || !targetSection || targetSection.level == -1) return;
24330
24331 targetAddress = isAfter ? targetSection.endAddress:targetSection.startAddress;
24332 target = getNodeFromAddress(targetAddress, me.body);
24333
24334 /* 判断目标地址是否被源章节包含 */
24335 if(!targetAddress || !target || isContainsAddress(sourceSection.startAddress, sourceSection.endAddress, targetAddress)) return;
24336
24337 var startNode = getNodeFromAddress(sourceSection.startAddress, me.body),
24338 endNode = getNodeFromAddress(sourceSection.endAddress, me.body),
24339 current,
24340 nextNode;
24341
24342 if(isAfter) {
24343 current = endNode;
24344 while ( current && !(domUtils.getPosition( startNode, current ) & domUtils.POSITION_FOLLOWING) ) {
24345 nextNode = current.previousSibling;
24346 domUtils.insertAfter(target, current);
24347 if(current == startNode) break;
24348 current = nextNode;
24349 }
24350 } else {
24351 current = startNode;
24352 while ( current && !(domUtils.getPosition( current, endNode ) & domUtils.POSITION_FOLLOWING) ) {
24353 nextNode = current.nextSibling;
24354 target.parentNode.insertBefore(current, target);
24355 if(current == endNode) break;
24356 current = nextNode;
24357 }
24358 }
24359
24360 me.fireEvent('updateSections');
24361
24362 /* 获取地址的包含关系 */
24363 function isContainsAddress(startAddress, endAddress, addressTarget){
24364 var isAfterStartAddress = false,
24365 isBeforeEndAddress = false;
24366 for(var i = 0; i< startAddress.length; i++){
24367 if(i >= addressTarget.length) break;
24368 if(addressTarget[i] > startAddress[i]) {
24369 isAfterStartAddress = true;
24370 break;
24371 } else if(addressTarget[i] < startAddress[i]) {
24372 break;
24373 }
24374 }
24375 for(var i = 0; i< endAddress.length; i++){
24376 if(i >= addressTarget.length) break;
24377 if(addressTarget[i] < startAddress[i]) {
24378 isBeforeEndAddress = true;
24379 break;
24380 } else if(addressTarget[i] > startAddress[i]) {
24381 break;
24382 }
24383 }
24384 return isAfterStartAddress && isBeforeEndAddress;
24385 }
24386 }
24387 },
24388 'deletesection': {
24389 execCommand: function (cmd, section, keepChildren) {
24390 var me = this;
24391
24392 if(!section) return;
24393
24394 function getNodeFromAddress(startAddress) {
24395 var current = me.body;
24396 for(var i = 0;i < startAddress.length; i++) {
24397 if(!current.childNodes) return null;
24398 current = current.childNodes[startAddress[i]];
24399 }
24400 return current;
24401 }
24402
24403 var startNode = getNodeFromAddress(section.startAddress),
24404 endNode = getNodeFromAddress(section.endAddress),
24405 current = startNode,
24406 nextNode;
24407
24408 if(!keepChildren) {
24409 while ( current && domUtils.inDoc(endNode, me.document) && !(domUtils.getPosition( current, endNode ) & domUtils.POSITION_FOLLOWING) ) {
24410 nextNode = current.nextSibling;
24411 domUtils.remove(current);
24412 current = nextNode;
24413 }
24414 } else {
24415 domUtils.remove(current);
24416 }
24417
24418 me.fireEvent('updateSections');
24419 }
24420 },
24421 'selectsection': {
24422 execCommand: function (cmd, section) {
24423 if(!section && !section.dom) return false;
24424 var me = this,
24425 range = me.selection.getRange(),
24426 address = {
24427 'startAddress':utils.clone(section.startAddress, []),
24428 'endAddress':utils.clone(section.endAddress, [])
24429 };
24430 address.endAddress[address.endAddress.length - 1]++;
24431 range.moveToAddress(address).select().scrollToView();
24432 return true;
24433 },
24434 notNeedUndo: true
24435 },
24436 'scrolltosection': {
24437 execCommand: function (cmd, section) {
24438 if(!section && !section.dom) return false;
24439 var me = this,
24440 range = me.selection.getRange(),
24441 address = {
24442 'startAddress':section.startAddress,
24443 'endAddress':section.endAddress
24444 };
24445 address.endAddress[address.endAddress.length - 1]++;
24446 range.moveToAddress(address).scrollToView();
24447 return true;
24448 },
24449 notNeedUndo: true
24450 }
24451 }
24452 }
24453});
24454
24455// plugins/simpleupload.js
24456/**
24457 * @description
24458 * 简单上传:点击按钮,直接选择文件上传
24459 * @author Jinqn
24460 * @date 2014-03-31
24461 */
24462UE.plugin.register('simpleupload', function (){
24463 var me = this,
24464 isLoaded = false,
24465 containerBtn;
24466
24467 function initUploadBtn(){
24468 var w = containerBtn.offsetWidth || 20,
24469 h = containerBtn.offsetHeight || 20,
24470 btnIframe = document.createElement('iframe'),
24471 btnStyle = 'display:block;width:' + w + 'px;height:' + h + 'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;cursor:pointer;';
24472
24473 domUtils.on(btnIframe, 'load', function(){
24474
24475 var timestrap = (+new Date()).toString(36),
24476 wrapper,
24477 btnIframeDoc,
24478 btnIframeBody;
24479
24480 btnIframeDoc = (btnIframe.contentDocument || btnIframe.contentWindow.document);
24481 btnIframeBody = btnIframeDoc.body;
24482 wrapper = btnIframeDoc.createElement('div');
24483
24484 wrapper.innerHTML = '<form id="edui_form_' + timestrap + '" target="edui_iframe_' + timestrap + '" method="POST" enctype="multipart/form-data" action="' + me.getOpt('serverUrl') + '" ' +
24485 'style="' + btnStyle + '">' +
24486 '<input id="edui_input_' + timestrap + '" type="file" accept="image/*" name="' + me.options.imageFieldName + '" ' +
24487 'style="' + btnStyle + '">' +
24488 '</form>' +
24489 '<iframe id="edui_iframe_' + timestrap + '" name="edui_iframe_' + timestrap + '" style="display:none;width:0;height:0;border:0;margin:0;padding:0;position:absolute;"></iframe>';
24490
24491 wrapper.className = 'edui-' + me.options.theme;
24492 wrapper.id = me.ui.id + '_iframeupload';
24493 btnIframeBody.style.cssText = btnStyle;
24494 btnIframeBody.style.width = w + 'px';
24495 btnIframeBody.style.height = h + 'px';
24496 btnIframeBody.appendChild(wrapper);
24497
24498 if (btnIframeBody.parentNode) {
24499 btnIframeBody.parentNode.style.width = w + 'px';
24500 btnIframeBody.parentNode.style.height = w + 'px';
24501 }
24502
24503 var form = btnIframeDoc.getElementById('edui_form_' + timestrap);
24504 var input = btnIframeDoc.getElementById('edui_input_' + timestrap);
24505 var iframe = btnIframeDoc.getElementById('edui_iframe_' + timestrap);
24506
24507 domUtils.on(input, 'change', function(){
24508 if(!input.value) return;
24509 var loadingId = 'loading_' + (+new Date()).toString(36);
24510 var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';
24511
24512 var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'));
24513 var allowFiles = me.getOpt('imageAllowFiles');
24514
24515 me.focus();
24516 me.execCommand('inserthtml', '<img class="loadingclass" id="' + loadingId + '" src="' + me.options.themePath + me.options.theme +'/images/spacer.gif" title="' + (me.getLang('simpleupload.loading') || '') + '" >');
24517
24518 function callback(){
24519 try{
24520 var link, json, loader,
24521 body = (iframe.contentDocument || iframe.contentWindow.document).body,
24522 result = body.innerText || body.textContent || '';
24523 json = (new Function("return " + result))();
24524 link = me.options.imageUrlPrefix + json.url;
24525 if(json.state == 'SUCCESS' && json.url) {
24526 loader = me.document.getElementById(loadingId);
24527 loader.setAttribute('src', link);
24528 loader.setAttribute('_src', link);
24529 loader.setAttribute('title', json.title || '');
24530 loader.setAttribute('alt', json.original || '');
24531 loader.removeAttribute('id');
24532 domUtils.removeClasses(loader, 'loadingclass');
24533 } else {
24534 showErrorLoader && showErrorLoader(json.state);
24535 }
24536 }catch(er){
24537 showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
24538 }
24539 form.reset();
24540 domUtils.un(iframe, 'load', callback);
24541 }
24542 function showErrorLoader(title){
24543 if(loadingId) {
24544 var loader = me.document.getElementById(loadingId);
24545 loader && domUtils.remove(loader);
24546 me.fireEvent('showmessage', {
24547 'id': loadingId,
24548 'content': title,
24549 'type': 'error',
24550 'timeout': 4000
24551 });
24552 }
24553 }
24554
24555 /* 判断后端配置是否没有加载成功 */
24556 if (!me.getOpt('imageActionName')) {
24557 errorHandler(me.getLang('autoupload.errorLoadConfig'));
24558 return;
24559 }
24560 // 判断文件格式是否错误
24561 var filename = input.value,
24562 fileext = filename ? filename.substr(filename.lastIndexOf('.')):'';
24563 if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
24564 showErrorLoader(me.getLang('simpleupload.exceedTypeError'));
24565 return;
24566 }
24567
24568 domUtils.on(iframe, 'load', callback);
24569 form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?':'&') + params);
24570 form.submit();
24571 });
24572
24573 var stateTimer;
24574 me.addListener('selectionchange', function () {
24575 clearTimeout(stateTimer);
24576 stateTimer = setTimeout(function() {
24577 var state = me.queryCommandState('simpleupload');
24578 if (state == -1) {
24579 input.disabled = 'disabled';
24580 } else {
24581 input.disabled = false;
24582 }
24583 }, 400);
24584 });
24585 isLoaded = true;
24586 });
24587
24588 btnIframe.style.cssText = btnStyle;
24589 containerBtn.appendChild(btnIframe);
24590 }
24591
24592 return {
24593 bindEvents:{
24594 'ready': function() {
24595 //设置loading的样式
24596 utils.cssRule('loading',
24597 '.loadingclass{display:inline-block;cursor:default;background: url(\''
24598 + this.options.themePath
24599 + this.options.theme +'/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n' +
24600 '.loaderrorclass{display:inline-block;cursor:default;background: url(\''
24601 + this.options.themePath
24602 + this.options.theme +'/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
24603 '}',
24604 this.document);
24605 },
24606 /* 初始化简单上传按钮 */
24607 'simpleuploadbtnready': function(type, container) {
24608 containerBtn = container;
24609 me.afterConfigReady(initUploadBtn);
24610 }
24611 },
24612 outputRule: function(root){
24613 utils.each(root.getNodesByTagName('img'),function(n){
24614 if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
24615 n.parentNode.removeChild(n);
24616 }
24617 });
24618 },
24619 commands: {
24620 'simpleupload': {
24621 queryCommandState: function () {
24622 return isLoaded ? 0:-1;
24623 }
24624 }
24625 }
24626 }
24627});
24628
24629// plugins/serverparam.js
24630/**
24631 * 服务器提交的额外参数列表设置插件
24632 * @file
24633 * @since 1.2.6.1
24634 */
24635UE.plugin.register('serverparam', function (){
24636
24637 var me = this,
24638 serverParam = {};
24639
24640 return {
24641 commands:{
24642 /**
24643 * 修改服务器提交的额外参数列表,清除所有项
24644 * @command serverparam
24645 * @method execCommand
24646 * @param { String } cmd 命令字符串
24647 * @example
24648 * ```javascript
24649 * editor.execCommand('serverparam');
24650 * editor.queryCommandValue('serverparam'); //返回空
24651 * ```
24652 */
24653 /**
24654 * 修改服务器提交的额外参数列表,删除指定项
24655 * @command serverparam
24656 * @method execCommand
24657 * @param { String } cmd 命令字符串
24658 * @param { String } key 要清除的属性
24659 * @example
24660 * ```javascript
24661 * editor.execCommand('serverparam', 'name'); //删除属性name
24662 * ```
24663 */
24664 /**
24665 * 修改服务器提交的额外参数列表,使用键值添加项
24666 * @command serverparam
24667 * @method execCommand
24668 * @param { String } cmd 命令字符串
24669 * @param { String } key 要添加的属性
24670 * @param { String } value 要添加属性的值
24671 * @example
24672 * ```javascript
24673 * editor.execCommand('serverparam', 'name', 'hello');
24674 * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
24675 * ```
24676 */
24677 /**
24678 * 修改服务器提交的额外参数列表,传入键值对对象添加多项
24679 * @command serverparam
24680 * @method execCommand
24681 * @param { String } cmd 命令字符串
24682 * @param { Object } key 传入的键值对对象
24683 * @example
24684 * ```javascript
24685 * editor.execCommand('serverparam', {'name': 'hello'});
24686 * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
24687 * ```
24688 */
24689 /**
24690 * 修改服务器提交的额外参数列表,使用自定义函数添加多项
24691 * @command serverparam
24692 * @method execCommand
24693 * @param { String } cmd 命令字符串
24694 * @param { Function } key 自定义获取参数的函数
24695 * @example
24696 * ```javascript
24697 * editor.execCommand('serverparam', function(editor){
24698 * return {'key': 'value'};
24699 * });
24700 * editor.queryCommandValue('serverparam'); //返回对象 {'key': 'value'}
24701 * ```
24702 */
24703
24704 /**
24705 * 获取服务器提交的额外参数列表
24706 * @command serverparam
24707 * @method queryCommandValue
24708 * @param { String } cmd 命令字符串
24709 * @example
24710 * ```javascript
24711 * editor.queryCommandValue( 'serverparam' ); //返回对象 {'key': 'value'}
24712 * ```
24713 */
24714 'serverparam':{
24715 execCommand:function (cmd, key, value) {
24716 if (key === undefined || key === null) { //不传参数,清空列表
24717 serverParam = {};
24718 } else if (utils.isString(key)) { //传入键值
24719 if(value === undefined || value === null) {
24720 delete serverParam[key];
24721 } else {
24722 serverParam[key] = value;
24723 }
24724 } else if (utils.isObject(key)) { //传入对象,覆盖列表项
24725 utils.extend(serverParam, key, true);
24726 } else if (utils.isFunction(key)){ //传入函数,添加列表项
24727 utils.extend(serverParam, key(), true);
24728 }
24729 },
24730 queryCommandValue: function(){
24731 return serverParam || {};
24732 }
24733 }
24734 }
24735 }
24736});
24737
24738
24739// plugins/insertfile.js
24740/**
24741 * 插入附件
24742 */
24743UE.plugin.register('insertfile', function (){
24744
24745 var me = this;
24746
24747 function getFileIcon(url){
24748 var ext = url.substr(url.lastIndexOf('.') + 1).toLowerCase(),
24749 maps = {
24750 "rar":"icon_rar.gif",
24751 "zip":"icon_rar.gif",
24752 "tar":"icon_rar.gif",
24753 "gz":"icon_rar.gif",
24754 "bz2":"icon_rar.gif",
24755 "doc":"icon_doc.gif",
24756 "docx":"icon_doc.gif",
24757 "pdf":"icon_pdf.gif",
24758 "mp3":"icon_mp3.gif",
24759 "xls":"icon_xls.gif",
24760 "chm":"icon_chm.gif",
24761 "ppt":"icon_ppt.gif",
24762 "pptx":"icon_ppt.gif",
24763 "avi":"icon_mv.gif",
24764 "rmvb":"icon_mv.gif",
24765 "wmv":"icon_mv.gif",
24766 "flv":"icon_mv.gif",
24767 "swf":"icon_mv.gif",
24768 "rm":"icon_mv.gif",
24769 "exe":"icon_exe.gif",
24770 "psd":"icon_psd.gif",
24771 "txt":"icon_txt.gif",
24772 "jpg":"icon_jpg.gif",
24773 "png":"icon_jpg.gif",
24774 "jpeg":"icon_jpg.gif",
24775 "gif":"icon_jpg.gif",
24776 "ico":"icon_jpg.gif",
24777 "bmp":"icon_jpg.gif"
24778 };
24779 return maps[ext] ? maps[ext]:maps['txt'];
24780 }
24781
24782 return {
24783 commands:{
24784 'insertfile': {
24785 execCommand: function (command, filelist){
24786 filelist = utils.isArray(filelist) ? filelist : [filelist];
24787
24788 var i, item, icon, title,
24789 html = '',
24790 URL = me.getOpt('UEDITOR_HOME_URL'),
24791 iconDir = URL + (URL.substr(URL.length - 1) == '/' ? '':'/') + 'dialogs/attachment/fileTypeImages/';
24792 for (i = 0; i < filelist.length; i++) {
24793 item = filelist[i];
24794 icon = iconDir + getFileIcon(item.url);
24795 title = item.title || item.url.substr(item.url.lastIndexOf('/') + 1);
24796 html += '<p style="line-height: 16px;">' +
24797 '<img style="vertical-align: middle; margin-right: 2px;" src="'+ icon + '" _src="' + icon + '" />' +
24798 '<a style="font-size:12px; color:#0066cc;" href="' + item.url +'" title="' + title + '">' + title + '</a>' +
24799 '</p>';
24800 }
24801 me.execCommand('insertHtml', html);
24802 }
24803 }
24804 }
24805 }
24806});
24807
24808
24809
24810
24811// plugins/xssFilter.js
24812/**
24813 * @file xssFilter.js
24814 * @desc xss过滤器
24815 * @author robbenmu
24816 */
24817
24818UE.plugins.xssFilter = function() {
24819
24820 var config = UEDITOR_CONFIG;
24821 var whitList = config.whitList;
24822
24823 function filter(node) {
24824
24825 var tagName = node.tagName;
24826 var attrs = node.attrs;
24827
24828 if (!whitList.hasOwnProperty(tagName)) {
24829 node.parentNode.removeChild(node);
24830 return false;
24831 }
24832
24833 UE.utils.each(attrs, function (val, key) {
24834
24835 if (whitList[tagName].indexOf(key) === -1) {
24836 node.setAttr(key);
24837 }
24838 });
24839 }
24840
24841 // 添加inserthtml\paste等操作用的过滤规则
24842 if (whitList && config.xssFilterRules) {
24843 this.options.filterRules = function () {
24844
24845 var result = {};
24846
24847 UE.utils.each(whitList, function(val, key) {
24848 result[key] = function (node) {
24849 return filter(node);
24850 };
24851 });
24852
24853 return result;
24854 }();
24855 }
24856
24857 var tagList = [];
24858
24859 UE.utils.each(whitList, function (val, key) {
24860 tagList.push(key);
24861 });
24862
24863 // 添加input过滤规则
24864 //
24865 if (whitList && config.inputXssFilter) {
24866 this.addInputRule(function (root) {
24867
24868 root.traversal(function(node) {
24869 if (node.type !== 'element') {
24870 return false;
24871 }
24872 filter(node);
24873 });
24874 });
24875 }
24876 // 添加output过滤规则
24877 //
24878 if (whitList && config.outputXssFilter) {
24879 this.addOutputRule(function (root) {
24880
24881 root.traversal(function(node) {
24882 if (node.type !== 'element') {
24883 return false;
24884 }
24885 filter(node);
24886 });
24887 });
24888 }
24889
24890};
24891
24892
24893// ui/ui.js
24894var baidu = baidu || {};
24895baidu.editor = baidu.editor || {};
24896UE.ui = baidu.editor.ui = {};
24897
24898// ui/uiutils.js
24899(function (){
24900 var browser = baidu.editor.browser,
24901 domUtils = baidu.editor.dom.domUtils;
24902
24903 var magic = '$EDITORUI';
24904 var root = window[magic] = {};
24905 var uidMagic = 'ID' + magic;
24906 var uidCount = 0;
24907
24908 var uiUtils = baidu.editor.ui.uiUtils = {
24909 uid: function (obj){
24910 return (obj ? obj[uidMagic] || (obj[uidMagic] = ++ uidCount) : ++ uidCount);
24911 },
24912 hook: function ( fn, callback ) {
24913 var dg;
24914 if (fn && fn._callbacks) {
24915 dg = fn;
24916 } else {
24917 dg = function (){
24918 var q;
24919 if (fn) {
24920 q = fn.apply(this, arguments);
24921 }
24922 var callbacks = dg._callbacks;
24923 var k = callbacks.length;
24924 while (k --) {
24925 var r = callbacks[k].apply(this, arguments);
24926 if (q === undefined) {
24927 q = r;
24928 }
24929 }
24930 return q;
24931 };
24932 dg._callbacks = [];
24933 }
24934 dg._callbacks.push(callback);
24935 return dg;
24936 },
24937 createElementByHtml: function (html){
24938 var el = document.createElement('div');
24939 el.innerHTML = html;
24940 el = el.firstChild;
24941 el.parentNode.removeChild(el);
24942 return el;
24943 },
24944 getViewportElement: function (){
24945 return (browser.ie && browser.quirks) ?
24946 document.body : document.documentElement;
24947 },
24948 getClientRect: function (element){
24949 var bcr;
24950 //trace IE6下在控制编辑器显隐时可能会报错,catch一下
24951 try{
24952 bcr = element.getBoundingClientRect();
24953 }catch(e){
24954 bcr={left:0,top:0,height:0,width:0}
24955 }
24956 var rect = {
24957 left: Math.round(bcr.left),
24958 top: Math.round(bcr.top),
24959 height: Math.round(bcr.bottom - bcr.top),
24960 width: Math.round(bcr.right - bcr.left)
24961 };
24962 var doc;
24963 while ((doc = element.ownerDocument) !== document &&
24964 (element = domUtils.getWindow(doc).frameElement)) {
24965 bcr = element.getBoundingClientRect();
24966 rect.left += bcr.left;
24967 rect.top += bcr.top;
24968 }
24969 rect.bottom = rect.top + rect.height;
24970 rect.right = rect.left + rect.width;
24971 return rect;
24972 },
24973 getViewportRect: function (){
24974 var viewportEl = uiUtils.getViewportElement();
24975 var width = (window.innerWidth || viewportEl.clientWidth) | 0;
24976 var height = (window.innerHeight ||viewportEl.clientHeight) | 0;
24977 return {
24978 left: 0,
24979 top: 0,
24980 height: height,
24981 width: width,
24982 bottom: height,
24983 right: width
24984 };
24985 },
24986 setViewportOffset: function (element, offset){
24987 var rect;
24988 var fixedLayer = uiUtils.getFixedLayer();
24989 if (element.parentNode === fixedLayer) {
24990 element.style.left = offset.left + 'px';
24991 element.style.top = offset.top + 'px';
24992 } else {
24993 domUtils.setViewportOffset(element, offset);
24994 }
24995 },
24996 getEventOffset: function (evt){
24997 var el = evt.target || evt.srcElement;
24998 var rect = uiUtils.getClientRect(el);
24999 var offset = uiUtils.getViewportOffsetByEvent(evt);
25000 return {
25001 left: offset.left - rect.left,
25002 top: offset.top - rect.top
25003 };
25004 },
25005 getViewportOffsetByEvent: function (evt){
25006 var el = evt.target || evt.srcElement;
25007 var frameEl = domUtils.getWindow(el).frameElement;
25008 var offset = {
25009 left: evt.clientX,
25010 top: evt.clientY
25011 };
25012 if (frameEl && el.ownerDocument !== document) {
25013 var rect = uiUtils.getClientRect(frameEl);
25014 offset.left += rect.left;
25015 offset.top += rect.top;
25016 }
25017 return offset;
25018 },
25019 setGlobal: function (id, obj){
25020 root[id] = obj;
25021 return magic + '["' + id + '"]';
25022 },
25023 unsetGlobal: function (id){
25024 delete root[id];
25025 },
25026 copyAttributes: function (tgt, src){
25027 var attributes = src.attributes;
25028 var k = attributes.length;
25029 while (k --) {
25030 var attrNode = attributes[k];
25031 if ( attrNode.nodeName != 'style' && attrNode.nodeName != 'class' && (!browser.ie || attrNode.specified) ) {
25032 tgt.setAttribute(attrNode.nodeName, attrNode.nodeValue);
25033 }
25034 }
25035 if (src.className) {
25036 domUtils.addClass(tgt,src.className);
25037 }
25038 if (src.style.cssText) {
25039 tgt.style.cssText += ';' + src.style.cssText;
25040 }
25041 },
25042 removeStyle: function (el, styleName){
25043 if (el.style.removeProperty) {
25044 el.style.removeProperty(styleName);
25045 } else if (el.style.removeAttribute) {
25046 el.style.removeAttribute(styleName);
25047 } else throw '';
25048 },
25049 contains: function (elA, elB){
25050 return elA && elB && (elA === elB ? false : (
25051 elA.contains ? elA.contains(elB) :
25052 elA.compareDocumentPosition(elB) & 16
25053 ));
25054 },
25055 startDrag: function (evt, callbacks,doc){
25056 var doc = doc || document;
25057 var startX = evt.clientX;
25058 var startY = evt.clientY;
25059 function handleMouseMove(evt){
25060 var x = evt.clientX - startX;
25061 var y = evt.clientY - startY;
25062 callbacks.ondragmove(x, y,evt);
25063 if (evt.stopPropagation) {
25064 evt.stopPropagation();
25065 } else {
25066 evt.cancelBubble = true;
25067 }
25068 }
25069 if (doc.addEventListener) {
25070 function handleMouseUp(evt){
25071 doc.removeEventListener('mousemove', handleMouseMove, true);
25072 doc.removeEventListener('mouseup', handleMouseUp, true);
25073 window.removeEventListener('mouseup', handleMouseUp, true);
25074 callbacks.ondragstop();
25075 }
25076 doc.addEventListener('mousemove', handleMouseMove, true);
25077 doc.addEventListener('mouseup', handleMouseUp, true);
25078 window.addEventListener('mouseup', handleMouseUp, true);
25079
25080 evt.preventDefault();
25081 } else {
25082 var elm = evt.srcElement;
25083 elm.setCapture();
25084 function releaseCaptrue(){
25085 elm.releaseCapture();
25086 elm.detachEvent('onmousemove', handleMouseMove);
25087 elm.detachEvent('onmouseup', releaseCaptrue);
25088 elm.detachEvent('onlosecaptrue', releaseCaptrue);
25089 callbacks.ondragstop();
25090 }
25091 elm.attachEvent('onmousemove', handleMouseMove);
25092 elm.attachEvent('onmouseup', releaseCaptrue);
25093 elm.attachEvent('onlosecaptrue', releaseCaptrue);
25094 evt.returnValue = false;
25095 }
25096 callbacks.ondragstart();
25097 },
25098 getFixedLayer: function (){
25099 var layer = document.getElementById('edui_fixedlayer');
25100 if (layer == null) {
25101 layer = document.createElement('div');
25102 layer.id = 'edui_fixedlayer';
25103 document.body.appendChild(layer);
25104 if (browser.ie && browser.version <= 8) {
25105 layer.style.position = 'absolute';
25106 bindFixedLayer();
25107 setTimeout(updateFixedOffset);
25108 } else {
25109 layer.style.position = 'fixed';
25110 }
25111 layer.style.left = '0';
25112 layer.style.top = '0';
25113 layer.style.width = '0';
25114 layer.style.height = '0';
25115 }
25116 return layer;
25117 },
25118 makeUnselectable: function (element){
25119 if (browser.opera || (browser.ie && browser.version < 9)) {
25120 element.unselectable = 'on';
25121 if (element.hasChildNodes()) {
25122 for (var i=0; i<element.childNodes.length; i++) {
25123 if (element.childNodes[i].nodeType == 1) {
25124 uiUtils.makeUnselectable(element.childNodes[i]);
25125 }
25126 }
25127 }
25128 } else {
25129 if (element.style.MozUserSelect !== undefined) {
25130 element.style.MozUserSelect = 'none';
25131 } else if (element.style.WebkitUserSelect !== undefined) {
25132 element.style.WebkitUserSelect = 'none';
25133 } else if (element.style.KhtmlUserSelect !== undefined) {
25134 element.style.KhtmlUserSelect = 'none';
25135 }
25136 }
25137 }
25138 };
25139 function updateFixedOffset(){
25140 var layer = document.getElementById('edui_fixedlayer');
25141 uiUtils.setViewportOffset(layer, {
25142 left: 0,
25143 top: 0
25144 });
25145// layer.style.display = 'none';
25146// layer.style.display = 'block';
25147
25148 //#trace: 1354
25149// setTimeout(updateFixedOffset);
25150 }
25151 function bindFixedLayer(adjOffset){
25152 domUtils.on(window, 'scroll', updateFixedOffset);
25153 domUtils.on(window, 'resize', baidu.editor.utils.defer(updateFixedOffset, 0, true));
25154 }
25155})();
25156
25157
25158// ui/uibase.js
25159(function () {
25160 var utils = baidu.editor.utils,
25161 uiUtils = baidu.editor.ui.uiUtils,
25162 EventBase = baidu.editor.EventBase,
25163 UIBase = baidu.editor.ui.UIBase = function () {
25164 };
25165
25166 UIBase.prototype = {
25167 className:'',
25168 uiName:'',
25169 initOptions:function (options) {
25170 var me = this;
25171 for (var k in options) {
25172 me[k] = options[k];
25173 }
25174 this.id = this.id || 'edui' + uiUtils.uid();
25175 },
25176 initUIBase:function () {
25177 this._globalKey = utils.unhtml(uiUtils.setGlobal(this.id, this));
25178 },
25179 render:function (holder) {
25180 var html = this.renderHtml();
25181 var el = uiUtils.createElementByHtml(html);
25182
25183 //by xuheng 给每个node添加class
25184 var list = domUtils.getElementsByTagName(el, "*");
25185 var theme = "edui-" + (this.theme || this.editor.options.theme);
25186 var layer = document.getElementById('edui_fixedlayer');
25187 for (var i = 0, node; node = list[i++];) {
25188 domUtils.addClass(node, theme);
25189 }
25190 domUtils.addClass(el, theme);
25191 if(layer){
25192 layer.className="";
25193 domUtils.addClass(layer,theme);
25194 }
25195
25196 var seatEl = this.getDom();
25197 if (seatEl != null) {
25198 seatEl.parentNode.replaceChild(el, seatEl);
25199 uiUtils.copyAttributes(el, seatEl);
25200 } else {
25201 if (typeof holder == 'string') {
25202 holder = document.getElementById(holder);
25203 }
25204 holder = holder || uiUtils.getFixedLayer();
25205 domUtils.addClass(holder, theme);
25206 holder.appendChild(el);
25207 }
25208 this.postRender();
25209 },
25210 getDom:function (name) {
25211 if (!name) {
25212 return document.getElementById(this.id);
25213 } else {
25214 return document.getElementById(this.id + '_' + name);
25215 }
25216 },
25217 postRender:function () {
25218 this.fireEvent('postrender');
25219 },
25220 getHtmlTpl:function () {
25221 return '';
25222 },
25223 formatHtml:function (tpl) {
25224 var prefix = 'edui-' + this.uiName;
25225 return (tpl
25226 .replace(/##/g, this.id)
25227 .replace(/%%-/g, this.uiName ? prefix + '-' : '')
25228 .replace(/%%/g, (this.uiName ? prefix : '') + ' ' + this.className)
25229 .replace(/\$\$/g, this._globalKey));
25230 },
25231 renderHtml:function () {
25232 return this.formatHtml(this.getHtmlTpl());
25233 },
25234 dispose:function () {
25235 var box = this.getDom();
25236 if (box) baidu.editor.dom.domUtils.remove(box);
25237 uiUtils.unsetGlobal(this.id);
25238 }
25239 };
25240 utils.inherits(UIBase, EventBase);
25241})();
25242
25243
25244// ui/separator.js
25245(function (){
25246 var utils = baidu.editor.utils,
25247 UIBase = baidu.editor.ui.UIBase,
25248 Separator = baidu.editor.ui.Separator = function (options){
25249 this.initOptions(options);
25250 this.initSeparator();
25251 };
25252 Separator.prototype = {
25253 uiName: 'separator',
25254 initSeparator: function (){
25255 this.initUIBase();
25256 },
25257 getHtmlTpl: function (){
25258 return '<div id="##" class="edui-box %%"></div>';
25259 }
25260 };
25261 utils.inherits(Separator, UIBase);
25262
25263})();
25264
25265
25266// ui/mask.js
25267///import core
25268///import uicore
25269(function (){
25270 var utils = baidu.editor.utils,
25271 domUtils = baidu.editor.dom.domUtils,
25272 UIBase = baidu.editor.ui.UIBase,
25273 uiUtils = baidu.editor.ui.uiUtils;
25274
25275 var Mask = baidu.editor.ui.Mask = function (options){
25276 this.initOptions(options);
25277 this.initUIBase();
25278 };
25279 Mask.prototype = {
25280 getHtmlTpl: function (){
25281 return '<div id="##" class="edui-mask %%" onclick="return $$._onClick(event, this);" onmousedown="return $$._onMouseDown(event, this);"></div>';
25282 },
25283 postRender: function (){
25284 var me = this;
25285 domUtils.on(window, 'resize', function (){
25286 setTimeout(function (){
25287 if (!me.isHidden()) {
25288 me._fill();
25289 }
25290 });
25291 });
25292 },
25293 show: function (zIndex){
25294 this._fill();
25295 this.getDom().style.display = '';
25296 this.getDom().style.zIndex = zIndex;
25297 },
25298 hide: function (){
25299 this.getDom().style.display = 'none';
25300 this.getDom().style.zIndex = '';
25301 },
25302 isHidden: function (){
25303 return this.getDom().style.display == 'none';
25304 },
25305 _onMouseDown: function (){
25306 return false;
25307 },
25308 _onClick: function (e, target){
25309 this.fireEvent('click', e, target);
25310 },
25311 _fill: function (){
25312 var el = this.getDom();
25313 var vpRect = uiUtils.getViewportRect();
25314 el.style.width = vpRect.width + 'px';
25315 el.style.height = vpRect.height + 'px';
25316 }
25317 };
25318 utils.inherits(Mask, UIBase);
25319})();
25320
25321
25322// ui/popup.js
25323///import core
25324///import uicore
25325(function () {
25326 var utils = baidu.editor.utils,
25327 uiUtils = baidu.editor.ui.uiUtils,
25328 domUtils = baidu.editor.dom.domUtils,
25329 UIBase = baidu.editor.ui.UIBase,
25330 Popup = baidu.editor.ui.Popup = function (options){
25331 this.initOptions(options);
25332 this.initPopup();
25333 };
25334
25335 var allPopups = [];
25336 function closeAllPopup( evt,el ){
25337 for ( var i = 0; i < allPopups.length; i++ ) {
25338 var pop = allPopups[i];
25339 if (!pop.isHidden()) {
25340 if (pop.queryAutoHide(el) !== false) {
25341 if(evt&&/scroll/ig.test(evt.type)&&pop.className=="edui-wordpastepop") return;
25342 pop.hide();
25343 }
25344 }
25345 }
25346
25347 if(allPopups.length)
25348 pop.editor.fireEvent("afterhidepop");
25349 }
25350
25351 Popup.postHide = closeAllPopup;
25352
25353 var ANCHOR_CLASSES = ['edui-anchor-topleft','edui-anchor-topright',
25354 'edui-anchor-bottomleft','edui-anchor-bottomright'];
25355 Popup.prototype = {
25356 SHADOW_RADIUS: 5,
25357 content: null,
25358 _hidden: false,
25359 autoRender: true,
25360 canSideLeft: true,
25361 canSideUp: true,
25362 initPopup: function (){
25363 this.initUIBase();
25364 allPopups.push( this );
25365 },
25366 getHtmlTpl: function (){
25367 return '<div id="##" class="edui-popup %%" onmousedown="return false;">' +
25368 ' <div id="##_body" class="edui-popup-body">' +
25369 ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
25370 ' <div class="edui-shadow"></div>' +
25371 ' <div id="##_content" class="edui-popup-content">' +
25372 this.getContentHtmlTpl() +
25373 ' </div>' +
25374 ' </div>' +
25375 '</div>';
25376 },
25377 getContentHtmlTpl: function (){
25378 if(this.content){
25379 if (typeof this.content == 'string') {
25380 return this.content;
25381 }
25382 return this.content.renderHtml();
25383 }else{
25384 return ''
25385 }
25386
25387 },
25388 _UIBase_postRender: UIBase.prototype.postRender,
25389 postRender: function (){
25390
25391
25392 if (this.content instanceof UIBase) {
25393 this.content.postRender();
25394 }
25395
25396 //捕获鼠标滚轮
25397 if( this.captureWheel && !this.captured ) {
25398
25399 this.captured = true;
25400
25401 var winHeight = ( document.documentElement.clientHeight || document.body.clientHeight ) - 80,
25402 _height = this.getDom().offsetHeight,
25403 _top = uiUtils.getClientRect( this.combox.getDom() ).top,
25404 content = this.getDom('content'),
25405 ifr = this.getDom('body').getElementsByTagName('iframe'),
25406 me = this;
25407
25408 ifr.length && ( ifr = ifr[0] );
25409
25410 while( _top + _height > winHeight ) {
25411 _height -= 30;
25412 }
25413 content.style.height = _height + 'px';
25414 //同步更改iframe高度
25415 ifr && ( ifr.style.height = _height + 'px' );
25416
25417 //阻止在combox上的鼠标滚轮事件, 防止用户的正常操作被误解
25418 if( window.XMLHttpRequest ) {
25419
25420 domUtils.on( content, ( 'onmousewheel' in document.body ) ? 'mousewheel' :'DOMMouseScroll' , function(e){
25421
25422 if(e.preventDefault) {
25423 e.preventDefault();
25424 } else {
25425 e.returnValue = false;
25426 }
25427
25428 if( e.wheelDelta ) {
25429
25430 content.scrollTop -= ( e.wheelDelta / 120 )*60;
25431
25432 } else {
25433
25434 content.scrollTop -= ( e.detail / -3 )*60;
25435
25436 }
25437
25438 });
25439
25440 } else {
25441
25442 //ie6
25443 domUtils.on( this.getDom(), 'mousewheel' , function(e){
25444
25445 e.returnValue = false;
25446
25447 me.getDom('content').scrollTop -= ( e.wheelDelta / 120 )*60;
25448
25449 });
25450
25451 }
25452
25453 }
25454 this.fireEvent('postRenderAfter');
25455 this.hide(true);
25456 this._UIBase_postRender();
25457 },
25458 _doAutoRender: function (){
25459 if (!this.getDom() && this.autoRender) {
25460 this.render();
25461 }
25462 },
25463 mesureSize: function (){
25464 var box = this.getDom('content');
25465 return uiUtils.getClientRect(box);
25466 },
25467 fitSize: function (){
25468 if( this.captureWheel && this.sized ) {
25469 return this.__size;
25470 }
25471 this.sized = true;
25472 var popBodyEl = this.getDom('body');
25473 popBodyEl.style.width = '';
25474 popBodyEl.style.height = '';
25475 var size = this.mesureSize();
25476 if( this.captureWheel ) {
25477 popBodyEl.style.width = -(-20 -size.width) + 'px';
25478 var height = parseInt( this.getDom('content').style.height, 10 );
25479 !window.isNaN( height ) && ( size.height = height );
25480 } else {
25481 popBodyEl.style.width = size.width + 'px';
25482 }
25483 popBodyEl.style.height = size.height + 'px';
25484 this.__size = size;
25485 this.captureWheel && (this.getDom('content').style.overflow = 'auto');
25486 return size;
25487 },
25488 showAnchor: function ( element, hoz ){
25489 this.showAnchorRect( uiUtils.getClientRect( element ), hoz );
25490 },
25491 showAnchorRect: function ( rect, hoz, adj ){
25492 this._doAutoRender();
25493 var vpRect = uiUtils.getViewportRect();
25494 this.getDom().style.visibility = 'hidden';
25495 this._show();
25496 var popSize = this.fitSize();
25497
25498 var sideLeft, sideUp, left, top;
25499 if (hoz) {
25500 sideLeft = this.canSideLeft && (rect.right + popSize.width > vpRect.right && rect.left > popSize.width);
25501 sideUp = this.canSideUp && (rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height);
25502 left = (sideLeft ? rect.left - popSize.width : rect.right);
25503 top = (sideUp ? rect.bottom - popSize.height : rect.top);
25504 } else {
25505 sideLeft = this.canSideLeft && (rect.right + popSize.width > vpRect.right && rect.left > popSize.width);
25506 sideUp = this.canSideUp && (rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height);
25507 left = (sideLeft ? rect.right - popSize.width : rect.left);
25508 top = (sideUp ? rect.top - popSize.height : rect.bottom);
25509 }
25510
25511 var popEl = this.getDom();
25512 uiUtils.setViewportOffset(popEl, {
25513 left: left,
25514 top: top
25515 });
25516 domUtils.removeClasses(popEl, ANCHOR_CLASSES);
25517 popEl.className += ' ' + ANCHOR_CLASSES[(sideUp ? 1 : 0) * 2 + (sideLeft ? 1 : 0)];
25518 if(this.editor){
25519 popEl.style.zIndex = this.editor.container.style.zIndex * 1 + 10;
25520 baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = popEl.style.zIndex - 1;
25521 }
25522 this.getDom().style.visibility = 'visible';
25523
25524 },
25525 showAt: function (offset) {
25526 var left = offset.left;
25527 var top = offset.top;
25528 var rect = {
25529 left: left,
25530 top: top,
25531 right: left,
25532 bottom: top,
25533 height: 0,
25534 width: 0
25535 };
25536 this.showAnchorRect(rect, false, true);
25537 },
25538 _show: function (){
25539 if (this._hidden) {
25540 var box = this.getDom();
25541 box.style.display = '';
25542 this._hidden = false;
25543// if (box.setActive) {
25544// box.setActive();
25545// }
25546 this.fireEvent('show');
25547 }
25548 },
25549 isHidden: function (){
25550 return this._hidden;
25551 },
25552 show: function (){
25553 this._doAutoRender();
25554 this._show();
25555 },
25556 hide: function (notNofity){
25557 if (!this._hidden && this.getDom()) {
25558 this.getDom().style.display = 'none';
25559 this._hidden = true;
25560 if (!notNofity) {
25561 this.fireEvent('hide');
25562 }
25563 }
25564 },
25565 queryAutoHide: function (el){
25566 return !el || !uiUtils.contains(this.getDom(), el);
25567 }
25568 };
25569 utils.inherits(Popup, UIBase);
25570
25571 domUtils.on( document, 'mousedown', function ( evt ) {
25572 var el = evt.target || evt.srcElement;
25573 closeAllPopup( evt,el );
25574 } );
25575 domUtils.on( window, 'scroll', function (evt,el) {
25576 closeAllPopup( evt,el );
25577 } );
25578
25579})();
25580
25581
25582// ui/colorpicker.js
25583///import core
25584///import uicore
25585(function (){
25586 var utils = baidu.editor.utils,
25587 UIBase = baidu.editor.ui.UIBase,
25588 ColorPicker = baidu.editor.ui.ColorPicker = function (options){
25589 this.initOptions(options);
25590 this.noColorText = this.noColorText || this.editor.getLang("clearColor");
25591 this.initUIBase();
25592 };
25593
25594 ColorPicker.prototype = {
25595 getHtmlTpl: function (){
25596 return genColorPicker(this.noColorText,this.editor);
25597 },
25598 _onTableClick: function (evt){
25599 var tgt = evt.target || evt.srcElement;
25600 var color = tgt.getAttribute('data-color');
25601 if (color) {
25602 this.fireEvent('pickcolor', color);
25603 }
25604 },
25605 _onTableOver: function (evt){
25606 var tgt = evt.target || evt.srcElement;
25607 var color = tgt.getAttribute('data-color');
25608 if (color) {
25609 this.getDom('preview').style.backgroundColor = color;
25610 }
25611 },
25612 _onTableOut: function (){
25613 this.getDom('preview').style.backgroundColor = '';
25614 },
25615 _onPickNoColor: function (){
25616 this.fireEvent('picknocolor');
25617 }
25618 };
25619 utils.inherits(ColorPicker, UIBase);
25620
25621 var COLORS = (
25622 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
25623 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
25624 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
25625 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
25626 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
25627 '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
25628 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(',');
25629
25630 function genColorPicker(noColorText,editor){
25631 var html = '<div id="##" class="edui-colorpicker %%">' +
25632 '<div class="edui-colorpicker-topbar edui-clearfix">' +
25633 '<div unselectable="on" id="##_preview" class="edui-colorpicker-preview"></div>' +
25634 '<div unselectable="on" class="edui-colorpicker-nocolor" onclick="$$._onPickNoColor(event, this);">'+ noColorText +'</div>' +
25635 '</div>' +
25636 '<table class="edui-box" style="border-collapse: collapse;" onmouseover="$$._onTableOver(event, this);" onmouseout="$$._onTableOut(event, this);" onclick="return $$._onTableClick(event, this);" cellspacing="0" cellpadding="0">' +
25637 '<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;padding-top: 2px"><td colspan="10">'+editor.getLang("themeColor")+'</td> </tr>'+
25638 '<tr class="edui-colorpicker-tablefirstrow" >';
25639 for (var i=0; i<COLORS.length; i++) {
25640 if (i && i%10 === 0) {
25641 html += '</tr>'+(i==60?'<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;"><td colspan="10">'+editor.getLang("standardColor")+'</td></tr>':'')+'<tr'+(i==60?' class="edui-colorpicker-tablefirstrow"':'')+'>';
25642 }
25643 html += i<70 ? '<td style="padding: 0 2px;"><a hidefocus title="'+COLORS[i]+'" onclick="return false;" href="javascript:" unselectable="on" class="edui-box edui-colorpicker-colorcell"' +
25644 ' data-color="#'+ COLORS[i] +'"'+
25645 ' style="background-color:#'+ COLORS[i] +';border:solid #ccc;'+
25646 (i<10 || i>=60?'border-width:1px;':
25647 i>=10&&i<20?'border-width:1px 1px 0 1px;':
25648
25649 'border-width:0 1px 0 1px;')+
25650 '"' +
25651 '></a></td>':'';
25652 }
25653 html += '</tr></table></div>';
25654 return html;
25655 }
25656})();
25657
25658
25659// ui/tablepicker.js
25660///import core
25661///import uicore
25662(function (){
25663 var utils = baidu.editor.utils,
25664 uiUtils = baidu.editor.ui.uiUtils,
25665 UIBase = baidu.editor.ui.UIBase;
25666
25667 var TablePicker = baidu.editor.ui.TablePicker = function (options){
25668 this.initOptions(options);
25669 this.initTablePicker();
25670 };
25671 TablePicker.prototype = {
25672 defaultNumRows: 10,
25673 defaultNumCols: 10,
25674 maxNumRows: 20,
25675 maxNumCols: 20,
25676 numRows: 10,
25677 numCols: 10,
25678 lengthOfCellSide: 22,
25679 initTablePicker: function (){
25680 this.initUIBase();
25681 },
25682 getHtmlTpl: function (){
25683 var me = this;
25684 return '<div id="##" class="edui-tablepicker %%">' +
25685 '<div class="edui-tablepicker-body">' +
25686 '<div class="edui-infoarea">' +
25687 '<span id="##_label" class="edui-label"></span>' +
25688 '</div>' +
25689 '<div class="edui-pickarea"' +
25690 ' onmousemove="$$._onMouseMove(event, this);"' +
25691 ' onmouseover="$$._onMouseOver(event, this);"' +
25692 ' onmouseout="$$._onMouseOut(event, this);"' +
25693 ' onclick="$$._onClick(event, this);"' +
25694 '>' +
25695 '<div id="##_overlay" class="edui-overlay"></div>' +
25696 '</div>' +
25697 '</div>' +
25698 '</div>';
25699 },
25700 _UIBase_render: UIBase.prototype.render,
25701 render: function (holder){
25702 this._UIBase_render(holder);
25703 this.getDom('label').innerHTML = '0'+this.editor.getLang("t_row")+' x 0'+this.editor.getLang("t_col");
25704 },
25705 _track: function (numCols, numRows){
25706 var style = this.getDom('overlay').style;
25707 var sideLen = this.lengthOfCellSide;
25708 style.width = numCols * sideLen + 'px';
25709 style.height = numRows * sideLen + 'px';
25710 var label = this.getDom('label');
25711 label.innerHTML = numCols +this.editor.getLang("t_col")+' x ' + numRows + this.editor.getLang("t_row");
25712 this.numCols = numCols;
25713 this.numRows = numRows;
25714 },
25715 _onMouseOver: function (evt, el){
25716 var rel = evt.relatedTarget || evt.fromElement;
25717 if (!uiUtils.contains(el, rel) && el !== rel) {
25718 this.getDom('label').innerHTML = '0'+this.editor.getLang("t_col")+' x 0'+this.editor.getLang("t_row");
25719 this.getDom('overlay').style.visibility = '';
25720 }
25721 },
25722 _onMouseOut: function (evt, el){
25723 var rel = evt.relatedTarget || evt.toElement;
25724 if (!uiUtils.contains(el, rel) && el !== rel) {
25725 this.getDom('label').innerHTML = '0'+this.editor.getLang("t_col")+' x 0'+this.editor.getLang("t_row");
25726 this.getDom('overlay').style.visibility = 'hidden';
25727 }
25728 },
25729 _onMouseMove: function (evt, el){
25730 var style = this.getDom('overlay').style;
25731 var offset = uiUtils.getEventOffset(evt);
25732 var sideLen = this.lengthOfCellSide;
25733 var numCols = Math.ceil(offset.left / sideLen);
25734 var numRows = Math.ceil(offset.top / sideLen);
25735 this._track(numCols, numRows);
25736 },
25737 _onClick: function (){
25738 this.fireEvent('picktable', this.numCols, this.numRows);
25739 }
25740 };
25741 utils.inherits(TablePicker, UIBase);
25742})();
25743
25744
25745// ui/stateful.js
25746(function (){
25747 var browser = baidu.editor.browser,
25748 domUtils = baidu.editor.dom.domUtils,
25749 uiUtils = baidu.editor.ui.uiUtils;
25750
25751 var TPL_STATEFUL = 'onmousedown="$$.Stateful_onMouseDown(event, this);"' +
25752 ' onmouseup="$$.Stateful_onMouseUp(event, this);"' +
25753 ( browser.ie ? (
25754 ' onmouseenter="$$.Stateful_onMouseEnter(event, this);"' +
25755 ' onmouseleave="$$.Stateful_onMouseLeave(event, this);"' )
25756 : (
25757 ' onmouseover="$$.Stateful_onMouseOver(event, this);"' +
25758 ' onmouseout="$$.Stateful_onMouseOut(event, this);"' ));
25759
25760 baidu.editor.ui.Stateful = {
25761 alwalysHoverable: false,
25762 target:null,//目标元素和this指向dom不一样
25763 Stateful_init: function (){
25764 this._Stateful_dGetHtmlTpl = this.getHtmlTpl;
25765 this.getHtmlTpl = this.Stateful_getHtmlTpl;
25766 },
25767 Stateful_getHtmlTpl: function (){
25768 var tpl = this._Stateful_dGetHtmlTpl();
25769 // 使用function避免$转义
25770 return tpl.replace(/stateful/g, function (){ return TPL_STATEFUL; });
25771 },
25772 Stateful_onMouseEnter: function (evt, el){
25773 this.target=el;
25774 if (!this.isDisabled() || this.alwalysHoverable) {
25775 this.addState('hover');
25776 this.fireEvent('over');
25777 }
25778 },
25779 Stateful_onMouseLeave: function (evt, el){
25780 if (!this.isDisabled() || this.alwalysHoverable) {
25781 this.removeState('hover');
25782 this.removeState('active');
25783 this.fireEvent('out');
25784 }
25785 },
25786 Stateful_onMouseOver: function (evt, el){
25787 var rel = evt.relatedTarget;
25788 if (!uiUtils.contains(el, rel) && el !== rel) {
25789 this.Stateful_onMouseEnter(evt, el);
25790 }
25791 },
25792 Stateful_onMouseOut: function (evt, el){
25793 var rel = evt.relatedTarget;
25794 if (!uiUtils.contains(el, rel) && el !== rel) {
25795 this.Stateful_onMouseLeave(evt, el);
25796 }
25797 },
25798 Stateful_onMouseDown: function (evt, el){
25799 if (!this.isDisabled()) {
25800 this.addState('active');
25801 }
25802 },
25803 Stateful_onMouseUp: function (evt, el){
25804 if (!this.isDisabled()) {
25805 this.removeState('active');
25806 }
25807 },
25808 Stateful_postRender: function (){
25809 if (this.disabled && !this.hasState('disabled')) {
25810 this.addState('disabled');
25811 }
25812 },
25813 hasState: function (state){
25814 return domUtils.hasClass(this.getStateDom(), 'edui-state-' + state);
25815 },
25816 addState: function (state){
25817 if (!this.hasState(state)) {
25818 this.getStateDom().className += ' edui-state-' + state;
25819 }
25820 },
25821 removeState: function (state){
25822 if (this.hasState(state)) {
25823 domUtils.removeClasses(this.getStateDom(), ['edui-state-' + state]);
25824 }
25825 },
25826 getStateDom: function (){
25827 return this.getDom('state');
25828 },
25829 isChecked: function (){
25830 return this.hasState('checked');
25831 },
25832 setChecked: function (checked){
25833 if (!this.isDisabled() && checked) {
25834 this.addState('checked');
25835 } else {
25836 this.removeState('checked');
25837 }
25838 },
25839 isDisabled: function (){
25840 return this.hasState('disabled');
25841 },
25842 setDisabled: function (disabled){
25843 if (disabled) {
25844 this.removeState('hover');
25845 this.removeState('checked');
25846 this.removeState('active');
25847 this.addState('disabled');
25848 } else {
25849 this.removeState('disabled');
25850 }
25851 }
25852 };
25853})();
25854
25855
25856// ui/button.js
25857///import core
25858///import uicore
25859///import ui/stateful.js
25860(function (){
25861 var utils = baidu.editor.utils,
25862 UIBase = baidu.editor.ui.UIBase,
25863 Stateful = baidu.editor.ui.Stateful,
25864 Button = baidu.editor.ui.Button = function (options){
25865 if(options.name){
25866 var btnName = options.name;
25867 var cssRules = options.cssRules;
25868 if(!options.className){
25869 options.className = 'edui-for-' + btnName;
25870 }
25871 options.cssRules = '.edui-default .edui-for-'+ btnName +' .edui-icon {'+ cssRules +'}'
25872 }
25873 this.initOptions(options);
25874 this.initButton();
25875 };
25876 Button.prototype = {
25877 uiName: 'button',
25878 label: '',
25879 title: '',
25880 showIcon: true,
25881 showText: true,
25882 cssRules:'',
25883 initButton: function (){
25884 this.initUIBase();
25885 this.Stateful_init();
25886 if(this.cssRules){
25887 utils.cssRule('edui-customize-'+this.name+'-style',this.cssRules);
25888 }
25889 },
25890 getHtmlTpl: function (){
25891 return '<div id="##" class="edui-box %%">' +
25892 '<div id="##_state" stateful>' +
25893 '<div class="%%-wrap"><div id="##_body" unselectable="on" ' + (this.title ? 'title="' + this.title + '"' : '') +
25894 ' class="%%-body" onmousedown="return $$._onMouseDown(event, this);" onclick="return $$._onClick(event, this);">' +
25895 (this.showIcon ? '<div class="edui-box edui-icon"></div>' : '') +
25896 (this.showText ? '<div class="edui-box edui-label">' + this.label + '</div>' : '') +
25897 '</div>' +
25898 '</div>' +
25899 '</div></div>';
25900 },
25901 postRender: function (){
25902 this.Stateful_postRender();
25903 this.setDisabled(this.disabled)
25904 },
25905 _onMouseDown: function (e){
25906 var target = e.target || e.srcElement,
25907 tagName = target && target.tagName && target.tagName.toLowerCase();
25908 if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
25909 return false;
25910 }
25911 },
25912 _onClick: function (){
25913 if (!this.isDisabled()) {
25914 this.fireEvent('click');
25915 }
25916 },
25917 setTitle: function(text){
25918 var label = this.getDom('label');
25919 label.innerHTML = text;
25920 }
25921 };
25922 utils.inherits(Button, UIBase);
25923 utils.extend(Button.prototype, Stateful);
25924
25925})();
25926
25927
25928// ui/splitbutton.js
25929///import core
25930///import uicore
25931///import ui/stateful.js
25932(function (){
25933 var utils = baidu.editor.utils,
25934 uiUtils = baidu.editor.ui.uiUtils,
25935 domUtils = baidu.editor.dom.domUtils,
25936 UIBase = baidu.editor.ui.UIBase,
25937 Stateful = baidu.editor.ui.Stateful,
25938 SplitButton = baidu.editor.ui.SplitButton = function (options){
25939 this.initOptions(options);
25940 this.initSplitButton();
25941 };
25942 SplitButton.prototype = {
25943 popup: null,
25944 uiName: 'splitbutton',
25945 title: '',
25946 initSplitButton: function (){
25947 this.initUIBase();
25948 this.Stateful_init();
25949 var me = this;
25950 if (this.popup != null) {
25951 var popup = this.popup;
25952 this.popup = null;
25953 this.setPopup(popup);
25954 }
25955 },
25956 _UIBase_postRender: UIBase.prototype.postRender,
25957 postRender: function (){
25958 this.Stateful_postRender();
25959 this._UIBase_postRender();
25960 },
25961 setPopup: function (popup){
25962 if (this.popup === popup) return;
25963 if (this.popup != null) {
25964 this.popup.dispose();
25965 }
25966 popup.addListener('show', utils.bind(this._onPopupShow, this));
25967 popup.addListener('hide', utils.bind(this._onPopupHide, this));
25968 popup.addListener('postrender', utils.bind(function (){
25969 popup.getDom('body').appendChild(
25970 uiUtils.createElementByHtml('<div id="' +
25971 this.popup.id + '_bordereraser" class="edui-bordereraser edui-background" style="width:' +
25972 (uiUtils.getClientRect(this.getDom()).width + 20) + 'px"></div>')
25973 );
25974 popup.getDom().className += ' ' + this.className;
25975 }, this));
25976 this.popup = popup;
25977 },
25978 _onPopupShow: function (){
25979 this.addState('opened');
25980 },
25981 _onPopupHide: function (){
25982 this.removeState('opened');
25983 },
25984 getHtmlTpl: function (){
25985 return '<div id="##" class="edui-box %%">' +
25986 '<div '+ (this.title ? 'title="' + this.title + '"' : '') +' id="##_state" stateful><div class="%%-body">' +
25987 '<div id="##_button_body" class="edui-box edui-button-body" onclick="$$._onButtonClick(event, this);">' +
25988 '<div class="edui-box edui-icon"></div>' +
25989 '</div>' +
25990 '<div class="edui-box edui-splitborder"></div>' +
25991 '<div class="edui-box edui-arrow" onclick="$$._onArrowClick();"></div>' +
25992 '</div></div></div>';
25993 },
25994 showPopup: function (){
25995 // 当popup往上弹出的时候,做特殊处理
25996 var rect = uiUtils.getClientRect(this.getDom());
25997 rect.top -= this.popup.SHADOW_RADIUS;
25998 rect.height += this.popup.SHADOW_RADIUS;
25999 this.popup.showAnchorRect(rect);
26000 },
26001 _onArrowClick: function (event, el){
26002 if (!this.isDisabled()) {
26003 this.showPopup();
26004 }
26005 },
26006 _onButtonClick: function (){
26007 if (!this.isDisabled()) {
26008 this.fireEvent('buttonclick');
26009 }
26010 }
26011 };
26012 utils.inherits(SplitButton, UIBase);
26013 utils.extend(SplitButton.prototype, Stateful, true);
26014
26015})();
26016
26017
26018// ui/colorbutton.js
26019///import core
26020///import uicore
26021///import ui/colorpicker.js
26022///import ui/popup.js
26023///import ui/splitbutton.js
26024(function (){
26025 var utils = baidu.editor.utils,
26026 uiUtils = baidu.editor.ui.uiUtils,
26027 ColorPicker = baidu.editor.ui.ColorPicker,
26028 Popup = baidu.editor.ui.Popup,
26029 SplitButton = baidu.editor.ui.SplitButton,
26030 ColorButton = baidu.editor.ui.ColorButton = function (options){
26031 this.initOptions(options);
26032 this.initColorButton();
26033 };
26034 ColorButton.prototype = {
26035 initColorButton: function (){
26036 var me = this;
26037 this.popup = new Popup({
26038 content: new ColorPicker({
26039 noColorText: me.editor.getLang("clearColor"),
26040 editor:me.editor,
26041 onpickcolor: function (t, color){
26042 me._onPickColor(color);
26043 },
26044 onpicknocolor: function (t, color){
26045 me._onPickNoColor(color);
26046 }
26047 }),
26048 editor:me.editor
26049 });
26050 this.initSplitButton();
26051 },
26052 _SplitButton_postRender: SplitButton.prototype.postRender,
26053 postRender: function (){
26054 this._SplitButton_postRender();
26055 this.getDom('button_body').appendChild(
26056 uiUtils.createElementByHtml('<div id="' + this.id + '_colorlump" class="edui-colorlump"></div>')
26057 );
26058 this.getDom().className += ' edui-colorbutton';
26059 },
26060 setColor: function (color){
26061 this.getDom('colorlump').style.backgroundColor = color;
26062 this.color = color;
26063 },
26064 _onPickColor: function (color){
26065 if (this.fireEvent('pickcolor', color) !== false) {
26066 this.setColor(color);
26067 this.popup.hide();
26068 }
26069 },
26070 _onPickNoColor: function (color){
26071 if (this.fireEvent('picknocolor') !== false) {
26072 this.popup.hide();
26073 }
26074 }
26075 };
26076 utils.inherits(ColorButton, SplitButton);
26077
26078})();
26079
26080
26081// ui/tablebutton.js
26082///import core
26083///import uicore
26084///import ui/popup.js
26085///import ui/tablepicker.js
26086///import ui/splitbutton.js
26087(function (){
26088 var utils = baidu.editor.utils,
26089 Popup = baidu.editor.ui.Popup,
26090 TablePicker = baidu.editor.ui.TablePicker,
26091 SplitButton = baidu.editor.ui.SplitButton,
26092 TableButton = baidu.editor.ui.TableButton = function (options){
26093 this.initOptions(options);
26094 this.initTableButton();
26095 };
26096 TableButton.prototype = {
26097 initTableButton: function (){
26098 var me = this;
26099 this.popup = new Popup({
26100 content: new TablePicker({
26101 editor:me.editor,
26102 onpicktable: function (t, numCols, numRows){
26103 me._onPickTable(numCols, numRows);
26104 }
26105 }),
26106 'editor':me.editor
26107 });
26108 this.initSplitButton();
26109 },
26110 _onPickTable: function (numCols, numRows){
26111 if (this.fireEvent('picktable', numCols, numRows) !== false) {
26112 this.popup.hide();
26113 }
26114 }
26115 };
26116 utils.inherits(TableButton, SplitButton);
26117
26118})();
26119
26120
26121// ui/autotypesetpicker.js
26122///import core
26123///import uicore
26124(function () {
26125 var utils = baidu.editor.utils,
26126 UIBase = baidu.editor.ui.UIBase;
26127
26128 var AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker = function (options) {
26129 this.initOptions(options);
26130 this.initAutoTypeSetPicker();
26131 };
26132 AutoTypeSetPicker.prototype = {
26133 initAutoTypeSetPicker:function () {
26134 this.initUIBase();
26135 },
26136 getHtmlTpl:function () {
26137 var me = this.editor,
26138 opt = me.options.autotypeset,
26139 lang = me.getLang("autoTypeSet");
26140
26141 var textAlignInputName = 'textAlignValue' + me.uid,
26142 imageBlockInputName = 'imageBlockLineValue' + me.uid,
26143 symbolConverInputName = 'symbolConverValue' + me.uid;
26144
26145 return '<div id="##" class="edui-autotypesetpicker %%">' +
26146 '<div class="edui-autotypesetpicker-body">' +
26147 '<table >' +
26148 '<tr><td nowrap><input type="checkbox" name="mergeEmptyline" ' + (opt["mergeEmptyline"] ? "checked" : "" ) + '>' + lang.mergeLine + '</td><td colspan="2"><input type="checkbox" name="removeEmptyline" ' + (opt["removeEmptyline"] ? "checked" : "" ) + '>' + lang.delLine + '</td></tr>' +
26149 '<tr><td nowrap><input type="checkbox" name="removeClass" ' + (opt["removeClass"] ? "checked" : "" ) + '>' + lang.removeFormat + '</td><td colspan="2"><input type="checkbox" name="indent" ' + (opt["indent"] ? "checked" : "" ) + '>' + lang.indent + '</td></tr>' +
26150 '<tr>' +
26151 '<td nowrap><input type="checkbox" name="textAlign" ' + (opt["textAlign"] ? "checked" : "" ) + '>' + lang.alignment + '</td>' +
26152 '<td colspan="2" id="' + textAlignInputName + '">' +
26153 '<input type="radio" name="'+ textAlignInputName +'" value="left" ' + ((opt["textAlign"] && opt["textAlign"] == "left") ? "checked" : "") + '>' + me.getLang("justifyleft") +
26154 '<input type="radio" name="'+ textAlignInputName +'" value="center" ' + ((opt["textAlign"] && opt["textAlign"] == "center") ? "checked" : "") + '>' + me.getLang("justifycenter") +
26155 '<input type="radio" name="'+ textAlignInputName +'" value="right" ' + ((opt["textAlign"] && opt["textAlign"] == "right") ? "checked" : "") + '>' + me.getLang("justifyright") +
26156 '</td>' +
26157 '</tr>' +
26158 '<tr>' +
26159 '<td nowrap><input type="checkbox" name="imageBlockLine" ' + (opt["imageBlockLine"] ? "checked" : "" ) + '>' + lang.imageFloat + '</td>' +
26160 '<td nowrap id="'+ imageBlockInputName +'">' +
26161 '<input type="radio" name="'+ imageBlockInputName +'" value="none" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "none") ? "checked" : "") + '>' + me.getLang("default") +
26162 '<input type="radio" name="'+ imageBlockInputName +'" value="left" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "left") ? "checked" : "") + '>' + me.getLang("justifyleft") +
26163 '<input type="radio" name="'+ imageBlockInputName +'" value="center" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "center") ? "checked" : "") + '>' + me.getLang("justifycenter") +
26164 '<input type="radio" name="'+ imageBlockInputName +'" value="right" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "right") ? "checked" : "") + '>' + me.getLang("justifyright") +
26165 '</td>' +
26166 '</tr>' +
26167 '<tr><td nowrap><input type="checkbox" name="clearFontSize" ' + (opt["clearFontSize"] ? "checked" : "" ) + '>' + lang.removeFontsize + '</td><td colspan="2"><input type="checkbox" name="clearFontFamily" ' + (opt["clearFontFamily"] ? "checked" : "" ) + '>' + lang.removeFontFamily + '</td></tr>' +
26168 '<tr><td nowrap colspan="3"><input type="checkbox" name="removeEmptyNode" ' + (opt["removeEmptyNode"] ? "checked" : "" ) + '>' + lang.removeHtml + '</td></tr>' +
26169 '<tr><td nowrap colspan="3"><input type="checkbox" name="pasteFilter" ' + (opt["pasteFilter"] ? "checked" : "" ) + '>' + lang.pasteFilter + '</td></tr>' +
26170 '<tr>' +
26171 '<td nowrap><input type="checkbox" name="symbolConver" ' + (opt["bdc2sb"] || opt["tobdc"] ? "checked" : "" ) + '>' + lang.symbol + '</td>' +
26172 '<td id="' + symbolConverInputName + '">' +
26173 '<input type="radio" name="bdc" value="bdc2sb" ' + (opt["bdc2sb"] ? "checked" : "" ) + '>' + lang.bdc2sb +
26174 '<input type="radio" name="bdc" value="tobdc" ' + (opt["tobdc"] ? "checked" : "" ) + '>' + lang.tobdc + '' +
26175 '</td>' +
26176 '<td nowrap align="right"><button >' + lang.run + '</button></td>' +
26177 '</tr>' +
26178 '</table>' +
26179 '</div>' +
26180 '</div>';
26181
26182
26183 },
26184 _UIBase_render:UIBase.prototype.render
26185 };
26186 utils.inherits(AutoTypeSetPicker, UIBase);
26187})();
26188
26189
26190// ui/autotypesetbutton.js
26191///import core
26192///import uicore
26193///import ui/popup.js
26194///import ui/autotypesetpicker.js
26195///import ui/splitbutton.js
26196(function (){
26197 var utils = baidu.editor.utils,
26198 Popup = baidu.editor.ui.Popup,
26199 AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker,
26200 SplitButton = baidu.editor.ui.SplitButton,
26201 AutoTypeSetButton = baidu.editor.ui.AutoTypeSetButton = function (options){
26202 this.initOptions(options);
26203 this.initAutoTypeSetButton();
26204 };
26205 function getPara(me){
26206
26207 var opt = {},
26208 cont = me.getDom(),
26209 editorId = me.editor.uid,
26210 inputType = null,
26211 attrName = null,
26212 ipts = domUtils.getElementsByTagName(cont,"input");
26213 for(var i=ipts.length-1,ipt;ipt=ipts[i--];){
26214 inputType = ipt.getAttribute("type");
26215 if(inputType=="checkbox"){
26216 attrName = ipt.getAttribute("name");
26217 opt[attrName] && delete opt[attrName];
26218 if(ipt.checked){
26219 var attrValue = document.getElementById( attrName + "Value" + editorId );
26220 if(attrValue){
26221 if(/input/ig.test(attrValue.tagName)){
26222 opt[attrName] = attrValue.value;
26223 } else {
26224 var iptChilds = attrValue.getElementsByTagName("input");
26225 for(var j=iptChilds.length-1,iptchild;iptchild=iptChilds[j--];){
26226 if(iptchild.checked){
26227 opt[attrName] = iptchild.value;
26228 break;
26229 }
26230 }
26231 }
26232 } else {
26233 opt[attrName] = true;
26234 }
26235 } else {
26236 opt[attrName] = false;
26237 }
26238 } else {
26239 opt[ipt.getAttribute("value")] = ipt.checked;
26240 }
26241
26242 }
26243
26244 var selects = domUtils.getElementsByTagName(cont,"select");
26245 for(var i=0,si;si=selects[i++];){
26246 var attr = si.getAttribute('name');
26247 opt[attr] = opt[attr] ? si.value : '';
26248 }
26249
26250 utils.extend(me.editor.options.autotypeset,opt);
26251
26252 me.editor.setPreferences('autotypeset', opt);
26253 }
26254
26255 AutoTypeSetButton.prototype = {
26256 initAutoTypeSetButton: function (){
26257
26258 var me = this;
26259 this.popup = new Popup({
26260 //传入配置参数
26261 content: new AutoTypeSetPicker({editor:me.editor}),
26262 'editor':me.editor,
26263 hide : function(){
26264 if (!this._hidden && this.getDom()) {
26265 getPara(this);
26266 this.getDom().style.display = 'none';
26267 this._hidden = true;
26268 this.fireEvent('hide');
26269 }
26270 }
26271 });
26272 var flag = 0;
26273 this.popup.addListener('postRenderAfter',function(){
26274 var popupUI = this;
26275 if(flag)return;
26276 var cont = this.getDom(),
26277 btn = cont.getElementsByTagName('button')[0];
26278
26279 btn.onclick = function(){
26280 getPara(popupUI);
26281 me.editor.execCommand('autotypeset');
26282 popupUI.hide()
26283 };
26284
26285 domUtils.on(cont, 'click', function(e) {
26286 var target = e.target || e.srcElement,
26287 editorId = me.editor.uid;
26288 if (target && target.tagName == 'INPUT') {
26289
26290 // 点击图片浮动的checkbox,去除对应的radio
26291 if (target.name == 'imageBlockLine' || target.name == 'textAlign' || target.name == 'symbolConver') {
26292 var checked = target.checked,
26293 radioTd = document.getElementById( target.name + 'Value' + editorId),
26294 radios = radioTd.getElementsByTagName('input'),
26295 defalutSelect = {
26296 'imageBlockLine': 'none',
26297 'textAlign': 'left',
26298 'symbolConver': 'tobdc'
26299 };
26300
26301 for (var i = 0; i < radios.length; i++) {
26302 if (checked) {
26303 if (radios[i].value == defalutSelect[target.name]) {
26304 radios[i].checked = 'checked';
26305 }
26306 } else {
26307 radios[i].checked = false;
26308 }
26309 }
26310 }
26311 // 点击radio,选中对应的checkbox
26312 if (target.name == ('imageBlockLineValue' + editorId) || target.name == ('textAlignValue' + editorId) || target.name == 'bdc') {
26313 var checkboxs = target.parentNode.previousSibling.getElementsByTagName('input');
26314 checkboxs && (checkboxs[0].checked = true);
26315 }
26316
26317 getPara(popupUI);
26318 }
26319 });
26320
26321 flag = 1;
26322 });
26323 this.initSplitButton();
26324 }
26325 };
26326 utils.inherits(AutoTypeSetButton, SplitButton);
26327
26328})();
26329
26330
26331// ui/cellalignpicker.js
26332///import core
26333///import uicore
26334(function () {
26335 var utils = baidu.editor.utils,
26336 Popup = baidu.editor.ui.Popup,
26337 Stateful = baidu.editor.ui.Stateful,
26338 UIBase = baidu.editor.ui.UIBase;
26339
26340 /**
26341 * 该参数将新增一个参数: selected, 参数类型为一个Object, 形如{ 'align': 'center', 'valign': 'top' }, 表示单元格的初始
26342 * 对齐状态为: 竖直居上,水平居中; 其中 align的取值为:'center', 'left', 'right'; valign的取值为: 'top', 'middle', 'bottom'
26343 * @update 2013/4/2 hancong03@baidu.com
26344 */
26345 var CellAlignPicker = baidu.editor.ui.CellAlignPicker = function (options) {
26346 this.initOptions(options);
26347 this.initSelected();
26348 this.initCellAlignPicker();
26349 };
26350 CellAlignPicker.prototype = {
26351 //初始化选中状态, 该方法将根据传递进来的参数获取到应该选中的对齐方式图标的索引
26352 initSelected: function(){
26353
26354 var status = {
26355
26356 valign: {
26357 top: 0,
26358 middle: 1,
26359 bottom: 2
26360 },
26361 align: {
26362 left: 0,
26363 center: 1,
26364 right: 2
26365 },
26366 count: 3
26367
26368 },
26369 result = -1;
26370
26371 if( this.selected ) {
26372 this.selectedIndex = status.valign[ this.selected.valign ] * status.count + status.align[ this.selected.align ];
26373 }
26374
26375 },
26376 initCellAlignPicker:function () {
26377 this.initUIBase();
26378 this.Stateful_init();
26379 },
26380 getHtmlTpl:function () {
26381
26382 var alignType = [ 'left', 'center', 'right' ],
26383 COUNT = 9,
26384 tempClassName = null,
26385 tempIndex = -1,
26386 tmpl = [];
26387
26388
26389 for( var i= 0; i<COUNT; i++ ) {
26390
26391 tempClassName = this.selectedIndex === i ? ' class="edui-cellalign-selected" ' : '';
26392 tempIndex = i % 3;
26393
26394 tempIndex === 0 && tmpl.push('<tr>');
26395
26396 tmpl.push( '<td index="'+ i +'" ' + tempClassName + ' stateful><div class="edui-icon edui-'+ alignType[ tempIndex ] +'"></div></td>' );
26397
26398 tempIndex === 2 && tmpl.push('</tr>');
26399
26400 }
26401
26402 return '<div id="##" class="edui-cellalignpicker %%">' +
26403 '<div class="edui-cellalignpicker-body">' +
26404 '<table onclick="$$._onClick(event);">' +
26405 tmpl.join('') +
26406 '</table>' +
26407 '</div>' +
26408 '</div>';
26409 },
26410 getStateDom: function (){
26411 return this.target;
26412 },
26413 _onClick: function (evt){
26414 var target= evt.target || evt.srcElement;
26415 if(/icon/.test(target.className)){
26416 this.items[target.parentNode.getAttribute("index")].onclick();
26417 Popup.postHide(evt);
26418 }
26419 },
26420 _UIBase_render:UIBase.prototype.render
26421 };
26422 utils.inherits(CellAlignPicker, UIBase);
26423 utils.extend(CellAlignPicker.prototype, Stateful,true);
26424})();
26425
26426
26427
26428
26429
26430// ui/pastepicker.js
26431///import core
26432///import uicore
26433(function () {
26434 var utils = baidu.editor.utils,
26435 Stateful = baidu.editor.ui.Stateful,
26436 uiUtils = baidu.editor.ui.uiUtils,
26437 UIBase = baidu.editor.ui.UIBase;
26438
26439 var PastePicker = baidu.editor.ui.PastePicker = function (options) {
26440 this.initOptions(options);
26441 this.initPastePicker();
26442 };
26443 PastePicker.prototype = {
26444 initPastePicker:function () {
26445 this.initUIBase();
26446 this.Stateful_init();
26447 },
26448 getHtmlTpl:function () {
26449 return '<div class="edui-pasteicon" onclick="$$._onClick(this)"></div>' +
26450 '<div class="edui-pastecontainer">' +
26451 '<div class="edui-title">' + this.editor.getLang("pasteOpt") + '</div>' +
26452 '<div class="edui-button">' +
26453 '<div title="' + this.editor.getLang("pasteSourceFormat") + '" onclick="$$.format(false)" stateful>' +
26454 '<div class="edui-richtxticon"></div></div>' +
26455 '<div title="' + this.editor.getLang("tagFormat") + '" onclick="$$.format(2)" stateful>' +
26456 '<div class="edui-tagicon"></div></div>' +
26457 '<div title="' + this.editor.getLang("pasteTextFormat") + '" onclick="$$.format(true)" stateful>' +
26458 '<div class="edui-plaintxticon"></div></div>' +
26459 '</div>' +
26460 '</div>' +
26461 '</div>'
26462 },
26463 getStateDom:function () {
26464 return this.target;
26465 },
26466 format:function (param) {
26467 this.editor.ui._isTransfer = true;
26468 this.editor.fireEvent('pasteTransfer', param);
26469 },
26470 _onClick:function (cur) {
26471 var node = domUtils.getNextDomNode(cur),
26472 screenHt = uiUtils.getViewportRect().height,
26473 subPop = uiUtils.getClientRect(node);
26474
26475 if ((subPop.top + subPop.height) > screenHt)
26476 node.style.top = (-subPop.height - cur.offsetHeight) + "px";
26477 else
26478 node.style.top = "";
26479
26480 if (/hidden/ig.test(domUtils.getComputedStyle(node, "visibility"))) {
26481 node.style.visibility = "visible";
26482 domUtils.addClass(cur, "edui-state-opened");
26483 } else {
26484 node.style.visibility = "hidden";
26485 domUtils.removeClasses(cur, "edui-state-opened")
26486 }
26487 },
26488 _UIBase_render:UIBase.prototype.render
26489 };
26490 utils.inherits(PastePicker, UIBase);
26491 utils.extend(PastePicker.prototype, Stateful, true);
26492})();
26493
26494
26495
26496
26497
26498
26499// ui/toolbar.js
26500(function (){
26501 var utils = baidu.editor.utils,
26502 uiUtils = baidu.editor.ui.uiUtils,
26503 UIBase = baidu.editor.ui.UIBase,
26504 Toolbar = baidu.editor.ui.Toolbar = function (options){
26505 this.initOptions(options);
26506 this.initToolbar();
26507 };
26508 Toolbar.prototype = {
26509 items: null,
26510 initToolbar: function (){
26511 this.items = this.items || [];
26512 this.initUIBase();
26513 },
26514 add: function (item,index){
26515 if(index === undefined){
26516 this.items.push(item);
26517 }else{
26518 this.items.splice(index,0,item)
26519 }
26520
26521 },
26522 getHtmlTpl: function (){
26523 var buff = [];
26524 for (var i=0; i<this.items.length; i++) {
26525 buff[i] = this.items[i].renderHtml();
26526 }
26527 return '<div id="##" class="edui-toolbar %%" onselectstart="return false;" onmousedown="return $$._onMouseDown(event, this);">' +
26528 buff.join('') +
26529 '</div>'
26530 },
26531 postRender: function (){
26532 var box = this.getDom();
26533 for (var i=0; i<this.items.length; i++) {
26534 this.items[i].postRender();
26535 }
26536 uiUtils.makeUnselectable(box);
26537 },
26538 _onMouseDown: function (e){
26539 var target = e.target || e.srcElement,
26540 tagName = target && target.tagName && target.tagName.toLowerCase();
26541 if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
26542 return false;
26543 }
26544 }
26545 };
26546 utils.inherits(Toolbar, UIBase);
26547
26548})();
26549
26550
26551// ui/menu.js
26552///import core
26553///import uicore
26554///import ui\popup.js
26555///import ui\stateful.js
26556(function () {
26557 var utils = baidu.editor.utils,
26558 domUtils = baidu.editor.dom.domUtils,
26559 uiUtils = baidu.editor.ui.uiUtils,
26560 UIBase = baidu.editor.ui.UIBase,
26561 Popup = baidu.editor.ui.Popup,
26562 Stateful = baidu.editor.ui.Stateful,
26563 CellAlignPicker = baidu.editor.ui.CellAlignPicker,
26564
26565 Menu = baidu.editor.ui.Menu = function (options) {
26566 this.initOptions(options);
26567 this.initMenu();
26568 };
26569
26570 var menuSeparator = {
26571 renderHtml:function () {
26572 return '<div class="edui-menuitem edui-menuseparator"><div class="edui-menuseparator-inner"></div></div>';
26573 },
26574 postRender:function () {
26575 },
26576 queryAutoHide:function () {
26577 return true;
26578 }
26579 };
26580 Menu.prototype = {
26581 items:null,
26582 uiName:'menu',
26583 initMenu:function () {
26584 this.items = this.items || [];
26585 this.initPopup();
26586 this.initItems();
26587 },
26588 initItems:function () {
26589 for (var i = 0; i < this.items.length; i++) {
26590 var item = this.items[i];
26591 if (item == '-') {
26592 this.items[i] = this.getSeparator();
26593 } else if (!(item instanceof MenuItem)) {
26594 item.editor = this.editor;
26595 item.theme = this.editor.options.theme;
26596 this.items[i] = this.createItem(item);
26597 }
26598 }
26599 },
26600 getSeparator:function () {
26601 return menuSeparator;
26602 },
26603 createItem:function (item) {
26604 //新增一个参数menu, 该参数存储了menuItem所对应的menu引用
26605 item.menu = this;
26606 return new MenuItem(item);
26607 },
26608 _Popup_getContentHtmlTpl:Popup.prototype.getContentHtmlTpl,
26609 getContentHtmlTpl:function () {
26610 if (this.items.length == 0) {
26611 return this._Popup_getContentHtmlTpl();
26612 }
26613 var buff = [];
26614 for (var i = 0; i < this.items.length; i++) {
26615 var item = this.items[i];
26616 buff[i] = item.renderHtml();
26617 }
26618 return ('<div class="%%-body">' + buff.join('') + '</div>');
26619 },
26620 _Popup_postRender:Popup.prototype.postRender,
26621 postRender:function () {
26622 var me = this;
26623 for (var i = 0; i < this.items.length; i++) {
26624 var item = this.items[i];
26625 item.ownerMenu = this;
26626 item.postRender();
26627 }
26628 domUtils.on(this.getDom(), 'mouseover', function (evt) {
26629 evt = evt || event;
26630 var rel = evt.relatedTarget || evt.fromElement;
26631 var el = me.getDom();
26632 if (!uiUtils.contains(el, rel) && el !== rel) {
26633 me.fireEvent('over');
26634 }
26635 });
26636 this._Popup_postRender();
26637 },
26638 queryAutoHide:function (el) {
26639 if (el) {
26640 if (uiUtils.contains(this.getDom(), el)) {
26641 return false;
26642 }
26643 for (var i = 0; i < this.items.length; i++) {
26644 var item = this.items[i];
26645 if (item.queryAutoHide(el) === false) {
26646 return false;
26647 }
26648 }
26649 }
26650 },
26651 clearItems:function () {
26652 for (var i = 0; i < this.items.length; i++) {
26653 var item = this.items[i];
26654 clearTimeout(item._showingTimer);
26655 clearTimeout(item._closingTimer);
26656 if (item.subMenu) {
26657 item.subMenu.destroy();
26658 }
26659 }
26660 this.items = [];
26661 },
26662 destroy:function () {
26663 if (this.getDom()) {
26664 domUtils.remove(this.getDom());
26665 }
26666 this.clearItems();
26667 },
26668 dispose:function () {
26669 this.destroy();
26670 }
26671 };
26672 utils.inherits(Menu, Popup);
26673
26674 /**
26675 * @update 2013/04/03 hancong03 新增一个参数menu, 该参数存储了menuItem所对应的menu引用
26676 * @type {Function}
26677 */
26678 var MenuItem = baidu.editor.ui.MenuItem = function (options) {
26679 this.initOptions(options);
26680 this.initUIBase();
26681 this.Stateful_init();
26682 if (this.subMenu && !(this.subMenu instanceof Menu)) {
26683 if (options.className && options.className.indexOf("aligntd") != -1) {
26684 var me = this;
26685
26686 //获取单元格对齐初始状态
26687 this.subMenu.selected = this.editor.queryCommandValue( 'cellalignment' );
26688
26689 this.subMenu = new Popup({
26690 content:new CellAlignPicker(this.subMenu),
26691 parentMenu:me,
26692 editor:me.editor,
26693 destroy:function () {
26694 if (this.getDom()) {
26695 domUtils.remove(this.getDom());
26696 }
26697 }
26698 });
26699 this.subMenu.addListener("postRenderAfter", function () {
26700 domUtils.on(this.getDom(), "mouseover", function () {
26701 me.addState('opened');
26702 });
26703 });
26704 } else {
26705 this.subMenu = new Menu(this.subMenu);
26706 }
26707 }
26708 };
26709 MenuItem.prototype = {
26710 label:'',
26711 subMenu:null,
26712 ownerMenu:null,
26713 uiName:'menuitem',
26714 alwalysHoverable:true,
26715 getHtmlTpl:function () {
26716 return '<div id="##" class="%%" stateful onclick="$$._onClick(event, this);">' +
26717 '<div class="%%-body">' +
26718 this.renderLabelHtml() +
26719 '</div>' +
26720 '</div>';
26721 },
26722 postRender:function () {
26723 var me = this;
26724 this.addListener('over', function () {
26725 me.ownerMenu.fireEvent('submenuover', me);
26726 if (me.subMenu) {
26727 me.delayShowSubMenu();
26728 }
26729 });
26730 if (this.subMenu) {
26731 this.getDom().className += ' edui-hassubmenu';
26732 this.subMenu.render();
26733 this.addListener('out', function () {
26734 me.delayHideSubMenu();
26735 });
26736 this.subMenu.addListener('over', function () {
26737 clearTimeout(me._closingTimer);
26738 me._closingTimer = null;
26739 me.addState('opened');
26740 });
26741 this.ownerMenu.addListener('hide', function () {
26742 me.hideSubMenu();
26743 });
26744 this.ownerMenu.addListener('submenuover', function (t, subMenu) {
26745 if (subMenu !== me) {
26746 me.delayHideSubMenu();
26747 }
26748 });
26749 this.subMenu._bakQueryAutoHide = this.subMenu.queryAutoHide;
26750 this.subMenu.queryAutoHide = function (el) {
26751 if (el && uiUtils.contains(me.getDom(), el)) {
26752 return false;
26753 }
26754 return this._bakQueryAutoHide(el);
26755 };
26756 }
26757 this.getDom().style.tabIndex = '-1';
26758 uiUtils.makeUnselectable(this.getDom());
26759 this.Stateful_postRender();
26760 },
26761 delayShowSubMenu:function () {
26762 var me = this;
26763 if (!me.isDisabled()) {
26764 me.addState('opened');
26765 clearTimeout(me._showingTimer);
26766 clearTimeout(me._closingTimer);
26767 me._closingTimer = null;
26768 me._showingTimer = setTimeout(function () {
26769 me.showSubMenu();
26770 }, 250);
26771 }
26772 },
26773 delayHideSubMenu:function () {
26774 var me = this;
26775 if (!me.isDisabled()) {
26776 me.removeState('opened');
26777 clearTimeout(me._showingTimer);
26778 if (!me._closingTimer) {
26779 me._closingTimer = setTimeout(function () {
26780 if (!me.hasState('opened')) {
26781 me.hideSubMenu();
26782 }
26783 me._closingTimer = null;
26784 }, 400);
26785 }
26786 }
26787 },
26788 renderLabelHtml:function () {
26789 return '<div class="edui-arrow"></div>' +
26790 '<div class="edui-box edui-icon"></div>' +
26791 '<div class="edui-box edui-label %%-label">' + (this.label || '') + '</div>';
26792 },
26793 getStateDom:function () {
26794 return this.getDom();
26795 },
26796 queryAutoHide:function (el) {
26797 if (this.subMenu && this.hasState('opened')) {
26798 return this.subMenu.queryAutoHide(el);
26799 }
26800 },
26801 _onClick:function (event, this_) {
26802 if (this.hasState('disabled')) return;
26803 if (this.fireEvent('click', event, this_) !== false) {
26804 if (this.subMenu) {
26805 this.showSubMenu();
26806 } else {
26807 Popup.postHide(event);
26808 }
26809 }
26810 },
26811 showSubMenu:function () {
26812 var rect = uiUtils.getClientRect(this.getDom());
26813 rect.right -= 5;
26814 rect.left += 2;
26815 rect.width -= 7;
26816 rect.top -= 4;
26817 rect.bottom += 4;
26818 rect.height += 8;
26819 this.subMenu.showAnchorRect(rect, true, true);
26820 },
26821 hideSubMenu:function () {
26822 this.subMenu.hide();
26823 }
26824 };
26825 utils.inherits(MenuItem, UIBase);
26826 utils.extend(MenuItem.prototype, Stateful, true);
26827})();
26828
26829
26830// ui/combox.js
26831///import core
26832///import uicore
26833///import ui/menu.js
26834///import ui/splitbutton.js
26835(function (){
26836 // todo: menu和item提成通用list
26837 var utils = baidu.editor.utils,
26838 uiUtils = baidu.editor.ui.uiUtils,
26839 Menu = baidu.editor.ui.Menu,
26840 SplitButton = baidu.editor.ui.SplitButton,
26841 Combox = baidu.editor.ui.Combox = function (options){
26842 this.initOptions(options);
26843 this.initCombox();
26844 };
26845 Combox.prototype = {
26846 uiName: 'combox',
26847 onbuttonclick:function () {
26848 this.showPopup();
26849 },
26850 initCombox: function (){
26851 var me = this;
26852 this.items = this.items || [];
26853 for (var i=0; i<this.items.length; i++) {
26854 var item = this.items[i];
26855 item.uiName = 'listitem';
26856 item.index = i;
26857 item.onclick = function (){
26858 me.selectByIndex(this.index);
26859 };
26860 }
26861 this.popup = new Menu({
26862 items: this.items,
26863 uiName: 'list',
26864 editor:this.editor,
26865 captureWheel: true,
26866 combox: this
26867 });
26868
26869 this.initSplitButton();
26870 },
26871 _SplitButton_postRender: SplitButton.prototype.postRender,
26872 postRender: function (){
26873 this._SplitButton_postRender();
26874 this.setLabel(this.label || '');
26875 this.setValue(this.initValue || '');
26876 },
26877 showPopup: function (){
26878 var rect = uiUtils.getClientRect(this.getDom());
26879 rect.top += 1;
26880 rect.bottom -= 1;
26881 rect.height -= 2;
26882 this.popup.showAnchorRect(rect);
26883 },
26884 getValue: function (){
26885 return this.value;
26886 },
26887 setValue: function (value){
26888 var index = this.indexByValue(value);
26889 if (index != -1) {
26890 this.selectedIndex = index;
26891 this.setLabel(this.items[index].label);
26892 this.value = this.items[index].value;
26893 } else {
26894 this.selectedIndex = -1;
26895 this.setLabel(this.getLabelForUnknowValue(value));
26896 this.value = value;
26897 }
26898 },
26899 setLabel: function (label){
26900 this.getDom('button_body').innerHTML = label;
26901 this.label = label;
26902 },
26903 getLabelForUnknowValue: function (value){
26904 return value;
26905 },
26906 indexByValue: function (value){
26907 for (var i=0; i<this.items.length; i++) {
26908 if (value == this.items[i].value) {
26909 return i;
26910 }
26911 }
26912 return -1;
26913 },
26914 getItem: function (index){
26915 return this.items[index];
26916 },
26917 selectByIndex: function (index){
26918 if (index < this.items.length && this.fireEvent('select', index) !== false) {
26919 this.selectedIndex = index;
26920 this.value = this.items[index].value;
26921 this.setLabel(this.items[index].label);
26922 }
26923 }
26924 };
26925 utils.inherits(Combox, SplitButton);
26926})();
26927
26928
26929// ui/dialog.js
26930///import core
26931///import uicore
26932///import ui/mask.js
26933///import ui/button.js
26934(function (){
26935 var utils = baidu.editor.utils,
26936 domUtils = baidu.editor.dom.domUtils,
26937 uiUtils = baidu.editor.ui.uiUtils,
26938 Mask = baidu.editor.ui.Mask,
26939 UIBase = baidu.editor.ui.UIBase,
26940 Button = baidu.editor.ui.Button,
26941 Dialog = baidu.editor.ui.Dialog = function (options){
26942 if(options.name){
26943 var name = options.name;
26944 var cssRules = options.cssRules;
26945 if(!options.className){
26946 options.className = 'edui-for-' + name;
26947 }
26948 if(cssRules){
26949 options.cssRules = '.edui-default .edui-for-'+ name +' .edui-dialog-content {'+ cssRules +'}'
26950 }
26951 }
26952 this.initOptions(utils.extend({
26953 autoReset: true,
26954 draggable: true,
26955 onok: function (){},
26956 oncancel: function (){},
26957 onclose: function (t, ok){
26958 return ok ? this.onok() : this.oncancel();
26959 },
26960 //是否控制dialog中的scroll事件, 默认为不阻止
26961 holdScroll: false
26962 },options));
26963 this.initDialog();
26964 };
26965 var modalMask;
26966 var dragMask;
26967 var activeDialog;
26968 Dialog.prototype = {
26969 draggable: false,
26970 uiName: 'dialog',
26971 initDialog: function (){
26972 var me = this,
26973 theme=this.editor.options.theme;
26974 if(this.cssRules){
26975 utils.cssRule('edui-customize-'+this.name+'-style',this.cssRules);
26976 }
26977 this.initUIBase();
26978 this.modalMask = (modalMask || (modalMask = new Mask({
26979 className: 'edui-dialog-modalmask',
26980 theme:theme,
26981 onclick: function (){
26982 activeDialog && activeDialog.close(false);
26983 }
26984 })));
26985 this.dragMask = (dragMask || (dragMask = new Mask({
26986 className: 'edui-dialog-dragmask',
26987 theme:theme
26988 })));
26989 this.closeButton = new Button({
26990 className: 'edui-dialog-closebutton',
26991 title: me.closeDialog,
26992 theme:theme,
26993 onclick: function (){
26994 me.close(false);
26995 }
26996 });
26997
26998 this.fullscreen && this.initResizeEvent();
26999
27000 if (this.buttons) {
27001 for (var i=0; i<this.buttons.length; i++) {
27002 if (!(this.buttons[i] instanceof Button)) {
27003 this.buttons[i] = new Button(utils.extend(this.buttons[i],{
27004 editor : this.editor
27005 },true));
27006 }
27007 }
27008 }
27009 },
27010 initResizeEvent: function () {
27011
27012 var me = this;
27013
27014 domUtils.on( window, "resize", function () {
27015
27016 if ( me._hidden || me._hidden === undefined ) {
27017 return;
27018 }
27019
27020 if ( me.__resizeTimer ) {
27021 window.clearTimeout( me.__resizeTimer );
27022 }
27023
27024 me.__resizeTimer = window.setTimeout( function () {
27025
27026 me.__resizeTimer = null;
27027
27028 var dialogWrapNode = me.getDom(),
27029 contentNode = me.getDom('content'),
27030 wrapRect = UE.ui.uiUtils.getClientRect( dialogWrapNode ),
27031 contentRect = UE.ui.uiUtils.getClientRect( contentNode ),
27032 vpRect = uiUtils.getViewportRect();
27033
27034 contentNode.style.width = ( vpRect.width - wrapRect.width + contentRect.width ) + "px";
27035 contentNode.style.height = ( vpRect.height - wrapRect.height + contentRect.height ) + "px";
27036
27037 dialogWrapNode.style.width = vpRect.width + "px";
27038 dialogWrapNode.style.height = vpRect.height + "px";
27039
27040 me.fireEvent( "resize" );
27041
27042 }, 100 );
27043
27044 } );
27045
27046 },
27047 fitSize: function (){
27048 var popBodyEl = this.getDom('body');
27049// if (!(baidu.editor.browser.ie && baidu.editor.browser.version == 7)) {
27050// uiUtils.removeStyle(popBodyEl, 'width');
27051// uiUtils.removeStyle(popBodyEl, 'height');
27052// }
27053 var size = this.mesureSize();
27054 popBodyEl.style.width = size.width + 'px';
27055 popBodyEl.style.height = size.height + 'px';
27056 return size;
27057 },
27058 safeSetOffset: function (offset){
27059 var me = this;
27060 var el = me.getDom();
27061 var vpRect = uiUtils.getViewportRect();
27062 var rect = uiUtils.getClientRect(el);
27063 var left = offset.left;
27064 if (left + rect.width > vpRect.right) {
27065 left = vpRect.right - rect.width;
27066 }
27067 var top = offset.top;
27068 if (top + rect.height > vpRect.bottom) {
27069 top = vpRect.bottom - rect.height;
27070 }
27071 el.style.left = Math.max(left, 0) + 'px';
27072 el.style.top = Math.max(top, 0) + 'px';
27073 },
27074 showAtCenter: function (){
27075
27076 var vpRect = uiUtils.getViewportRect();
27077
27078 if ( !this.fullscreen ) {
27079 this.getDom().style.display = '';
27080 var popSize = this.fitSize();
27081 var titleHeight = this.getDom('titlebar').offsetHeight | 0;
27082 var left = vpRect.width / 2 - popSize.width / 2;
27083 var top = vpRect.height / 2 - (popSize.height - titleHeight) / 2 - titleHeight;
27084 var popEl = this.getDom();
27085 this.safeSetOffset({
27086 left: Math.max(left | 0, 0),
27087 top: Math.max(top | 0, 0)
27088 });
27089 if (!domUtils.hasClass(popEl, 'edui-state-centered')) {
27090 popEl.className += ' edui-state-centered';
27091 }
27092 } else {
27093 var dialogWrapNode = this.getDom(),
27094 contentNode = this.getDom('content');
27095
27096 dialogWrapNode.style.display = "block";
27097
27098 var wrapRect = UE.ui.uiUtils.getClientRect( dialogWrapNode ),
27099 contentRect = UE.ui.uiUtils.getClientRect( contentNode );
27100 dialogWrapNode.style.left = "-100000px";
27101
27102 contentNode.style.width = ( vpRect.width - wrapRect.width + contentRect.width ) + "px";
27103 contentNode.style.height = ( vpRect.height - wrapRect.height + contentRect.height ) + "px";
27104
27105 dialogWrapNode.style.width = vpRect.width + "px";
27106 dialogWrapNode.style.height = vpRect.height + "px";
27107 dialogWrapNode.style.left = 0;
27108
27109 //保存环境的overflow值
27110 this._originalContext = {
27111 html: {
27112 overflowX: document.documentElement.style.overflowX,
27113 overflowY: document.documentElement.style.overflowY
27114 },
27115 body: {
27116 overflowX: document.body.style.overflowX,
27117 overflowY: document.body.style.overflowY
27118 }
27119 };
27120
27121 document.documentElement.style.overflowX = 'hidden';
27122 document.documentElement.style.overflowY = 'hidden';
27123 document.body.style.overflowX = 'hidden';
27124 document.body.style.overflowY = 'hidden';
27125
27126 }
27127
27128 this._show();
27129 },
27130 getContentHtml: function (){
27131 var contentHtml = '';
27132 if (typeof this.content == 'string') {
27133 contentHtml = this.content;
27134 } else if (this.iframeUrl) {
27135 contentHtml = '<span id="'+ this.id +'_contmask" class="dialogcontmask"></span><iframe id="'+ this.id +
27136 '_iframe" class="%%-iframe" height="100%" width="100%" frameborder="0" src="'+ this.iframeUrl +'"></iframe>';
27137 }
27138 return contentHtml;
27139 },
27140 getHtmlTpl: function (){
27141 var footHtml = '';
27142
27143 if (this.buttons) {
27144 var buff = [];
27145 for (var i=0; i<this.buttons.length; i++) {
27146 buff[i] = this.buttons[i].renderHtml();
27147 }
27148 footHtml = '<div class="%%-foot">' +
27149 '<div id="##_buttons" class="%%-buttons">' + buff.join('') + '</div>' +
27150 '</div>';
27151 }
27152
27153 return '<div id="##" class="%%"><div '+ ( !this.fullscreen ? 'class="%%"' : 'class="%%-wrap edui-dialog-fullscreen-flag"' ) +'><div id="##_body" class="%%-body">' +
27154 '<div class="%%-shadow"></div>' +
27155 '<div id="##_titlebar" class="%%-titlebar">' +
27156 '<div class="%%-draghandle" onmousedown="$$._onTitlebarMouseDown(event, this);">' +
27157 '<span class="%%-caption">' + (this.title || '') + '</span>' +
27158 '</div>' +
27159 this.closeButton.renderHtml() +
27160 '</div>' +
27161 '<div id="##_content" class="%%-content">'+ ( this.autoReset ? '' : this.getContentHtml()) +'</div>' +
27162 footHtml +
27163 '</div></div></div>';
27164 },
27165 postRender: function (){
27166 // todo: 保持居中/记住上次关闭位置选项
27167 if (!this.modalMask.getDom()) {
27168 this.modalMask.render();
27169 this.modalMask.hide();
27170 }
27171 if (!this.dragMask.getDom()) {
27172 this.dragMask.render();
27173 this.dragMask.hide();
27174 }
27175 var me = this;
27176 this.addListener('show', function (){
27177 me.modalMask.show(this.getDom().style.zIndex - 2);
27178 });
27179 this.addListener('hide', function (){
27180 me.modalMask.hide();
27181 });
27182 if (this.buttons) {
27183 for (var i=0; i<this.buttons.length; i++) {
27184 this.buttons[i].postRender();
27185 }
27186 }
27187 domUtils.on(window, 'resize', function (){
27188 setTimeout(function (){
27189 if (!me.isHidden()) {
27190 me.safeSetOffset(uiUtils.getClientRect(me.getDom()));
27191 }
27192 });
27193 });
27194
27195 //hold住scroll事件,防止dialog的滚动影响页面
27196// if( this.holdScroll ) {
27197//
27198// if( !me.iframeUrl ) {
27199// domUtils.on( document.getElementById( me.id + "_iframe"), !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
27200// domUtils.preventDefault(e);
27201// } );
27202// } else {
27203// me.addListener('dialogafterreset', function(){
27204// window.setTimeout(function(){
27205// var iframeWindow = document.getElementById( me.id + "_iframe").contentWindow;
27206//
27207// if( browser.ie ) {
27208//
27209// var timer = window.setInterval(function(){
27210//
27211// if( iframeWindow.document && iframeWindow.document.body ) {
27212// window.clearInterval( timer );
27213// timer = null;
27214// domUtils.on( iframeWindow.document.body, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
27215// domUtils.preventDefault(e);
27216// } );
27217// }
27218//
27219// }, 100);
27220//
27221// } else {
27222// domUtils.on( iframeWindow, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
27223// domUtils.preventDefault(e);
27224// } );
27225// }
27226//
27227// }, 1);
27228// });
27229// }
27230//
27231// }
27232 this._hide();
27233 },
27234 mesureSize: function (){
27235 var body = this.getDom('body');
27236 var width = uiUtils.getClientRect(this.getDom('content')).width;
27237 var dialogBodyStyle = body.style;
27238 dialogBodyStyle.width = width;
27239 return uiUtils.getClientRect(body);
27240 },
27241 _onTitlebarMouseDown: function (evt, el){
27242 if (this.draggable) {
27243 var rect;
27244 var vpRect = uiUtils.getViewportRect();
27245 var me = this;
27246 uiUtils.startDrag(evt, {
27247 ondragstart: function (){
27248 rect = uiUtils.getClientRect(me.getDom());
27249 me.getDom('contmask').style.visibility = 'visible';
27250 me.dragMask.show(me.getDom().style.zIndex - 1);
27251 },
27252 ondragmove: function (x, y){
27253 var left = rect.left + x;
27254 var top = rect.top + y;
27255 me.safeSetOffset({
27256 left: left,
27257 top: top
27258 });
27259 },
27260 ondragstop: function (){
27261 me.getDom('contmask').style.visibility = 'hidden';
27262 domUtils.removeClasses(me.getDom(), ['edui-state-centered']);
27263 me.dragMask.hide();
27264 }
27265 });
27266 }
27267 },
27268 reset: function (){
27269 this.getDom('content').innerHTML = this.getContentHtml();
27270 this.fireEvent('dialogafterreset');
27271 },
27272 _show: function (){
27273 if (this._hidden) {
27274 this.getDom().style.display = '';
27275
27276 //要高过编辑器的zindxe
27277 this.editor.container.style.zIndex && (this.getDom().style.zIndex = this.editor.container.style.zIndex * 1 + 10);
27278 this._hidden = false;
27279 this.fireEvent('show');
27280 baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = this.getDom().style.zIndex - 4;
27281 }
27282 },
27283 isHidden: function (){
27284 return this._hidden;
27285 },
27286 _hide: function (){
27287 if (!this._hidden) {
27288 var wrapNode = this.getDom();
27289 wrapNode.style.display = 'none';
27290 wrapNode.style.zIndex = '';
27291 wrapNode.style.width = '';
27292 wrapNode.style.height = '';
27293 this._hidden = true;
27294 this.fireEvent('hide');
27295 }
27296 },
27297 open: function (){
27298 if (this.autoReset) {
27299 //有可能还没有渲染
27300 try{
27301 this.reset();
27302 }catch(e){
27303 this.render();
27304 this.open()
27305 }
27306 }
27307 this.showAtCenter();
27308 if (this.iframeUrl) {
27309 try {
27310 this.getDom('iframe').focus();
27311 } catch(ex){}
27312 }
27313 activeDialog = this;
27314 },
27315 _onCloseButtonClick: function (evt, el){
27316 this.close(false);
27317 },
27318 close: function (ok){
27319 if (this.fireEvent('close', ok) !== false) {
27320 //还原环境
27321 if ( this.fullscreen ) {
27322
27323 document.documentElement.style.overflowX = this._originalContext.html.overflowX;
27324 document.documentElement.style.overflowY = this._originalContext.html.overflowY;
27325 document.body.style.overflowX = this._originalContext.body.overflowX;
27326 document.body.style.overflowY = this._originalContext.body.overflowY;
27327 delete this._originalContext;
27328
27329 }
27330 this._hide();
27331
27332 //销毁content
27333 var content = this.getDom('content');
27334 var iframe = this.getDom('iframe');
27335 if (content && iframe) {
27336 var doc = iframe.contentDocument || iframe.contentWindow.document;
27337 doc && (doc.body.innerHTML = '');
27338 domUtils.remove(content);
27339 }
27340 }
27341 }
27342 };
27343 utils.inherits(Dialog, UIBase);
27344})();
27345
27346
27347// ui/menubutton.js
27348///import core
27349///import uicore
27350///import ui/menu.js
27351///import ui/splitbutton.js
27352(function (){
27353 var utils = baidu.editor.utils,
27354 Menu = baidu.editor.ui.Menu,
27355 SplitButton = baidu.editor.ui.SplitButton,
27356 MenuButton = baidu.editor.ui.MenuButton = function (options){
27357 this.initOptions(options);
27358 this.initMenuButton();
27359 };
27360 MenuButton.prototype = {
27361 initMenuButton: function (){
27362 var me = this;
27363 this.uiName = "menubutton";
27364 this.popup = new Menu({
27365 items: me.items,
27366 className: me.className,
27367 editor:me.editor
27368 });
27369 this.popup.addListener('show', function (){
27370 var list = this;
27371 for (var i=0; i<list.items.length; i++) {
27372 list.items[i].removeState('checked');
27373 if (list.items[i].value == me._value) {
27374 list.items[i].addState('checked');
27375 this.value = me._value;
27376 }
27377 }
27378 });
27379 this.initSplitButton();
27380 },
27381 setValue : function(value){
27382 this._value = value;
27383 }
27384
27385 };
27386 utils.inherits(MenuButton, SplitButton);
27387})();
27388
27389// ui/multiMenu.js
27390///import core
27391///import uicore
27392 ///commands 表情
27393(function(){
27394 var utils = baidu.editor.utils,
27395 Popup = baidu.editor.ui.Popup,
27396 SplitButton = baidu.editor.ui.SplitButton,
27397 MultiMenuPop = baidu.editor.ui.MultiMenuPop = function(options){
27398 this.initOptions(options);
27399 this.initMultiMenu();
27400 };
27401
27402 MultiMenuPop.prototype = {
27403 initMultiMenu: function (){
27404 var me = this;
27405 this.popup = new Popup({
27406 content: '',
27407 editor : me.editor,
27408 iframe_rendered: false,
27409 onshow: function (){
27410 if (!this.iframe_rendered) {
27411 this.iframe_rendered = true;
27412 this.getDom('content').innerHTML = '<iframe id="'+me.id+'_iframe" src="'+ me.iframeUrl +'" frameborder="0"></iframe>';
27413 me.editor.container.style.zIndex && (this.getDom().style.zIndex = me.editor.container.style.zIndex * 1 + 1);
27414 }
27415 }
27416 // canSideUp:false,
27417 // canSideLeft:false
27418 });
27419 this.onbuttonclick = function(){
27420 this.showPopup();
27421 };
27422 this.initSplitButton();
27423 }
27424
27425 };
27426
27427 utils.inherits(MultiMenuPop, SplitButton);
27428})();
27429
27430
27431// ui/shortcutmenu.js
27432(function () {
27433 var UI = baidu.editor.ui,
27434 UIBase = UI.UIBase,
27435 uiUtils = UI.uiUtils,
27436 utils = baidu.editor.utils,
27437 domUtils = baidu.editor.dom.domUtils;
27438
27439 var allMenus = [],//存储所有快捷菜单
27440 timeID,
27441 isSubMenuShow = false;//是否有子pop显示
27442
27443 var ShortCutMenu = UI.ShortCutMenu = function (options) {
27444 this.initOptions (options);
27445 this.initShortCutMenu ();
27446 };
27447
27448 ShortCutMenu.postHide = hideAllMenu;
27449
27450 ShortCutMenu.prototype = {
27451 isHidden : true ,
27452 SPACE : 5 ,
27453 initShortCutMenu : function () {
27454 this.items = this.items || [];
27455 this.initUIBase ();
27456 this.initItems ();
27457 this.initEvent ();
27458 allMenus.push (this);
27459 } ,
27460 initEvent : function () {
27461 var me = this,
27462 doc = me.editor.document;
27463
27464 domUtils.on (doc , "mousemove" , function (e) {
27465 if (me.isHidden === false) {
27466 //有pop显示就不隐藏快捷菜单
27467 if (me.getSubMenuMark () || me.eventType == "contextmenu") return;
27468
27469
27470 var flag = true,
27471 el = me.getDom (),
27472 wt = el.offsetWidth,
27473 ht = el.offsetHeight,
27474 distanceX = wt / 2 + me.SPACE,//距离中心X标准
27475 distanceY = ht / 2,//距离中心Y标准
27476 x = Math.abs (e.screenX - me.left),//离中心距离横坐标
27477 y = Math.abs (e.screenY - me.top);//离中心距离纵坐标
27478
27479 clearTimeout (timeID);
27480 timeID = setTimeout (function () {
27481 if (y > 0 && y < distanceY) {
27482 me.setOpacity (el , "1");
27483 } else if (y > distanceY && y < distanceY + 70) {
27484 me.setOpacity (el , "0.5");
27485 flag = false;
27486 } else if (y > distanceY + 70 && y < distanceY + 140) {
27487 me.hide ();
27488 }
27489
27490 if (flag && x > 0 && x < distanceX) {
27491 me.setOpacity (el , "1")
27492 } else if (x > distanceX && x < distanceX + 70) {
27493 me.setOpacity (el , "0.5")
27494 } else if (x > distanceX + 70 && x < distanceX + 140) {
27495 me.hide ();
27496 }
27497 });
27498 }
27499 });
27500
27501 //ie\ff下 mouseout不准
27502 if (browser.chrome) {
27503 domUtils.on (doc , "mouseout" , function (e) {
27504 var relatedTgt = e.relatedTarget || e.toElement;
27505
27506 if (relatedTgt == null || relatedTgt.tagName == "HTML") {
27507 me.hide ();
27508 }
27509 });
27510 }
27511
27512 me.editor.addListener ("afterhidepop" , function () {
27513 if (!me.isHidden) {
27514 isSubMenuShow = true;
27515 }
27516 });
27517
27518 } ,
27519 initItems : function () {
27520 if (utils.isArray (this.items)) {
27521 for (var i = 0, len = this.items.length ; i < len ; i++) {
27522 var item = this.items[i].toLowerCase ();
27523
27524 if (UI[item]) {
27525 this.items[i] = new UI[item] (this.editor);
27526 this.items[i].className += " edui-shortcutsubmenu ";
27527 }
27528 }
27529 }
27530 } ,
27531 setOpacity : function (el , value) {
27532 if (browser.ie && browser.version < 9) {
27533 el.style.filter = "alpha(opacity = " + parseFloat (value) * 100 + ");"
27534 } else {
27535 el.style.opacity = value;
27536 }
27537 } ,
27538 getSubMenuMark : function () {
27539 isSubMenuShow = false;
27540 var layerEle = uiUtils.getFixedLayer ();
27541 var list = domUtils.getElementsByTagName (layerEle , "div" , function (node) {
27542 return domUtils.hasClass (node , "edui-shortcutsubmenu edui-popup")
27543 });
27544
27545 for (var i = 0, node ; node = list[i++] ;) {
27546 if (node.style.display != "none") {
27547 isSubMenuShow = true;
27548 }
27549 }
27550 return isSubMenuShow;
27551 } ,
27552 show : function (e , hasContextmenu) {
27553 var me = this,
27554 offset = {},
27555 el = this.getDom (),
27556 fixedlayer = uiUtils.getFixedLayer ();
27557
27558 function setPos (offset) {
27559 if (offset.left < 0) {
27560 offset.left = 0;
27561 }
27562 if (offset.top < 0) {
27563 offset.top = 0;
27564 }
27565 el.style.cssText = "position:absolute;left:" + offset.left + "px;top:" + offset.top + "px;";
27566 }
27567
27568 function setPosByCxtMenu (menu) {
27569 if (!menu.tagName) {
27570 menu = menu.getDom ();
27571 }
27572 offset.left = parseInt (menu.style.left);
27573 offset.top = parseInt (menu.style.top);
27574 offset.top -= el.offsetHeight + 15;
27575 setPos (offset);
27576 }
27577
27578
27579 me.eventType = e.type;
27580 el.style.cssText = "display:block;left:-9999px";
27581
27582 if (e.type == "contextmenu" && hasContextmenu) {
27583 var menu = domUtils.getElementsByTagName (fixedlayer , "div" , "edui-contextmenu")[0];
27584 if (menu) {
27585 setPosByCxtMenu (menu)
27586 } else {
27587 me.editor.addListener ("aftershowcontextmenu" , function (type , menu) {
27588 setPosByCxtMenu (menu);
27589 });
27590 }
27591 } else {
27592 offset = uiUtils.getViewportOffsetByEvent (e);
27593 offset.top -= el.offsetHeight + me.SPACE;
27594 offset.left += me.SPACE + 20;
27595 setPos (offset);
27596 me.setOpacity (el , 0.2);
27597 }
27598
27599
27600 me.isHidden = false;
27601 me.left = e.screenX + el.offsetWidth / 2 - me.SPACE;
27602 me.top = e.screenY - (el.offsetHeight / 2) - me.SPACE;
27603
27604 if (me.editor) {
27605 el.style.zIndex = me.editor.container.style.zIndex * 1 + 10;
27606 fixedlayer.style.zIndex = el.style.zIndex - 1;
27607 }
27608 } ,
27609 hide : function () {
27610 if (this.getDom ()) {
27611 this.getDom ().style.display = "none";
27612 }
27613 this.isHidden = true;
27614 } ,
27615 postRender : function () {
27616 if (utils.isArray (this.items)) {
27617 for (var i = 0, item ; item = this.items[i++] ;) {
27618 item.postRender ();
27619 }
27620 }
27621 } ,
27622 getHtmlTpl : function () {
27623 var buff;
27624 if (utils.isArray (this.items)) {
27625 buff = [];
27626 for (var i = 0 ; i < this.items.length ; i++) {
27627 buff[i] = this.items[i].renderHtml ();
27628 }
27629 buff = buff.join ("");
27630 } else {
27631 buff = this.items;
27632 }
27633
27634 return '<div id="##" class="%% edui-toolbar" data-src="shortcutmenu" onmousedown="return false;" onselectstart="return false;" >' +
27635 buff +
27636 '</div>';
27637 }
27638 };
27639
27640 utils.inherits (ShortCutMenu , UIBase);
27641
27642 function hideAllMenu (e) {
27643 var tgt = e.target || e.srcElement,
27644 cur = domUtils.findParent (tgt , function (node) {
27645 return domUtils.hasClass (node , "edui-shortcutmenu") || domUtils.hasClass (node , "edui-popup");
27646 } , true);
27647
27648 if (!cur) {
27649 for (var i = 0, menu ; menu = allMenus[i++] ;) {
27650 menu.hide ()
27651 }
27652 }
27653 }
27654
27655 domUtils.on (document , 'mousedown' , function (e) {
27656 hideAllMenu (e);
27657 });
27658
27659 domUtils.on (window , 'scroll' , function (e) {
27660 hideAllMenu (e);
27661 });
27662
27663}) ();
27664
27665
27666// ui/breakline.js
27667(function (){
27668 var utils = baidu.editor.utils,
27669 UIBase = baidu.editor.ui.UIBase,
27670 Breakline = baidu.editor.ui.Breakline = function (options){
27671 this.initOptions(options);
27672 this.initSeparator();
27673 };
27674 Breakline.prototype = {
27675 uiName: 'Breakline',
27676 initSeparator: function (){
27677 this.initUIBase();
27678 },
27679 getHtmlTpl: function (){
27680 return '<br/>';
27681 }
27682 };
27683 utils.inherits(Breakline, UIBase);
27684
27685})();
27686
27687
27688// ui/message.js
27689///import core
27690///import uicore
27691(function () {
27692 var utils = baidu.editor.utils,
27693 domUtils = baidu.editor.dom.domUtils,
27694 UIBase = baidu.editor.ui.UIBase,
27695 Message = baidu.editor.ui.Message = function (options){
27696 this.initOptions(options);
27697 this.initMessage();
27698 };
27699
27700 Message.prototype = {
27701 initMessage: function (){
27702 this.initUIBase();
27703 },
27704 getHtmlTpl: function (){
27705 return '<div id="##" class="edui-message %%">' +
27706 ' <div id="##_closer" class="edui-message-closer">×</div>' +
27707 ' <div id="##_body" class="edui-message-body edui-message-type-info">' +
27708 ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
27709 ' <div class="edui-shadow"></div>' +
27710 ' <div id="##_content" class="edui-message-content">' +
27711 ' </div>' +
27712 ' </div>' +
27713 '</div>';
27714 },
27715 reset: function(opt){
27716 var me = this;
27717 if (!opt.keepshow) {
27718 clearTimeout(this.timer);
27719 me.timer = setTimeout(function(){
27720 me.hide();
27721 }, opt.timeout || 4000);
27722 }
27723
27724 opt.content !== undefined && me.setContent(opt.content);
27725 opt.type !== undefined && me.setType(opt.type);
27726
27727 me.show();
27728 },
27729 postRender: function(){
27730 var me = this,
27731 closer = this.getDom('closer');
27732 closer && domUtils.on(closer, 'click', function(){
27733 me.hide();
27734 });
27735 },
27736 setContent: function(content){
27737 this.getDom('content').innerHTML = content;
27738 },
27739 setType: function(type){
27740 type = type || 'info';
27741 var body = this.getDom('body');
27742 body.className = body.className.replace(/edui-message-type-[\w-]+/, 'edui-message-type-' + type);
27743 },
27744 getContent: function(){
27745 return this.getDom('content').innerHTML;
27746 },
27747 getType: function(){
27748 var arr = this.getDom('body').match(/edui-message-type-([\w-]+)/);
27749 return arr ? arr[1]:'';
27750 },
27751 show: function (){
27752 this.getDom().style.display = 'block';
27753 },
27754 hide: function (){
27755 var dom = this.getDom();
27756 if (dom) {
27757 dom.style.display = 'none';
27758 dom.parentNode && dom.parentNode.removeChild(dom);
27759 }
27760 }
27761 };
27762
27763 utils.inherits(Message, UIBase);
27764
27765})();
27766
27767
27768// adapter/editorui.js
27769//ui跟编辑器的适配層
27770//那个按钮弹出是dialog,是下拉筐等都是在这个js中配置
27771//自己写的ui也要在这里配置,放到baidu.editor.ui下边,当编辑器实例化的时候会根据ueditor.config中的toolbars找到相应的进行实例化
27772(function () {
27773 var utils = baidu.editor.utils;
27774 var editorui = baidu.editor.ui;
27775 var _Dialog = editorui.Dialog;
27776 editorui.buttons = {};
27777
27778 editorui.Dialog = function (options) {
27779 var dialog = new _Dialog(options);
27780 dialog.addListener('hide', function () {
27781
27782 if (dialog.editor) {
27783 var editor = dialog.editor;
27784 try {
27785 if (browser.gecko) {
27786 var y = editor.window.scrollY,
27787 x = editor.window.scrollX;
27788 editor.body.focus();
27789 editor.window.scrollTo(x, y);
27790 } else {
27791 editor.focus();
27792 }
27793
27794
27795 } catch (ex) {
27796 }
27797 }
27798 });
27799 return dialog;
27800 };
27801
27802 var iframeUrlMap = {
27803 'anchor':'~/dialogs/anchor/anchor.html',
27804 'insertimage':'~/dialogs/image/image.html',
27805 'link':'~/dialogs/link/link.html',
27806 'spechars':'~/dialogs/spechars/spechars.html',
27807 'searchreplace':'~/dialogs/searchreplace/searchreplace.html',
27808 'map':'~/dialogs/map/map.html',
27809 'gmap':'~/dialogs/gmap/gmap.html',
27810 'insertvideo':'~/dialogs/video/video.html',
27811 'help':'~/dialogs/help/help.html',
27812 'preview':'~/dialogs/preview/preview.html',
27813 'emotion':'~/dialogs/emotion/emotion.html',
27814 'wordimage':'~/dialogs/wordimage/wordimage.html',
27815 'attachment':'~/dialogs/attachment/attachment.html',
27816 'insertframe':'~/dialogs/insertframe/insertframe.html',
27817 'edittip':'~/dialogs/table/edittip.html',
27818 'edittable':'~/dialogs/table/edittable.html',
27819 'edittd':'~/dialogs/table/edittd.html',
27820 'webapp':'~/dialogs/webapp/webapp.html',
27821 'snapscreen':'~/dialogs/snapscreen/snapscreen.html',
27822 'scrawl':'~/dialogs/scrawl/scrawl.html',
27823 'music':'~/dialogs/music/music.html',
27824 'template':'~/dialogs/template/template.html',
27825 'background':'~/dialogs/background/background.html',
27826 'charts': '~/dialogs/charts/charts.html'
27827 };
27828 //为工具栏添加按钮,以下都是统一的按钮触发命令,所以写在一起
27829 var btnCmds = ['undo', 'redo', 'formatmatch',
27830 'bold', 'italic', 'underline', 'fontborder', 'touppercase', 'tolowercase',
27831 'strikethrough', 'subscript', 'superscript', 'source', 'indent', 'outdent',
27832 'blockquote', 'pasteplain', 'pagebreak',
27833 'selectall', 'print','horizontal', 'removeformat', 'time', 'date', 'unlink',
27834 'insertparagraphbeforetable', 'insertrow', 'insertcol', 'mergeright', 'mergedown', 'deleterow',
27835 'deletecol', 'splittorows', 'splittocols', 'splittocells', 'mergecells', 'deletetable', 'drafts'];
27836
27837 for (var i = 0, ci; ci = btnCmds[i++];) {
27838 ci = ci.toLowerCase();
27839 editorui[ci] = function (cmd) {
27840 return function (editor) {
27841 var ui = new editorui.Button({
27842 className:'edui-for-' + cmd,
27843 title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '',
27844 onclick:function () {
27845 editor.execCommand(cmd);
27846 },
27847 theme:editor.options.theme,
27848 showText:false
27849 });
27850 editorui.buttons[cmd] = ui;
27851 editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
27852 var state = editor.queryCommandState(cmd);
27853 if (state == -1) {
27854 ui.setDisabled(true);
27855 ui.setChecked(false);
27856 } else {
27857 if (!uiReady) {
27858 ui.setDisabled(false);
27859 ui.setChecked(state);
27860 }
27861 }
27862 });
27863 return ui;
27864 };
27865 }(ci);
27866 }
27867
27868 //清除文档
27869 editorui.cleardoc = function (editor) {
27870 var ui = new editorui.Button({
27871 className:'edui-for-cleardoc',
27872 title:editor.options.labelMap.cleardoc || editor.getLang("labelMap.cleardoc") || '',
27873 theme:editor.options.theme,
27874 onclick:function () {
27875 if (confirm(editor.getLang("confirmClear"))) {
27876 editor.execCommand('cleardoc');
27877 }
27878 }
27879 });
27880 editorui.buttons["cleardoc"] = ui;
27881 editor.addListener('selectionchange', function () {
27882 ui.setDisabled(editor.queryCommandState('cleardoc') == -1);
27883 });
27884 return ui;
27885 };
27886
27887 //排版,图片排版,文字方向
27888 var typeset = {
27889 'justify':['left', 'right', 'center', 'justify'],
27890 'imagefloat':['none', 'left', 'center', 'right'],
27891 'directionality':['ltr', 'rtl']
27892 };
27893
27894 for (var p in typeset) {
27895
27896 (function (cmd, val) {
27897 for (var i = 0, ci; ci = val[i++];) {
27898 (function (cmd2) {
27899 editorui[cmd.replace('float', '') + cmd2] = function (editor) {
27900 var ui = new editorui.Button({
27901 className:'edui-for-' + cmd.replace('float', '') + cmd2,
27902 title:editor.options.labelMap[cmd.replace('float', '') + cmd2] || editor.getLang("labelMap." + cmd.replace('float', '') + cmd2) || '',
27903 theme:editor.options.theme,
27904 onclick:function () {
27905 editor.execCommand(cmd, cmd2);
27906 }
27907 });
27908 editorui.buttons[cmd] = ui;
27909 editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
27910 ui.setDisabled(editor.queryCommandState(cmd) == -1);
27911 ui.setChecked(editor.queryCommandValue(cmd) == cmd2 && !uiReady);
27912 });
27913 return ui;
27914 };
27915 })(ci)
27916 }
27917 })(p, typeset[p])
27918 }
27919
27920 //字体颜色和背景颜色
27921 for (var i = 0, ci; ci = ['backcolor', 'forecolor'][i++];) {
27922 editorui[ci] = function (cmd) {
27923 return function (editor) {
27924 var ui = new editorui.ColorButton({
27925 className:'edui-for-' + cmd,
27926 color:'default',
27927 title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '',
27928 editor:editor,
27929 onpickcolor:function (t, color) {
27930 editor.execCommand(cmd, color);
27931 },
27932 onpicknocolor:function () {
27933 editor.execCommand(cmd, 'default');
27934 this.setColor('transparent');
27935 this.color = 'default';
27936 },
27937 onbuttonclick:function () {
27938 editor.execCommand(cmd, this.color);
27939 }
27940 });
27941 editorui.buttons[cmd] = ui;
27942 editor.addListener('selectionchange', function () {
27943 ui.setDisabled(editor.queryCommandState(cmd) == -1);
27944 });
27945 return ui;
27946 };
27947 }(ci);
27948 }
27949
27950
27951 var dialogBtns = {
27952 noOk:['searchreplace', 'help', 'spechars', 'webapp','preview'],
27953 ok:['attachment', 'anchor', 'link', 'insertimage', 'map', 'gmap', 'insertframe', 'wordimage',
27954 'insertvideo', 'insertframe', 'edittip', 'edittable', 'edittd', 'scrawl', 'template', 'music', 'background', 'charts']
27955 };
27956
27957 for (var p in dialogBtns) {
27958 (function (type, vals) {
27959 for (var i = 0, ci; ci = vals[i++];) {
27960 //todo opera下存在问题
27961 if (browser.opera && ci === "searchreplace") {
27962 continue;
27963 }
27964 (function (cmd) {
27965 editorui[cmd] = function (editor, iframeUrl, title) {
27966 iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd];
27967 title = editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '';
27968
27969 var dialog;
27970 //没有iframeUrl不创建dialog
27971 if (iframeUrl) {
27972 dialog = new editorui.Dialog(utils.extend({
27973 iframeUrl:editor.ui.mapUrl(iframeUrl),
27974 editor:editor,
27975 className:'edui-for-' + cmd,
27976 title:title,
27977 holdScroll: cmd === 'insertimage',
27978 fullscreen: /charts|preview/.test(cmd),
27979 closeDialog:editor.getLang("closeDialog")
27980 }, type == 'ok' ? {
27981 buttons:[
27982 {
27983 className:'edui-okbutton',
27984 label:editor.getLang("ok"),
27985 editor:editor,
27986 onclick:function () {
27987 dialog.close(true);
27988 }
27989 },
27990 {
27991 className:'edui-cancelbutton',
27992 label:editor.getLang("cancel"),
27993 editor:editor,
27994 onclick:function () {
27995 dialog.close(false);
27996 }
27997 }
27998 ]
27999 } : {}));
28000
28001 editor.ui._dialogs[cmd + "Dialog"] = dialog;
28002 }
28003
28004 var ui = new editorui.Button({
28005 className:'edui-for-' + cmd,
28006 title:title,
28007 onclick:function () {
28008 if (dialog) {
28009 switch (cmd) {
28010 case "wordimage":
28011 var images = editor.execCommand("wordimage");
28012 if (images && images.length) {
28013 dialog.render();
28014 dialog.open();
28015 }
28016 break;
28017 case "scrawl":
28018 if (editor.queryCommandState("scrawl") != -1) {
28019 dialog.render();
28020 dialog.open();
28021 }
28022
28023 break;
28024 default:
28025 dialog.render();
28026 dialog.open();
28027 }
28028 }
28029 },
28030 theme:editor.options.theme,
28031 disabled:(cmd == 'scrawl' && editor.queryCommandState("scrawl") == -1) || ( cmd == 'charts' )
28032 });
28033 editorui.buttons[cmd] = ui;
28034 editor.addListener('selectionchange', function () {
28035 //只存在于右键菜单而无工具栏按钮的ui不需要检测状态
28036 var unNeedCheckState = {'edittable':1};
28037 if (cmd in unNeedCheckState)return;
28038
28039 var state = editor.queryCommandState(cmd);
28040 if (ui.getDom()) {
28041 ui.setDisabled(state == -1);
28042 ui.setChecked(state);
28043 }
28044
28045 });
28046
28047 return ui;
28048 };
28049 })(ci.toLowerCase())
28050 }
28051 })(p, dialogBtns[p]);
28052 }
28053
28054 editorui.snapscreen = function (editor, iframeUrl, title) {
28055 title = editor.options.labelMap['snapscreen'] || editor.getLang("labelMap.snapscreen") || '';
28056 var ui = new editorui.Button({
28057 className:'edui-for-snapscreen',
28058 title:title,
28059 onclick:function () {
28060 editor.execCommand("snapscreen");
28061 },
28062 theme:editor.options.theme
28063
28064 });
28065 editorui.buttons['snapscreen'] = ui;
28066 iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})["snapscreen"] || iframeUrlMap["snapscreen"];
28067 if (iframeUrl) {
28068 var dialog = new editorui.Dialog({
28069 iframeUrl:editor.ui.mapUrl(iframeUrl),
28070 editor:editor,
28071 className:'edui-for-snapscreen',
28072 title:title,
28073 buttons:[
28074 {
28075 className:'edui-okbutton',
28076 label:editor.getLang("ok"),
28077 editor:editor,
28078 onclick:function () {
28079 dialog.close(true);
28080 }
28081 },
28082 {
28083 className:'edui-cancelbutton',
28084 label:editor.getLang("cancel"),
28085 editor:editor,
28086 onclick:function () {
28087 dialog.close(false);
28088 }
28089 }
28090 ]
28091
28092 });
28093 dialog.render();
28094 editor.ui._dialogs["snapscreenDialog"] = dialog;
28095 }
28096 editor.addListener('selectionchange', function () {
28097 ui.setDisabled(editor.queryCommandState('snapscreen') == -1);
28098 });
28099 return ui;
28100 };
28101
28102 editorui.insertcode = function (editor, list, title) {
28103 list = editor.options['insertcode'] || [];
28104 title = editor.options.labelMap['insertcode'] || editor.getLang("labelMap.insertcode") || '';
28105 // if (!list.length) return;
28106 var items = [];
28107 utils.each(list,function(key,val){
28108 items.push({
28109 label:key,
28110 value:val,
28111 theme:editor.options.theme,
28112 renderLabelHtml:function () {
28113 return '<div class="edui-label %%-label" >' + (this.label || '') + '</div>';
28114 }
28115 });
28116 });
28117
28118 var ui = new editorui.Combox({
28119 editor:editor,
28120 items:items,
28121 onselect:function (t, index) {
28122 editor.execCommand('insertcode', this.items[index].value);
28123 },
28124 onbuttonclick:function () {
28125 this.showPopup();
28126 },
28127 title:title,
28128 initValue:title,
28129 className:'edui-for-insertcode',
28130 indexByValue:function (value) {
28131 if (value) {
28132 for (var i = 0, ci; ci = this.items[i]; i++) {
28133 if (ci.value.indexOf(value) != -1)
28134 return i;
28135 }
28136 }
28137
28138 return -1;
28139 }
28140 });
28141 editorui.buttons['insertcode'] = ui;
28142 editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
28143 if (!uiReady) {
28144 var state = editor.queryCommandState('insertcode');
28145 if (state == -1) {
28146 ui.setDisabled(true);
28147 } else {
28148 ui.setDisabled(false);
28149 var value = editor.queryCommandValue('insertcode');
28150 if(!value){
28151 ui.setValue(title);
28152 return;
28153 }
28154 //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
28155 value && (value = value.replace(/['"]/g, '').split(',')[0]);
28156 ui.setValue(value);
28157
28158 }
28159 }
28160
28161 });
28162 return ui;
28163 };
28164 editorui.fontfamily = function (editor, list, title) {
28165
28166 list = editor.options['fontfamily'] || [];
28167 title = editor.options.labelMap['fontfamily'] || editor.getLang("labelMap.fontfamily") || '';
28168 if (!list.length) return;
28169 for (var i = 0, ci, items = []; ci = list[i]; i++) {
28170 var langLabel = editor.getLang('fontfamily')[ci.name] || "";
28171 (function (key, val) {
28172 items.push({
28173 label:key,
28174 value:val,
28175 theme:editor.options.theme,
28176 renderLabelHtml:function () {
28177 return '<div class="edui-label %%-label" style="font-family:' +
28178 utils.unhtml(this.value) + '">' + (this.label || '') + '</div>';
28179 }
28180 });
28181 })(ci.label || langLabel, ci.val)
28182 }
28183 var ui = new editorui.Combox({
28184 editor:editor,
28185 items:items,
28186 onselect:function (t, index) {
28187 editor.execCommand('FontFamily', this.items[index].value);
28188 },
28189 onbuttonclick:function () {
28190 this.showPopup();
28191 },
28192 title:title,
28193 initValue:title,
28194 className:'edui-for-fontfamily',
28195 indexByValue:function (value) {
28196 if (value) {
28197 for (var i = 0, ci; ci = this.items[i]; i++) {
28198 if (ci.value.indexOf(value) != -1)
28199 return i;
28200 }
28201 }
28202
28203 return -1;
28204 }
28205 });
28206 editorui.buttons['fontfamily'] = ui;
28207 editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
28208 if (!uiReady) {
28209 var state = editor.queryCommandState('FontFamily');
28210 if (state == -1) {
28211 ui.setDisabled(true);
28212 } else {
28213 ui.setDisabled(false);
28214 var value = editor.queryCommandValue('FontFamily');
28215 //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
28216 value && (value = value.replace(/['"]/g, '').split(',')[0]);
28217 ui.setValue(value);
28218
28219 }
28220 }
28221
28222 });
28223 return ui;
28224 };
28225
28226 editorui.fontsize = function (editor, list, title) {
28227 title = editor.options.labelMap['fontsize'] || editor.getLang("labelMap.fontsize") || '';
28228 list = list || editor.options['fontsize'] || [];
28229 if (!list.length) return;
28230 var items = [];
28231 for (var i = 0; i < list.length; i++) {
28232 var size = list[i] + 'px';
28233 items.push({
28234 label:size,
28235 value:size,
28236 theme:editor.options.theme,
28237 renderLabelHtml:function () {
28238 return '<div class="edui-label %%-label" style="line-height:1;font-size:' +
28239 this.value + '">' + (this.label || '') + '</div>';
28240 }
28241 });
28242 }
28243 var ui = new editorui.Combox({
28244 editor:editor,
28245 items:items,
28246 title:title,
28247 initValue:title,
28248 onselect:function (t, index) {
28249 editor.execCommand('FontSize', this.items[index].value);
28250 },
28251 onbuttonclick:function () {
28252 this.showPopup();
28253 },
28254 className:'edui-for-fontsize'
28255 });
28256 editorui.buttons['fontsize'] = ui;
28257 editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
28258 if (!uiReady) {
28259 var state = editor.queryCommandState('FontSize');
28260 if (state == -1) {
28261 ui.setDisabled(true);
28262 } else {
28263 ui.setDisabled(false);
28264 ui.setValue(editor.queryCommandValue('FontSize'));
28265 }
28266 }
28267
28268 });
28269 return ui;
28270 };
28271
28272 editorui.paragraph = function (editor, list, title) {
28273 title = editor.options.labelMap['paragraph'] || editor.getLang("labelMap.paragraph") || '';
28274 list = editor.options['paragraph'] || [];
28275 if (utils.isEmptyObject(list)) return;
28276 var items = [];
28277 for (var i in list) {
28278 items.push({
28279 value:i,
28280 label:list[i] || editor.getLang("paragraph")[i],
28281 theme:editor.options.theme,
28282 renderLabelHtml:function () {
28283 return '<div class="edui-label %%-label"><span class="edui-for-' + this.value + '">' + (this.label || '') + '</span></div>';
28284 }
28285 })
28286 }
28287 var ui = new editorui.Combox({
28288 editor:editor,
28289 items:items,
28290 title:title,
28291 initValue:title,
28292 className:'edui-for-paragraph',
28293 onselect:function (t, index) {
28294 editor.execCommand('Paragraph', this.items[index].value);
28295 },
28296 onbuttonclick:function () {
28297 this.showPopup();
28298 }
28299 });
28300 editorui.buttons['paragraph'] = ui;
28301 editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
28302 if (!uiReady) {
28303 var state = editor.queryCommandState('Paragraph');
28304 if (state == -1) {
28305 ui.setDisabled(true);
28306 } else {
28307 ui.setDisabled(false);
28308 var value = editor.queryCommandValue('Paragraph');
28309 var index = ui.indexByValue(value);
28310 if (index != -1) {
28311 ui.setValue(value);
28312 } else {
28313 ui.setValue(ui.initValue);
28314 }
28315 }
28316 }
28317
28318 });
28319 return ui;
28320 };
28321
28322
28323 //自定义标题
28324 editorui.customstyle = function (editor) {
28325 var list = editor.options['customstyle'] || [],
28326 title = editor.options.labelMap['customstyle'] || editor.getLang("labelMap.customstyle") || '';
28327 if (!list.length)return;
28328 var langCs = editor.getLang('customstyle');
28329 for (var i = 0, items = [], t; t = list[i++];) {
28330 (function (t) {
28331 var ck = {};
28332 ck.label = t.label ? t.label : langCs[t.name];
28333 ck.style = t.style;
28334 ck.className = t.className;
28335 ck.tag = t.tag;
28336 items.push({
28337 label:ck.label,
28338 value:ck,
28339 theme:editor.options.theme,
28340 renderLabelHtml:function () {
28341 return '<div class="edui-label %%-label">' + '<' + ck.tag + ' ' + (ck.className ? ' class="' + ck.className + '"' : "")
28342 + (ck.style ? ' style="' + ck.style + '"' : "") + '>' + ck.label + "<\/" + ck.tag + ">"
28343 + '</div>';
28344 }
28345 });
28346 })(t);
28347 }
28348
28349 var ui = new editorui.Combox({
28350 editor:editor,
28351 items:items,
28352 title:title,
28353 initValue:title,
28354 className:'edui-for-customstyle',
28355 onselect:function (t, index) {
28356 editor.execCommand('customstyle', this.items[index].value);
28357 },
28358 onbuttonclick:function () {
28359 this.showPopup();
28360 },
28361 indexByValue:function (value) {
28362 for (var i = 0, ti; ti = this.items[i++];) {
28363 if (ti.label == value) {
28364 return i - 1
28365 }
28366 }
28367 return -1;
28368 }
28369 });
28370 editorui.buttons['customstyle'] = ui;
28371 editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
28372 if (!uiReady) {
28373 var state = editor.queryCommandState('customstyle');
28374 if (state == -1) {
28375 ui.setDisabled(true);
28376 } else {
28377 ui.setDisabled(false);
28378 var value = editor.queryCommandValue('customstyle');
28379 var index = ui.indexByValue(value);
28380 if (index != -1) {
28381 ui.setValue(value);
28382 } else {
28383 ui.setValue(ui.initValue);
28384 }
28385 }
28386 }
28387
28388 });
28389 return ui;
28390 };
28391 editorui.inserttable = function (editor, iframeUrl, title) {
28392 title = editor.options.labelMap['inserttable'] || editor.getLang("labelMap.inserttable") || '';
28393 var ui = new editorui.TableButton({
28394 editor:editor,
28395 title:title,
28396 className:'edui-for-inserttable',
28397 onpicktable:function (t, numCols, numRows) {
28398 editor.execCommand('InsertTable', {numRows:numRows, numCols:numCols, border:1});
28399 },
28400 onbuttonclick:function () {
28401 this.showPopup();
28402 }
28403 });
28404 editorui.buttons['inserttable'] = ui;
28405 editor.addListener('selectionchange', function () {
28406 ui.setDisabled(editor.queryCommandState('inserttable') == -1);
28407 });
28408 return ui;
28409 };
28410
28411 editorui.lineheight = function (editor) {
28412 var val = editor.options.lineheight || [];
28413 if (!val.length)return;
28414 for (var i = 0, ci, items = []; ci = val[i++];) {
28415 items.push({
28416 //todo:写死了
28417 label:ci,
28418 value:ci,
28419 theme:editor.options.theme,
28420 onclick:function () {
28421 editor.execCommand("lineheight", this.value);
28422 }
28423 })
28424 }
28425 var ui = new editorui.MenuButton({
28426 editor:editor,
28427 className:'edui-for-lineheight',
28428 title:editor.options.labelMap['lineheight'] || editor.getLang("labelMap.lineheight") || '',
28429 items:items,
28430 onbuttonclick:function () {
28431 var value = editor.queryCommandValue('LineHeight') || this.value;
28432 editor.execCommand("LineHeight", value);
28433 }
28434 });
28435 editorui.buttons['lineheight'] = ui;
28436 editor.addListener('selectionchange', function () {
28437 var state = editor.queryCommandState('LineHeight');
28438 if (state == -1) {
28439 ui.setDisabled(true);
28440 } else {
28441 ui.setDisabled(false);
28442 var value = editor.queryCommandValue('LineHeight');
28443 value && ui.setValue((value + '').replace(/cm/, ''));
28444 ui.setChecked(state)
28445 }
28446 });
28447 return ui;
28448 };
28449
28450 var rowspacings = ['top', 'bottom'];
28451 for (var r = 0, ri; ri = rowspacings[r++];) {
28452 (function (cmd) {
28453 editorui['rowspacing' + cmd] = function (editor) {
28454 var val = editor.options['rowspacing' + cmd] || [];
28455 if (!val.length) return null;
28456 for (var i = 0, ci, items = []; ci = val[i++];) {
28457 items.push({
28458 label:ci,
28459 value:ci,
28460 theme:editor.options.theme,
28461 onclick:function () {
28462 editor.execCommand("rowspacing", this.value, cmd);
28463 }
28464 })
28465 }
28466 var ui = new editorui.MenuButton({
28467 editor:editor,
28468 className:'edui-for-rowspacing' + cmd,
28469 title:editor.options.labelMap['rowspacing' + cmd] || editor.getLang("labelMap.rowspacing" + cmd) || '',
28470 items:items,
28471 onbuttonclick:function () {
28472 var value = editor.queryCommandValue('rowspacing', cmd) || this.value;
28473 editor.execCommand("rowspacing", value, cmd);
28474 }
28475 });
28476 editorui.buttons[cmd] = ui;
28477 editor.addListener('selectionchange', function () {
28478 var state = editor.queryCommandState('rowspacing', cmd);
28479 if (state == -1) {
28480 ui.setDisabled(true);
28481 } else {
28482 ui.setDisabled(false);
28483 var value = editor.queryCommandValue('rowspacing', cmd);
28484 value && ui.setValue((value + '').replace(/%/, ''));
28485 ui.setChecked(state)
28486 }
28487 });
28488 return ui;
28489 }
28490 })(ri)
28491 }
28492 //有序,无序列表
28493 var lists = ['insertorderedlist', 'insertunorderedlist'];
28494 for (var l = 0, cl; cl = lists[l++];) {
28495 (function (cmd) {
28496 editorui[cmd] = function (editor) {
28497 var vals = editor.options[cmd],
28498 _onMenuClick = function () {
28499 editor.execCommand(cmd, this.value);
28500 }, items = [];
28501 for (var i in vals) {
28502 items.push({
28503 label:vals[i] || editor.getLang()[cmd][i] || "",
28504 value:i,
28505 theme:editor.options.theme,
28506 onclick:_onMenuClick
28507 })
28508 }
28509 var ui = new editorui.MenuButton({
28510 editor:editor,
28511 className:'edui-for-' + cmd,
28512 title:editor.getLang("labelMap." + cmd) || '',
28513 'items':items,
28514 onbuttonclick:function () {
28515 var value = editor.queryCommandValue(cmd) || this.value;
28516 editor.execCommand(cmd, value);
28517 }
28518 });
28519 editorui.buttons[cmd] = ui;
28520 editor.addListener('selectionchange', function () {
28521 var state = editor.queryCommandState(cmd);
28522 if (state == -1) {
28523 ui.setDisabled(true);
28524 } else {
28525 ui.setDisabled(false);
28526 var value = editor.queryCommandValue(cmd);
28527 ui.setValue(value);
28528 ui.setChecked(state)
28529 }
28530 });
28531 return ui;
28532 };
28533 })(cl)
28534 }
28535
28536 editorui.fullscreen = function (editor, title) {
28537 title = editor.options.labelMap['fullscreen'] || editor.getLang("labelMap.fullscreen") || '';
28538 var ui = new editorui.Button({
28539 className:'edui-for-fullscreen',
28540 title:title,
28541 theme:editor.options.theme,
28542 onclick:function () {
28543 if (editor.ui) {
28544 editor.ui.setFullScreen(!editor.ui.isFullScreen());
28545 }
28546 this.setChecked(editor.ui.isFullScreen());
28547 }
28548 });
28549 editorui.buttons['fullscreen'] = ui;
28550 editor.addListener('selectionchange', function () {
28551 var state = editor.queryCommandState('fullscreen');
28552 ui.setDisabled(state == -1);
28553 ui.setChecked(editor.ui.isFullScreen());
28554 });
28555 return ui;
28556 };
28557
28558 // 表情
28559 editorui["emotion"] = function (editor, iframeUrl) {
28560 var cmd = "emotion";
28561 var ui = new editorui.MultiMenuPop({
28562 title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd + "") || '',
28563 editor:editor,
28564 className:'edui-for-' + cmd,
28565 iframeUrl:editor.ui.mapUrl(iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd])
28566 });
28567 editorui.buttons[cmd] = ui;
28568
28569 editor.addListener('selectionchange', function () {
28570 ui.setDisabled(editor.queryCommandState(cmd) == -1)
28571 });
28572 return ui;
28573 };
28574
28575 editorui.autotypeset = function (editor) {
28576 var ui = new editorui.AutoTypeSetButton({
28577 editor:editor,
28578 title:editor.options.labelMap['autotypeset'] || editor.getLang("labelMap.autotypeset") || '',
28579 className:'edui-for-autotypeset',
28580 onbuttonclick:function () {
28581 editor.execCommand('autotypeset')
28582 }
28583 });
28584 editorui.buttons['autotypeset'] = ui;
28585 editor.addListener('selectionchange', function () {
28586 ui.setDisabled(editor.queryCommandState('autotypeset') == -1);
28587 });
28588 return ui;
28589 };
28590
28591 /* 简单上传插件 */
28592 editorui["simpleupload"] = function (editor) {
28593 var name = 'simpleupload',
28594 ui = new editorui.Button({
28595 className:'edui-for-' + name,
28596 title:editor.options.labelMap[name] || editor.getLang("labelMap." + name) || '',
28597 onclick:function () {},
28598 theme:editor.options.theme,
28599 showText:false
28600 });
28601 editorui.buttons[name] = ui;
28602 editor.addListener('ready', function() {
28603 var b = ui.getDom('body'),
28604 iconSpan = b.children[0];
28605 editor.fireEvent('simpleuploadbtnready', iconSpan);
28606 });
28607 editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
28608 var state = editor.queryCommandState(name);
28609 if (state == -1) {
28610 ui.setDisabled(true);
28611 ui.setChecked(false);
28612 } else {
28613 if (!uiReady) {
28614 ui.setDisabled(false);
28615 ui.setChecked(state);
28616 }
28617 }
28618 });
28619 return ui;
28620 };
28621
28622})();
28623
28624
28625// adapter/editor.js
28626///import core
28627///commands 全屏
28628///commandsName FullScreen
28629///commandsTitle 全屏
28630(function () {
28631 var utils = baidu.editor.utils,
28632 uiUtils = baidu.editor.ui.uiUtils,
28633 UIBase = baidu.editor.ui.UIBase,
28634 domUtils = baidu.editor.dom.domUtils;
28635 var nodeStack = [];
28636
28637 function EditorUI(options) {
28638 this.initOptions(options);
28639 this.initEditorUI();
28640 }
28641
28642 EditorUI.prototype = {
28643 uiName:'editor',
28644 initEditorUI:function () {
28645 this.editor.ui = this;
28646 this._dialogs = {};
28647 this.initUIBase();
28648 this._initToolbars();
28649 var editor = this.editor,
28650 me = this;
28651
28652 editor.addListener('ready', function () {
28653 //提供getDialog方法
28654 editor.getDialog = function (name) {
28655 return editor.ui._dialogs[name + "Dialog"];
28656 };
28657 domUtils.on(editor.window, 'scroll', function (evt) {
28658 baidu.editor.ui.Popup.postHide(evt);
28659 });
28660 //提供编辑器实时宽高(全屏时宽高不变化)
28661 editor.ui._actualFrameWidth = editor.options.initialFrameWidth;
28662
28663 UE.browser.ie && UE.browser.version === 6 && editor.container.ownerDocument.execCommand("BackgroundImageCache", false, true);
28664
28665 //display bottom-bar label based on config
28666 if (editor.options.elementPathEnabled) {
28667 editor.ui.getDom('elementpath').innerHTML = '<div class="edui-editor-breadcrumb">' + editor.getLang("elementPathTip") + ':</div>';
28668 }
28669 if (editor.options.wordCount) {
28670 function countFn() {
28671 setCount(editor,me);
28672 domUtils.un(editor.document, "click", arguments.callee);
28673 }
28674 domUtils.on(editor.document, "click", countFn);
28675 editor.ui.getDom('wordcount').innerHTML = editor.getLang("wordCountTip");
28676 }
28677 editor.ui._scale();
28678 if (editor.options.scaleEnabled) {
28679 if (editor.autoHeightEnabled) {
28680 editor.disableAutoHeight();
28681 }
28682 me.enableScale();
28683 } else {
28684 me.disableScale();
28685 }
28686 if (!editor.options.elementPathEnabled && !editor.options.wordCount && !editor.options.scaleEnabled) {
28687 editor.ui.getDom('elementpath').style.display = "none";
28688 editor.ui.getDom('wordcount').style.display = "none";
28689 editor.ui.getDom('scale').style.display = "none";
28690 }
28691
28692 if (!editor.selection.isFocus())return;
28693 editor.fireEvent('selectionchange', false, true);
28694
28695
28696 });
28697
28698 editor.addListener('mousedown', function (t, evt) {
28699 var el = evt.target || evt.srcElement;
28700 baidu.editor.ui.Popup.postHide(evt, el);
28701 baidu.editor.ui.ShortCutMenu.postHide(evt);
28702
28703 });
28704 editor.addListener("delcells", function () {
28705 if (UE.ui['edittip']) {
28706 new UE.ui['edittip'](editor);
28707 }
28708 editor.getDialog('edittip').open();
28709 });
28710
28711 var pastePop, isPaste = false, timer;
28712 editor.addListener("afterpaste", function () {
28713 if(editor.queryCommandState('pasteplain'))
28714 return;
28715 if(baidu.editor.ui.PastePicker){
28716 pastePop = new baidu.editor.ui.Popup({
28717 content:new baidu.editor.ui.PastePicker({editor:editor}),
28718 editor:editor,
28719 className:'edui-wordpastepop'
28720 });
28721 pastePop.render();
28722 }
28723 isPaste = true;
28724 });
28725
28726 editor.addListener("afterinserthtml", function () {
28727 clearTimeout(timer);
28728 timer = setTimeout(function () {
28729 if (pastePop && (isPaste || editor.ui._isTransfer)) {
28730 if(pastePop.isHidden()){
28731 var span = domUtils.createElement(editor.document, 'span', {
28732 'style':"line-height:0px;",
28733 'innerHTML':'\ufeff'
28734 }),
28735 range = editor.selection.getRange();
28736 range.insertNode(span);
28737 var tmp= getDomNode(span, 'firstChild', 'previousSibling');
28738 tmp && pastePop.showAnchor(tmp.nodeType == 3 ? tmp.parentNode : tmp);
28739 domUtils.remove(span);
28740 }else{
28741 pastePop.show();
28742 }
28743 delete editor.ui._isTransfer;
28744 isPaste = false;
28745 }
28746 }, 200)
28747 });
28748 editor.addListener('contextmenu', function (t, evt) {
28749 baidu.editor.ui.Popup.postHide(evt);
28750 });
28751 editor.addListener('keydown', function (t, evt) {
28752 if (pastePop) pastePop.dispose(evt);
28753 var keyCode = evt.keyCode || evt.which;
28754 if(evt.altKey&&keyCode==90){
28755 UE.ui.buttons['fullscreen'].onclick();
28756 }
28757 });
28758 editor.addListener('wordcount', function (type) {
28759 setCount(this,me);
28760 });
28761 function setCount(editor,ui) {
28762 editor.setOpt({
28763 wordCount:true,
28764 maximumWords:10000,
28765 wordCountMsg:editor.options.wordCountMsg || editor.getLang("wordCountMsg"),
28766 wordOverFlowMsg:editor.options.wordOverFlowMsg || editor.getLang("wordOverFlowMsg")
28767 });
28768 var opt = editor.options,
28769 max = opt.maximumWords,
28770 msg = opt.wordCountMsg ,
28771 errMsg = opt.wordOverFlowMsg,
28772 countDom = ui.getDom('wordcount');
28773 if (!opt.wordCount) {
28774 return;
28775 }
28776 var count = editor.getContentLength(true);
28777 if (count > max) {
28778 countDom.innerHTML = errMsg;
28779 editor.fireEvent("wordcountoverflow");
28780 } else {
28781 countDom.innerHTML = msg.replace("{#leave}", max - count).replace("{#count}", count);
28782 }
28783 }
28784
28785 editor.addListener('selectionchange', function () {
28786 if (editor.options.elementPathEnabled) {
28787 me[(editor.queryCommandState('elementpath') == -1 ? 'dis' : 'en') + 'ableElementPath']()
28788 }
28789 if (editor.options.scaleEnabled) {
28790 me[(editor.queryCommandState('scale') == -1 ? 'dis' : 'en') + 'ableScale']();
28791
28792 }
28793 });
28794 var popup = new baidu.editor.ui.Popup({
28795 editor:editor,
28796 content:'',
28797 className:'edui-bubble',
28798 _onEditButtonClick:function () {
28799 this.hide();
28800 editor.ui._dialogs.linkDialog.open();
28801 },
28802 _onImgEditButtonClick:function (name) {
28803 this.hide();
28804 editor.ui._dialogs[name] && editor.ui._dialogs[name].open();
28805
28806 },
28807 _onImgSetFloat:function (value) {
28808 this.hide();
28809 editor.execCommand("imagefloat", value);
28810
28811 },
28812 _setIframeAlign:function (value) {
28813 var frame = popup.anchorEl;
28814 var newFrame = frame.cloneNode(true);
28815 switch (value) {
28816 case -2:
28817 newFrame.setAttribute("align", "");
28818 break;
28819 case -1:
28820 newFrame.setAttribute("align", "left");
28821 break;
28822 case 1:
28823 newFrame.setAttribute("align", "right");
28824 break;
28825 }
28826 frame.parentNode.insertBefore(newFrame, frame);
28827 domUtils.remove(frame);
28828 popup.anchorEl = newFrame;
28829 popup.showAnchor(popup.anchorEl);
28830 },
28831 _updateIframe:function () {
28832 var frame = editor._iframe = popup.anchorEl;
28833 if(domUtils.hasClass(frame, 'ueditor_baidumap')) {
28834 editor.selection.getRange().selectNode(frame).select();
28835 editor.ui._dialogs.mapDialog.open();
28836 popup.hide();
28837 } else {
28838 editor.ui._dialogs.insertframeDialog.open();
28839 popup.hide();
28840 }
28841 },
28842 _onRemoveButtonClick:function (cmdName) {
28843 editor.execCommand(cmdName);
28844 this.hide();
28845 },
28846 queryAutoHide:function (el) {
28847 if (el && el.ownerDocument == editor.document) {
28848 if (el.tagName.toLowerCase() == 'img' || domUtils.findParentByTagName(el, 'a', true)) {
28849 return el !== popup.anchorEl;
28850 }
28851 }
28852 return baidu.editor.ui.Popup.prototype.queryAutoHide.call(this, el);
28853 }
28854 });
28855 popup.render();
28856 if (editor.options.imagePopup) {
28857 editor.addListener('mouseover', function (t, evt) {
28858 evt = evt || window.event;
28859 var el = evt.target || evt.srcElement;
28860 if (editor.ui._dialogs.insertframeDialog && /iframe/ig.test(el.tagName)) {
28861 var html = popup.formatHtml(
28862 '<nobr>' + editor.getLang("property") + ': <span onclick=$$._setIframeAlign(-2) class="edui-clickable">' + editor.getLang("default") + '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(-1) class="edui-clickable">' + editor.getLang("justifyleft") + '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(1) class="edui-clickable">' + editor.getLang("justifyright") + '</span>&nbsp;&nbsp;' +
28863 ' <span onclick="$$._updateIframe( this);" class="edui-clickable">' + editor.getLang("modify") + '</span></nobr>');
28864 if (html) {
28865 popup.getDom('content').innerHTML = html;
28866 popup.anchorEl = el;
28867 popup.showAnchor(popup.anchorEl);
28868 } else {
28869 popup.hide();
28870 }
28871 }
28872 });
28873 editor.addListener('selectionchange', function (t, causeByUi) {
28874 if (!causeByUi) return;
28875 var html = '', str = "",
28876 img = editor.selection.getRange().getClosedNode(),
28877 dialogs = editor.ui._dialogs;
28878 if (img && img.tagName == 'IMG') {
28879 var dialogName = 'insertimageDialog';
28880 if (img.className.indexOf("edui-faked-video") != -1 || img.className.indexOf("edui-upload-video") != -1) {
28881 dialogName = "insertvideoDialog"
28882 }
28883 if (img.className.indexOf("edui-faked-webapp") != -1) {
28884 dialogName = "webappDialog"
28885 }
28886 if (img.src.indexOf("http://api.map.baidu.com") != -1) {
28887 dialogName = "mapDialog"
28888 }
28889 if (img.className.indexOf("edui-faked-music") != -1) {
28890 dialogName = "musicDialog"
28891 }
28892 if (img.src.indexOf("http://maps.google.com/maps/api/staticmap") != -1) {
28893 dialogName = "gmapDialog"
28894 }
28895 if (img.getAttribute("anchorname")) {
28896 dialogName = "anchorDialog";
28897 html = popup.formatHtml(
28898 '<nobr>' + editor.getLang("property") + ': <span onclick=$$._onImgEditButtonClick("anchorDialog") class="edui-clickable">' + editor.getLang("modify") + '</span>&nbsp;&nbsp;' +
28899 '<span onclick=$$._onRemoveButtonClick(\'anchor\') class="edui-clickable">' + editor.getLang("delete") + '</span></nobr>');
28900 }
28901 if (img.getAttribute("word_img")) {
28902 //todo 放到dialog去做查询
28903 editor.word_img = [img.getAttribute("word_img")];
28904 dialogName = "wordimageDialog"
28905 }
28906 if(domUtils.hasClass(img, 'loadingclass') || domUtils.hasClass(img, 'loaderrorclass')) {
28907 dialogName = "";
28908 }
28909 if (!dialogs[dialogName]) {
28910 return;
28911 }
28912 str = '<nobr>' + editor.getLang("property") + ': '+
28913 '<span onclick=$$._onImgSetFloat("none") class="edui-clickable">' + editor.getLang("default") + '</span>&nbsp;&nbsp;' +
28914 '<span onclick=$$._onImgSetFloat("left") class="edui-clickable">' + editor.getLang("justifyleft") + '</span>&nbsp;&nbsp;' +
28915 '<span onclick=$$._onImgSetFloat("right") class="edui-clickable">' + editor.getLang("justifyright") + '</span>&nbsp;&nbsp;' +
28916 '<span onclick=$$._onImgSetFloat("center") class="edui-clickable">' + editor.getLang("justifycenter") + '</span>&nbsp;&nbsp;'+
28917 '<span onclick="$$._onImgEditButtonClick(\'' + dialogName + '\');" class="edui-clickable">' + editor.getLang("modify") + '</span></nobr>';
28918
28919 !html && (html = popup.formatHtml(str))
28920
28921 }
28922 if (editor.ui._dialogs.linkDialog) {
28923 var link = editor.queryCommandValue('link');
28924 var url;
28925 if (link && (url = (link.getAttribute('_href') || link.getAttribute('href', 2)))) {
28926 var txt = url;
28927 if (url.length > 30) {
28928 txt = url.substring(0, 20) + "...";
28929 }
28930 if (html) {
28931 html += '<div style="height:5px;"></div>'
28932 }
28933 html += popup.formatHtml(
28934 '<nobr>' + editor.getLang("anthorMsg") + ': <a target="_blank" href="' + url + '" title="' + url + '" >' + txt + '</a>' +
28935 ' <span class="edui-clickable" onclick="$$._onEditButtonClick();">' + editor.getLang("modify") + '</span>' +
28936 ' <span class="edui-clickable" onclick="$$._onRemoveButtonClick(\'unlink\');"> ' + editor.getLang("clear") + '</span></nobr>');
28937 popup.showAnchor(link);
28938 }
28939 }
28940
28941 if (html) {
28942 popup.getDom('content').innerHTML = html;
28943 popup.anchorEl = img || link;
28944 popup.showAnchor(popup.anchorEl);
28945 } else {
28946 popup.hide();
28947 }
28948 });
28949 }
28950
28951 },
28952 _initToolbars:function () {
28953 var editor = this.editor;
28954 var toolbars = this.toolbars || [];
28955 var toolbarUis = [];
28956 for (var i = 0; i < toolbars.length; i++) {
28957 var toolbar = toolbars[i];
28958 var toolbarUi = new baidu.editor.ui.Toolbar({theme:editor.options.theme});
28959 for (var j = 0; j < toolbar.length; j++) {
28960 var toolbarItem = toolbar[j];
28961 var toolbarItemUi = null;
28962 if (typeof toolbarItem == 'string') {
28963 toolbarItem = toolbarItem.toLowerCase();
28964 if (toolbarItem == '|') {
28965 toolbarItem = 'Separator';
28966 }
28967 if(toolbarItem == '||'){
28968 toolbarItem = 'Breakline';
28969 }
28970 if (baidu.editor.ui[toolbarItem]) {
28971 toolbarItemUi = new baidu.editor.ui[toolbarItem](editor);
28972 }
28973
28974 //fullscreen这里单独处理一下,放到首行去
28975 if (toolbarItem == 'fullscreen') {
28976 if (toolbarUis && toolbarUis[0]) {
28977 toolbarUis[0].items.splice(0, 0, toolbarItemUi);
28978 } else {
28979 toolbarItemUi && toolbarUi.items.splice(0, 0, toolbarItemUi);
28980 }
28981
28982 continue;
28983
28984
28985 }
28986 } else {
28987 toolbarItemUi = toolbarItem;
28988 }
28989 if (toolbarItemUi && toolbarItemUi.id) {
28990
28991 toolbarUi.add(toolbarItemUi);
28992 }
28993 }
28994 toolbarUis[i] = toolbarUi;
28995 }
28996
28997 //接受外部定制的UI
28998
28999 utils.each(UE._customizeUI,function(obj,key){
29000 var itemUI,index;
29001 if(obj.id && obj.id != editor.key){
29002 return false;
29003 }
29004 itemUI = obj.execFn.call(editor,editor,key);
29005 if(itemUI){
29006 index = obj.index;
29007 if(index === undefined){
29008 index = toolbarUi.items.length;
29009 }
29010 toolbarUi.add(itemUI,index)
29011 }
29012 });
29013
29014 this.toolbars = toolbarUis;
29015 },
29016 getHtmlTpl:function () {
29017 return '<div id="##" class="%%">' +
29018 '<div id="##_toolbarbox" class="%%-toolbarbox">' +
29019 (this.toolbars.length ?
29020 '<div id="##_toolbarboxouter" class="%%-toolbarboxouter"><div class="%%-toolbarboxinner">' +
29021 this.renderToolbarBoxHtml() +
29022 '</div></div>' : '') +
29023 '<div id="##_toolbarmsg" class="%%-toolbarmsg" style="display:none;">' +
29024 '<div id = "##_upload_dialog" class="%%-toolbarmsg-upload" onclick="$$.showWordImageDialog();">' + this.editor.getLang("clickToUpload") + '</div>' +
29025 '<div class="%%-toolbarmsg-close" onclick="$$.hideToolbarMsg();">x</div>' +
29026 '<div id="##_toolbarmsg_label" class="%%-toolbarmsg-label"></div>' +
29027 '<div style="height:0;overflow:hidden;clear:both;"></div>' +
29028 '</div>' +
29029 '<div id="##_message_holder" class="%%-messageholder"></div>' +
29030 '</div>' +
29031 '<div id="##_iframeholder" class="%%-iframeholder">' +
29032 '</div>' +
29033 //modify wdcount by matao
29034 '<div id="##_bottombar" class="%%-bottomContainer"><table><tr>' +
29035 '<td id="##_elementpath" class="%%-bottombar"></td>' +
29036 '<td id="##_wordcount" class="%%-wordcount"></td>' +
29037 '<td id="##_scale" class="%%-scale"><div class="%%-icon"></div></td>' +
29038 '</tr></table></div>' +
29039 '<div id="##_scalelayer"></div>' +
29040 '</div>';
29041 },
29042 showWordImageDialog:function () {
29043 this._dialogs['wordimageDialog'].open();
29044 },
29045 renderToolbarBoxHtml:function () {
29046 var buff = [];
29047 for (var i = 0; i < this.toolbars.length; i++) {
29048 buff.push(this.toolbars[i].renderHtml());
29049 }
29050 return buff.join('');
29051 },
29052 setFullScreen:function (fullscreen) {
29053
29054 var editor = this.editor,
29055 container = editor.container.parentNode.parentNode;
29056 if (this._fullscreen != fullscreen) {
29057 this._fullscreen = fullscreen;
29058 this.editor.fireEvent('beforefullscreenchange', fullscreen);
29059 if (baidu.editor.browser.gecko) {
29060 var bk = editor.selection.getRange().createBookmark();
29061 }
29062 if (fullscreen) {
29063 while (container.tagName != "BODY") {
29064 var position = baidu.editor.dom.domUtils.getComputedStyle(container, "position");
29065 nodeStack.push(position);
29066 container.style.position = "static";
29067 container = container.parentNode;
29068 }
29069 this._bakHtmlOverflow = document.documentElement.style.overflow;
29070 this._bakBodyOverflow = document.body.style.overflow;
29071 this._bakAutoHeight = this.editor.autoHeightEnabled;
29072 this._bakScrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
29073
29074 this._bakEditorContaninerWidth = editor.iframe.parentNode.offsetWidth;
29075 if (this._bakAutoHeight) {
29076 //当全屏时不能执行自动长高
29077 editor.autoHeightEnabled = false;
29078 this.editor.disableAutoHeight();
29079 }
29080
29081 document.documentElement.style.overflow = 'hidden';
29082 //修复,滚动条不收起的问题
29083
29084 window.scrollTo(0,window.scrollY);
29085 this._bakCssText = this.getDom().style.cssText;
29086 this._bakCssText1 = this.getDom('iframeholder').style.cssText;
29087 editor.iframe.parentNode.style.width = '';
29088 this._updateFullScreen();
29089 } else {
29090 while (container.tagName != "BODY") {
29091 container.style.position = nodeStack.shift();
29092 container = container.parentNode;
29093 }
29094 this.getDom().style.cssText = this._bakCssText;
29095 this.getDom('iframeholder').style.cssText = this._bakCssText1;
29096 if (this._bakAutoHeight) {
29097 editor.autoHeightEnabled = true;
29098 this.editor.enableAutoHeight();
29099 }
29100
29101 document.documentElement.style.overflow = this._bakHtmlOverflow;
29102 document.body.style.overflow = this._bakBodyOverflow;
29103 editor.iframe.parentNode.style.width = this._bakEditorContaninerWidth + 'px';
29104 window.scrollTo(0, this._bakScrollTop);
29105 }
29106 if (browser.gecko && editor.body.contentEditable === 'true') {
29107 var input = document.createElement('input');
29108 document.body.appendChild(input);
29109 editor.body.contentEditable = false;
29110 setTimeout(function () {
29111 input.focus();
29112 setTimeout(function () {
29113 editor.body.contentEditable = true;
29114 editor.fireEvent('fullscreenchanged', fullscreen);
29115 editor.selection.getRange().moveToBookmark(bk).select(true);
29116 baidu.editor.dom.domUtils.remove(input);
29117 fullscreen && window.scroll(0, 0);
29118 }, 0)
29119 }, 0)
29120 }
29121
29122 if(editor.body.contentEditable === 'true'){
29123 this.editor.fireEvent('fullscreenchanged', fullscreen);
29124 this.triggerLayout();
29125 }
29126
29127 }
29128 },
29129 _updateFullScreen:function () {
29130 if (this._fullscreen) {
29131 var vpRect = uiUtils.getViewportRect();
29132 this.getDom().style.cssText = 'border:0;position:absolute;left:0;top:' + (this.editor.options.topOffset || 0) + 'px;width:' + vpRect.width + 'px;height:' + vpRect.height + 'px;z-index:' + (this.getDom().style.zIndex * 1 + 100);
29133 uiUtils.setViewportOffset(this.getDom(), { left:0, top:this.editor.options.topOffset || 0 });
29134 this.editor.setHeight(vpRect.height - this.getDom('toolbarbox').offsetHeight - this.getDom('bottombar').offsetHeight - (this.editor.options.topOffset || 0),true);
29135 //不手动调一下,会导致全屏失效
29136 if(browser.gecko){
29137 try{
29138 window.onresize();
29139 }catch(e){
29140
29141 }
29142
29143 }
29144 }
29145 },
29146 _updateElementPath:function () {
29147 var bottom = this.getDom('elementpath'), list;
29148 if (this.elementPathEnabled && (list = this.editor.queryCommandValue('elementpath'))) {
29149
29150 var buff = [];
29151 for (var i = 0, ci; ci = list[i]; i++) {
29152 buff[i] = this.formatHtml('<span unselectable="on" onclick="$$.editor.execCommand(&quot;elementpath&quot;, &quot;' + i + '&quot;);">' + ci + '</span>');
29153 }
29154 bottom.innerHTML = '<div class="edui-editor-breadcrumb" onmousedown="return false;">' + this.editor.getLang("elementPathTip") + ': ' + buff.join(' &gt; ') + '</div>';
29155
29156 } else {
29157 bottom.style.display = 'none'
29158 }
29159 },
29160 disableElementPath:function () {
29161 var bottom = this.getDom('elementpath');
29162 bottom.innerHTML = '';
29163 bottom.style.display = 'none';
29164 this.elementPathEnabled = false;
29165
29166 },
29167 enableElementPath:function () {
29168 var bottom = this.getDom('elementpath');
29169 bottom.style.display = '';
29170 this.elementPathEnabled = true;
29171 this._updateElementPath();
29172 },
29173 _scale:function () {
29174 var doc = document,
29175 editor = this.editor,
29176 editorHolder = editor.container,
29177 editorDocument = editor.document,
29178 toolbarBox = this.getDom("toolbarbox"),
29179 bottombar = this.getDom("bottombar"),
29180 scale = this.getDom("scale"),
29181 scalelayer = this.getDom("scalelayer");
29182
29183 var isMouseMove = false,
29184 position = null,
29185 minEditorHeight = 0,
29186 minEditorWidth = editor.options.minFrameWidth,
29187 pageX = 0,
29188 pageY = 0,
29189 scaleWidth = 0,
29190 scaleHeight = 0;
29191
29192 function down() {
29193 position = domUtils.getXY(editorHolder);
29194
29195 if (!minEditorHeight) {
29196 minEditorHeight = editor.options.minFrameHeight + toolbarBox.offsetHeight + bottombar.offsetHeight;
29197 }
29198
29199 scalelayer.style.cssText = "position:absolute;left:0;display:;top:0;background-color:#41ABFF;opacity:0.4;filter: Alpha(opacity=40);width:" + editorHolder.offsetWidth + "px;height:"
29200 + editorHolder.offsetHeight + "px;z-index:" + (editor.options.zIndex + 1);
29201
29202 domUtils.on(doc, "mousemove", move);
29203 domUtils.on(editorDocument, "mouseup", up);
29204 domUtils.on(doc, "mouseup", up);
29205 }
29206
29207 var me = this;
29208 //by xuheng 全屏时关掉缩放
29209 this.editor.addListener('fullscreenchanged', function (e, fullScreen) {
29210 if (fullScreen) {
29211 me.disableScale();
29212
29213 } else {
29214 if (me.editor.options.scaleEnabled) {
29215 me.enableScale();
29216 var tmpNode = me.editor.document.createElement('span');
29217 me.editor.body.appendChild(tmpNode);
29218 me.editor.body.style.height = Math.max(domUtils.getXY(tmpNode).y, me.editor.iframe.offsetHeight - 20) + 'px';
29219 domUtils.remove(tmpNode)
29220 }
29221 }
29222 });
29223 function move(event) {
29224 clearSelection();
29225 var e = event || window.event;
29226 pageX = e.pageX || (doc.documentElement.scrollLeft + e.clientX);
29227 pageY = e.pageY || (doc.documentElement.scrollTop + e.clientY);
29228 scaleWidth = pageX - position.x;
29229 scaleHeight = pageY - position.y;
29230
29231 if (scaleWidth >= minEditorWidth) {
29232 isMouseMove = true;
29233 scalelayer.style.width = scaleWidth + 'px';
29234 }
29235 if (scaleHeight >= minEditorHeight) {
29236 isMouseMove = true;
29237 scalelayer.style.height = scaleHeight + "px";
29238 }
29239 }
29240
29241 function up() {
29242 if (isMouseMove) {
29243 isMouseMove = false;
29244 editor.ui._actualFrameWidth = scalelayer.offsetWidth - 2;
29245 editorHolder.style.width = editor.ui._actualFrameWidth + 'px';
29246
29247 editor.setHeight(scalelayer.offsetHeight - bottombar.offsetHeight - toolbarBox.offsetHeight - 2,true);
29248 }
29249 if (scalelayer) {
29250 scalelayer.style.display = "none";
29251 }
29252 clearSelection();
29253 domUtils.un(doc, "mousemove", move);
29254 domUtils.un(editorDocument, "mouseup", up);
29255 domUtils.un(doc, "mouseup", up);
29256 }
29257
29258 function clearSelection() {
29259 if (browser.ie)
29260 doc.selection.clear();
29261 else
29262 window.getSelection().removeAllRanges();
29263 }
29264
29265 this.enableScale = function () {
29266 //trace:2868
29267 if (editor.queryCommandState("source") == 1) return;
29268 scale.style.display = "";
29269 this.scaleEnabled = true;
29270 domUtils.on(scale, "mousedown", down);
29271 };
29272 this.disableScale = function () {
29273 scale.style.display = "none";
29274 this.scaleEnabled = false;
29275 domUtils.un(scale, "mousedown", down);
29276 };
29277 },
29278 isFullScreen:function () {
29279 return this._fullscreen;
29280 },
29281 postRender:function () {
29282 UIBase.prototype.postRender.call(this);
29283 for (var i = 0; i < this.toolbars.length; i++) {
29284 this.toolbars[i].postRender();
29285 }
29286 var me = this;
29287 var timerId,
29288 domUtils = baidu.editor.dom.domUtils,
29289 updateFullScreenTime = function () {
29290 clearTimeout(timerId);
29291 timerId = setTimeout(function () {
29292 me._updateFullScreen();
29293 });
29294 };
29295 domUtils.on(window, 'resize', updateFullScreenTime);
29296
29297 me.addListener('destroy', function () {
29298 domUtils.un(window, 'resize', updateFullScreenTime);
29299 clearTimeout(timerId);
29300 })
29301 },
29302 showToolbarMsg:function (msg, flag) {
29303 this.getDom('toolbarmsg_label').innerHTML = msg;
29304 this.getDom('toolbarmsg').style.display = '';
29305 //
29306 if (!flag) {
29307 var w = this.getDom('upload_dialog');
29308 w.style.display = 'none';
29309 }
29310 },
29311 hideToolbarMsg:function () {
29312 this.getDom('toolbarmsg').style.display = 'none';
29313 },
29314 mapUrl:function (url) {
29315 return url ? url.replace('~/', this.editor.options.UEDITOR_HOME_URL || '') : ''
29316 },
29317 triggerLayout:function () {
29318 var dom = this.getDom();
29319 if (dom.style.zoom == '1') {
29320 dom.style.zoom = '100%';
29321 } else {
29322 dom.style.zoom = '1';
29323 }
29324 }
29325 };
29326 utils.inherits(EditorUI, baidu.editor.ui.UIBase);
29327
29328
29329 var instances = {};
29330
29331
29332 UE.ui.Editor = function (options) {
29333 var editor = new UE.Editor(options);
29334 editor.options.editor = editor;
29335 utils.loadFile(document, {
29336 href:editor.options.themePath + editor.options.theme + "/css/ueditor.css",
29337 tag:"link",
29338 type:"text/css",
29339 rel:"stylesheet"
29340 });
29341
29342 var oldRender = editor.render;
29343 editor.render = function (holder) {
29344 if (holder.constructor === String) {
29345 editor.key = holder;
29346 instances[holder] = editor;
29347 }
29348 utils.domReady(function () {
29349 editor.langIsReady ? renderUI() : editor.addListener("langReady", renderUI);
29350 function renderUI() {
29351 editor.setOpt({
29352 labelMap:editor.options.labelMap || editor.getLang('labelMap')
29353 });
29354 new EditorUI(editor.options);
29355 if (holder) {
29356 if (holder.constructor === String) {
29357 holder = document.getElementById(holder);
29358 }
29359 holder && holder.getAttribute('name') && ( editor.options.textarea = holder.getAttribute('name'));
29360 if (holder && /script|textarea/ig.test(holder.tagName)) {
29361 var newDiv = document.createElement('div');
29362 holder.parentNode.insertBefore(newDiv, holder);
29363 var cont = holder.value || holder.innerHTML;
29364 editor.options.initialContent = /^[\t\r\n ]*$/.test(cont) ? editor.options.initialContent :
29365 cont.replace(/>[\n\r\t]+([ ]{4})+/g, '>')
29366 .replace(/[\n\r\t]+([ ]{4})+</g, '<')
29367 .replace(/>[\n\r\t]+</g, '><');
29368 holder.className && (newDiv.className = holder.className);
29369 holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);
29370 if (/textarea/i.test(holder.tagName)) {
29371 editor.textarea = holder;
29372 editor.textarea.style.display = 'none';
29373
29374
29375 } else {
29376 holder.parentNode.removeChild(holder);
29377
29378
29379 }
29380 if(holder.id){
29381 newDiv.id = holder.id;
29382 domUtils.removeAttributes(holder,'id');
29383 }
29384 holder = newDiv;
29385 holder.innerHTML = '';
29386 }
29387
29388 }
29389 domUtils.addClass(holder, "edui-" + editor.options.theme);
29390 editor.ui.render(holder);
29391 var opt = editor.options;
29392 //给实例添加一个编辑器的容器引用
29393 editor.container = editor.ui.getDom();
29394 var parents = domUtils.findParents(holder,true);
29395 var displays = [];
29396 for(var i = 0 ,ci;ci=parents[i];i++){
29397 displays[i] = ci.style.display;
29398 ci.style.display = 'block'
29399 }
29400 if (opt.initialFrameWidth) {
29401 opt.minFrameWidth = opt.initialFrameWidth;
29402 } else {
29403 opt.minFrameWidth = opt.initialFrameWidth = holder.offsetWidth;
29404 var styleWidth = holder.style.width;
29405 if(/%$/.test(styleWidth)) {
29406 opt.initialFrameWidth = styleWidth;
29407 }
29408 }
29409 if (opt.initialFrameHeight) {
29410 opt.minFrameHeight = opt.initialFrameHeight;
29411 } else {
29412 opt.initialFrameHeight = opt.minFrameHeight = holder.offsetHeight;
29413 }
29414 for(var i = 0 ,ci;ci=parents[i];i++){
29415 ci.style.display = displays[i]
29416 }
29417 //编辑器最外容器设置了高度,会导致,编辑器不占位
29418 //todo 先去掉,没有找到原因
29419 if(holder.style.height){
29420 holder.style.height = ''
29421 }
29422 editor.container.style.width = opt.initialFrameWidth + (/%$/.test(opt.initialFrameWidth) ? '' : 'px');
29423 editor.container.style.zIndex = opt.zIndex;
29424 oldRender.call(editor, editor.ui.getDom('iframeholder'));
29425 editor.fireEvent("afteruiready");
29426 }
29427 })
29428 };
29429 return editor;
29430 };
29431
29432
29433 /**
29434 * @file
29435 * @name UE
29436 * @short UE
29437 * @desc UEditor的顶部命名空间
29438 */
29439 /**
29440 * @name getEditor
29441 * @since 1.2.4+
29442 * @grammar UE.getEditor(id,[opt]) => Editor实例
29443 * @desc 提供一个全局的方法得到编辑器实例
29444 *
29445 * * ''id'' 放置编辑器的容器id, 如果容器下的编辑器已经存在,就直接返回
29446 * * ''opt'' 编辑器的可选参数
29447 * @example
29448 * UE.getEditor('containerId',{onready:function(){//创建一个编辑器实例
29449 * this.setContent('hello')
29450 * }});
29451 * UE.getEditor('containerId'); //返回刚创建的实例
29452 *
29453 */
29454 UE.getEditor = function (id, opt) {
29455 var editor = instances[id];
29456 if (!editor) {
29457 editor = instances[id] = new UE.ui.Editor(opt);
29458 editor.render(id);
29459 }
29460 return editor;
29461 };
29462
29463
29464 UE.delEditor = function (id) {
29465 var editor;
29466 if (editor = instances[id]) {
29467 editor.key && editor.destroy();
29468 delete instances[id]
29469 }
29470 };
29471
29472 UE.registerUI = function(uiName,fn,index,editorId){
29473 utils.each(uiName.split(/\s+/), function (name) {
29474 UE._customizeUI[name] = {
29475 id : editorId,
29476 execFn:fn,
29477 index:index
29478 };
29479 })
29480
29481 }
29482
29483})();
29484
29485// adapter/message.js
29486UE.registerUI('message', function(editor) {
29487
29488 var editorui = baidu.editor.ui;
29489 var Message = editorui.Message;
29490 var holder;
29491 var _messageItems = [];
29492 var me = editor;
29493
29494 me.addListener('ready', function(){
29495 holder = document.getElementById(me.ui.id + '_message_holder');
29496 updateHolderPos();
29497 setTimeout(function(){
29498 updateHolderPos();
29499 }, 500);
29500 });
29501
29502 me.addListener('showmessage', function(type, opt){
29503 opt = utils.isString(opt) ? {
29504 'content': opt
29505 } : opt;
29506 var message = new Message({
29507 'timeout': opt.timeout,
29508 'type': opt.type,
29509 'content': opt.content,
29510 'keepshow': opt.keepshow,
29511 'editor': me
29512 }),
29513 mid = opt.id || ('msg_' + (+new Date()).toString(36));
29514 message.render(holder);
29515 _messageItems[mid] = message;
29516 message.reset(opt);
29517 updateHolderPos();
29518 return mid;
29519 });
29520
29521 me.addListener('updatemessage',function(type, id, opt){
29522 opt = utils.isString(opt) ? {
29523 'content': opt
29524 } : opt;
29525 var message = _messageItems[id];
29526 message.render(holder);
29527 message && message.reset(opt);
29528 });
29529
29530 me.addListener('hidemessage',function(type, id){
29531 var message = _messageItems[id];
29532 message && message.hide();
29533 });
29534
29535 function updateHolderPos(){
29536 var toolbarbox = me.ui.getDom('toolbarbox');
29537 if (toolbarbox) {
29538 holder.style.top = toolbarbox.offsetHeight + 3 + 'px';
29539 }
29540 holder.style.zIndex = Math.max(me.options.zIndex, me.iframe.style.zIndex) + 1;
29541 }
29542
29543});
29544
29545
29546// adapter/autosave.js
29547UE.registerUI('autosave', function(editor) {
29548 var timer = null,uid = null;
29549 editor.on('afterautosave',function(){
29550 clearTimeout(timer);
29551
29552 timer = setTimeout(function(){
29553 if(uid){
29554 editor.trigger('hidemessage',uid);
29555 }
29556 uid = editor.trigger('showmessage',{
29557 content : editor.getLang('autosave.success'),
29558 timeout : 2000
29559 });
29560
29561 },2000)
29562 })
29563
29564});
29565
29566
29567
29568})();