1 /* 2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 (function() 7 { 8 var eventNameList = [ 'click', 'keydown', 'mousedown', 'keypress', 'mouseover', 'mouseout' ]; 9 10 // Inline event callbacks assigned via innerHTML/outerHTML, such as 11 // onclick/onmouseover, are ignored in AIR. 12 // Use DOM2 event listeners to substitue inline handlers instead. 13 function convertInlineHandlers( container ) 14 { 15 // TODO: document.querySelectorAll is not supported in AIR. 16 var children = container.getElementsByTag( '*' ), 17 count = children.count(), 18 child; 19 20 for ( var i = 0; i < count; i++ ) 21 { 22 child = children.getItem( i ); 23 24 (function( node ) 25 { 26 for ( var j = 0; j < eventNameList.length; j++ ) 27 { 28 (function( eventName ) 29 { 30 var inlineEventHandler = node.getAttribute( 'on' + eventName ); 31 if ( node.hasAttribute( 'on' + eventName ) ) 32 { 33 node.removeAttribute( 'on' + eventName ); 34 node.on( eventName, function( evt ) 35 { 36 var callFunc = /(return\s*)?CKEDITOR\.tools\.callFunction\(([^)]+)\)/.exec( inlineEventHandler ), 37 hasReturn = callFunc && callFunc[ 1 ], 38 callFuncArgs = callFunc && callFunc[ 2 ].split( ',' ), 39 preventDefault = /return false;/.test( inlineEventHandler ); 40 41 if ( callFuncArgs ) 42 { 43 var nums = callFuncArgs.length, 44 argName; 45 46 for ( var i = 0; i < nums; i++ ) 47 { 48 // Trim spaces around param. 49 callFuncArgs[ i ] = argName = CKEDITOR.tools.trim( callFuncArgs[ i ] ); 50 51 // String form param. 52 var strPattern = argName.match( /^(["'])([^"']*?)\1$/ ); 53 if ( strPattern ) 54 { 55 callFuncArgs[ i ] = strPattern[ 2 ]; 56 continue; 57 } 58 59 // Integer form param. 60 if ( argName.match( /\d+/ ) ) 61 { 62 callFuncArgs[ i ] = parseInt( argName, 10 ); 63 continue; 64 } 65 66 // Speical variables. 67 switch( argName ) 68 { 69 case 'this' : 70 callFuncArgs[ i ] = node.$; 71 break; 72 case 'event' : 73 callFuncArgs[ i ] = evt.data.$; 74 break; 75 case 'null' : 76 callFuncArgs [ i ] = null; 77 break; 78 } 79 } 80 81 var retval = CKEDITOR.tools.callFunction.apply( window, callFuncArgs ); 82 if ( hasReturn && retval === false ) 83 preventDefault = 1; 84 } 85 86 if ( preventDefault ) 87 evt.data.preventDefault(); 88 }); 89 } 90 })( eventNameList[ j ] ); 91 } 92 })( child ); 93 } 94 } 95 96 CKEDITOR.plugins.add( 'adobeair', 97 { 98 init : function( editor ) 99 { 100 if ( !CKEDITOR.env.air ) 101 return; 102 103 // Body doesn't get default margin on AIR. 104 editor.addCss( 'body { padding: 8px }' ); 105 106 editor.on( 'uiReady', function() 107 { 108 convertInlineHandlers( editor.container ); 109 110 if ( editor.sharedSpaces ) 111 { 112 for ( var space in editor.sharedSpaces ) 113 convertInlineHandlers( editor.sharedSpaces[ space ] ); 114 } 115 116 editor.on( 'elementsPathUpdate', function( evt ) { convertInlineHandlers( evt.data.space ); } ); 117 }); 118 119 editor.on( 'contentDom', function() 120 { 121 // Hyperlinks are enabled in editable documents in Adobe 122 // AIR. Prevent their click behavior. 123 editor.document.on( 'click', function( ev ) 124 { 125 ev.data.preventDefault( true ); 126 }); 127 }); 128 } 129 }); 130 131 CKEDITOR.ui.on( 'ready', function( evt ) 132 { 133 var ui = evt.data; 134 // richcombo, panelbutton and menu 135 if ( ui._.panel ) 136 { 137 var panel = ui._.panel._.panel, 138 holder; 139 140 ( function() 141 { 142 // Adding dom event listeners off-line are not supported in AIR, 143 // waiting for panel iframe loaded. 144 if ( !panel.isLoaded ) 145 { 146 setTimeout( arguments.callee, 30 ); 147 return; 148 } 149 holder = panel._.holder; 150 convertInlineHandlers( holder ); 151 })(); 152 } 153 else if ( ui instanceof CKEDITOR.dialog ) 154 convertInlineHandlers( ui._.element ); 155 }); 156 })(); 157 158 CKEDITOR.dom.document.prototype.write = CKEDITOR.tools.override( CKEDITOR.dom.document.prototype.write, 159 function( original_write ) 160 { 161 function appendElement( parent, tagName, fullTag, text ) 162 { 163 var node = parent.append( tagName ), 164 attrs = CKEDITOR.htmlParser.fragment.fromHtml( fullTag ).children[ 0 ].attributes; 165 attrs && node.setAttributes( attrs ); 166 text && node.append( parent.getDocument().createText( text ) ); 167 } 168 169 return function( html, mode ) 170 { 171 // document.write() or document.writeln() fail silently after 172 // the page load event in Adobe AIR. 173 // DOM manipulation could be used instead. 174 if ( this.getBody() ) 175 { 176 // We're taking the below extra work only because innerHTML 177 // on <html> element doesn't work as expected. 178 var doc = this, 179 head = this.getHead(); 180 181 // Create style nodes for inline css. ( <style> content doesn't applied when setting via innerHTML ) 182 html = html.replace( /(<style[^>]*>)([\s\S]*?)<\/style>/gi, 183 function ( match, startTag, styleText ) 184 { 185 appendElement( head, 'style', startTag, styleText ); 186 return ''; 187 }); 188 189 html = html.replace( /<base\b[^>]*\/>/i, 190 function( match ) 191 { 192 appendElement( head, 'base', match ); 193 return ''; 194 }); 195 196 html = html.replace( /<title>([\s\S]*)<\/title>/i, 197 function( match, title ) 198 { 199 doc.$.title = title; 200 return ''; 201 }); 202 203 // Move the rest of head stuff. 204 html = html.replace( /<head>([\s\S]*)<\/head>/i, 205 function( headHtml ) 206 { 207 // Inject the <head> HTML inside a <div>. 208 // Do that before getDocumentHead because WebKit moves 209 // <link css> elements to the <head> at this point. 210 var div = new CKEDITOR.dom.element( 'div', doc ); 211 div.setHtml( headHtml ); 212 // Move the <div> nodes to <head>. 213 div.moveChildren( head ); 214 return ''; 215 }); 216 217 html.replace( /(<body[^>]*>)([\s\S]*)(?=$|<\/body>)/i, 218 function( match, startTag, innerHTML ) 219 { 220 doc.getBody().setHtml( innerHTML ); 221 var attrs = CKEDITOR.htmlParser.fragment.fromHtml( startTag ).children[ 0 ].attributes; 222 attrs && doc.getBody().setAttributes( attrs ); 223 }); 224 } 225 else 226 original_write.apply( this, arguments ); 227 }; 228 }); 229