1 /** 2 * monitor user's paste key ,clear user input,modified from ckeditor 3 * @author yiminghe@gmail.com 4 */ 5 KISSY.add("editor/core/clipboard", function (S, Editor, KERange, KES) { 6 var $ = S.all, 7 UA = S.UA, 8 KER = Editor.RANGE, 9 Event = S.Event; 10 11 function Paste(editor) { 12 var self = this; 13 self.editor = editor; 14 self._init(); 15 } 16 17 S.augment(Paste, { 18 _init:function () { 19 var self = this, 20 editor = self.editor, 21 editorBody = editor.get("document")[0].body; 22 // Event.on(editor.document.body, UA['ie'] ? "beforepaste" : "keydown", self._paste, self); 23 // beforepaste not fire on webkit and firefox 24 // paste fire too later in ie ,cause error 25 // 奇怪哦 26 // http://help.dottoro.com/ljxqbxkf.php 27 // refer : http://stackoverflow.com/questions/2176861/javascript-get-clipboard-data-on-paste-event-cross-browser 28 Event.on(editorBody, 29 (UA.ie ? 'beforepaste' : 'paste'), 30 self._paste, self); 31 32 // Dismiss the (wrong) 'beforepaste' event fired on context menu open. (#7953) 33 // Note: IE Bug: queryCommandEnabled('paste') fires also 'beforepaste(copy/cut)' 34 Event.on(editorBody, 'contextmenu', function () { 35 depressBeforeEvent = 1; 36 setTimeout(function () { 37 depressBeforeEvent = 0; 38 }, 10); 39 }); 40 editor.addCommand("copy", new cutCopyCmd("copy")); 41 editor.addCommand("cut", new cutCopyCmd("cut")); 42 editor.addCommand("paste", new cutCopyCmd("paste")); 43 44 }, 45 _paste:function (ev) { 46 47 if (depressBeforeEvent) { 48 return; 49 } 50 51 // ie beforepaste 会触发两次,第一次 pastebin 为锚点内容,奇怪 52 // chrome keydown 也会两次 53 S.log(ev.type + " : " + " paste event happen"); 54 55 var self = this, 56 editor = self.editor, 57 doc = editor.get("document")[0]; 58 59 // Avoid recursions on 'paste' event or consequent paste too fast. (#5730) 60 if (doc.getElementById('ke_pastebin')) { 61 // ie beforepaste 会重复触发 62 // chrome keydown 也会重复触发 63 // 第一次 bms 是对的,但是 pasterbin 内容是错的 64 // 第二次 bms 是错的,但是内容是对的 65 // 这样返回刚好,用同一个 pastebin 得到最后的正确内容 66 // bms 第一次时创建成功 67 S.log(ev.type + " : trigger more than once ..."); 68 return; 69 } 70 71 var sel = editor.getSelection(), 72 range = new KERange(doc); 73 74 // Create container to paste into 75 var pastebin = $(UA['webkit'] ? '<body></body>' : '<div></div>', null, doc); 76 pastebin.attr('id', 'ke_pastebin'); 77 // Safari requires a filler node inside the div to have the content pasted into it. (#4882) 78 UA['webkit'] && pastebin[0].appendChild(doc.createTextNode('\xa0')); 79 doc.body.appendChild(pastebin[0]); 80 81 pastebin.css({ 82 position:'absolute', 83 // Position the bin exactly at the position of the selected element 84 // to avoid any subsequent document scroll. 85 top:sel.getStartElement().offset().top + 'px', 86 width:'1px', 87 height:'1px', 88 overflow:'hidden' 89 }); 90 91 // It's definitely a better user experience if we make the paste-bin pretty unnoticed 92 // by pulling it off the screen. 93 pastebin.css('left', '-1000px'); 94 95 var bms = sel.createBookmarks(); 96 97 // Turn off design mode temporarily before give focus to the paste bin. 98 range.setStartAt(pastebin, KER.POSITION_AFTER_START); 99 range.setEndAt(pastebin, KER.POSITION_BEFORE_END); 100 range.select(true); 101 // Wait a while and grab the pasted contents 102 setTimeout(function () { 103 104 // Grab the HTML contents. 105 // We need to look for a apple style wrapper on webkit it also adds 106 // a div wrapper if you copy/paste the body of the editor. 107 // Remove hidden div and restore selection. 108 var bogusSpan; 109 110 pastebin = ( UA['webkit'] 111 && ( bogusSpan = pastebin.first() ) 112 && (bogusSpan.hasClass('Apple-style-span') ) ? 113 bogusSpan : pastebin ); 114 115 sel.selectBookmarks(bms); 116 117 pastebin.remove(); 118 119 var html = pastebin.html(); 120 121 //S.log("paster " + html); 122 123 //莫名其妙会有这个东西!,不知道 124 //去掉 125 if (!( html = S.trim(html.replace(/<span[^>]+_ke_bookmark[^<]*?<\/span>( )*/ig, '')) )) { 126 // ie 第2次触发 beforepaste 会报错! 127 // 第一次 bms 是对的,但是 pasterbin 内容是错的 128 // 第二次 bms 是错的,但是内容是对的 129 return; 130 } 131 132 S.log("paste " + html); 133 134 var re = editor.fire("paste", { 135 html:html, 136 holder:pastebin 137 }); 138 139 if (re !== undefined) { 140 html = re; 141 } 142 143 144 // MS-WORD format sniffing. 145 if (/(class="?Mso|style="[^"]*\bmso\-|w:WordDocument)/.test(html)) { 146 // 动态载入 word 过滤规则 147 S.use("editor/core/dynamic/wordFilter", function (S, wordFilter) { 148 editor.insertHtml(wordFilter.toDataFormat(html, editor)); 149 }); 150 } else { 151 editor.insertHtml(html); 152 } 153 154 }, 0); 155 } 156 }); 157 158 // Tries to execute any of the paste, cut or copy commands in IE. Returns a 159 // boolean indicating that the operation succeeded. 160 var execIECommand = function (editor, command) { 161 var doc = editor.get("document")[0], 162 body = $(doc.body); 163 164 var enabled = false; 165 var onExec = function () { 166 enabled = true; 167 }; 168 169 // The following seems to be the only reliable way to detect that 170 // clipboard commands are enabled in IE. It will fire the 171 // onpaste/oncut/oncopy events only if the security settings allowed 172 // the command to execute. 173 body.on(command, onExec); 174 175 // IE6/7: document.execCommand has problem to paste into positioned element. 176 ( UA['ie'] > 7 ? doc : doc.selection.createRange() ) [ 'execCommand' ](command); 177 178 body.detach(command, onExec); 179 180 return enabled; 181 }; 182 183 // Attempts to execute the Cut and Copy operations. 184 var tryToCutCopy = UA['ie'] ? 185 function (editor, type) { 186 return execIECommand(editor, type); 187 } 188 : // !IE. 189 function (editor, type) { 190 try { 191 // Other browsers throw an error if the command is disabled. 192 return editor.get("document")[0].execCommand(type); 193 } 194 catch (e) { 195 return false; 196 } 197 }; 198 199 var error_types = { 200 cut:"您的浏览器安全设置不允许编辑器自动执行剪切操作,请使用键盘快捷键(Ctrl/Cmd+X)来完成", 201 copy:"您的浏览器安全设置不允许编辑器自动执行复制操作,请使用键盘快捷键(Ctrl/Cmd+C)来完成", 202 paste:"您的浏览器安全设置不允许编辑器自动执行粘贴操作,请使用键盘快捷键(Ctrl/Cmd+V)来完成" 203 }; 204 205 // A class that represents one of the cut or copy commands. 206 var cutCopyCmd = function (type) { 207 this.type = type; 208 }; 209 210 cutCopyCmd.prototype = { 211 exec:function (editor) { 212 this.type == 'cut' && fixCut(editor); 213 214 var success = tryToCutCopy(editor, this.type); 215 216 if (!success) 217 alert(error_types[this.type]); // Show cutError or copyError. 218 219 return success; 220 } 221 }; 222 223 // Cutting off control type element in IE standards breaks the selection entirely. (#4881) 224 function fixCut(editor) { 225 if (!UA['ie'] || editor.get("document")[0].compatMode == 'BackCompat') 226 return; 227 228 var sel = editor.getSelection(); 229 var control; 230 if (( sel.getType() == KES.SELECTION_ELEMENT ) && ( control = sel.getSelectedElement() )) { 231 var range = sel.getRanges()[ 0 ]; 232 var dummy = $(editor.get("document")[0].createTextNode('')); 233 dummy.insertBefore(control); 234 range.setStartBefore(dummy); 235 range.setEndAfter(control); 236 sel.selectRanges([ range ]); 237 238 // Clear up the fix if the paste wasn't succeeded. 239 setTimeout(function () { 240 // Element still online? 241 if (control.parent()) { 242 dummy.remove(); 243 sel.selectElement(control); 244 } 245 }, 0); 246 } 247 } 248 249 var lang = { 250 copy:"复制", 251 paste:"粘贴", 252 cut:"剪切" 253 }; 254 255 var depressBeforeEvent; 256 257 return { 258 init:function (editor) { 259 editor.docReady(function () { 260 new Paste(editor); 261 }); 262 263 var pastes = {copy:1, cut:1, paste:1}; 264 265 /** 266 * 给所有右键都加入复制粘贴 267 */ 268 editor.on("contextmenu", function (ev) { 269 var contextmenu = ev.contextmenu; 270 271 if (contextmenu.__copy_fix) { 272 return; 273 } 274 275 contextmenu.__copy_fix = 1; 276 277 for (var i in pastes) { 278 if (pastes.hasOwnProperty(i)) { 279 contextmenu.addChild({ 280 xclass:'menuitem', 281 content:lang[i], 282 value:i 283 }); 284 } 285 } 286 287 contextmenu.on('click', function (e) { 288 var value = e.target.get("value"); 289 if (pastes[value]) { 290 this.hide(); 291 // 给 ie 一点 hide() 中的事件触发 handler 运行机会, 292 // 原编辑器获得焦点后再进行下步操作 293 setTimeout(function () { 294 editor.execCommand(value); 295 }, 30); 296 } 297 }); 298 }); 299 } 300 }; 301 }, { 302 requires:['./base', './range', './selection'] 303 }); 304