1(function($) { 2 3CKEDITOR.disableAutoInline = true; 4 5// Exclude every id starting with 'cke_' in ajax_html_ids during AJAX requests. 6Drupal.wysiwyg.excludeIdSelectors.wysiwyg_ckeditor = ['[id^="cke_"]']; 7 8// Keeps track of private instance data. 9var instanceMap; 10 11/** 12 * Initialize the editor library. 13 * 14 * This method is called once the first time a library is needed. If new 15 * WYSIWYG fields are added later, update() will be called instead. 16 * 17 * @param settings 18 * An object containing editor settings for each input format. 19 * @param pluginInfo 20 * An object containing global plugin configuration. 21 */ 22Drupal.wysiwyg.editor.init.ckeditor = function(settings, pluginInfo) { 23 instanceMap = {}; 24 25 // Manually set the cache-busting string to the same value as Drupal. 26 if (Drupal.settings.wysiwyg.ckeditor.hasOwnProperty('timestamp')) { 27 CKEDITOR.timestamp = Drupal.settings.wysiwyg.ckeditor.timestamp; 28 } 29 30 // Nothing to do here other than register new plugins etc. 31 Drupal.wysiwyg.editor.update.ckeditor(settings, pluginInfo); 32}; 33 34/** 35 * Update the editor library when new settings are available. 36 * 37 * This method is called instead of init() when at least one new WYSIWYG field 38 * has been added to the document and the library has already been initialized. 39 * 40 * $param settings 41 * An object containing editor settings for each input format. 42 * $param pluginInfo 43 * An object containing global plugin configuration. 44 */ 45Drupal.wysiwyg.editor.update.ckeditor = function(settings, pluginInfo) { 46 // Register native external plugins. 47 // Array syntax required; 'native' is a predefined token in JavaScript. 48 for (var pluginId in pluginInfo['native']) { 49 if (pluginInfo['native'].hasOwnProperty(pluginId) && (!CKEDITOR.plugins.externals || !CKEDITOR.plugins.externals[pluginId])) { 50 var plugin = pluginInfo['native'][pluginId]; 51 CKEDITOR.plugins.addExternal(pluginId, plugin.path, plugin.fileName); 52 } 53 } 54 // Build and register Drupal plugin wrappers. 55 for (var pluginId in pluginInfo.drupal) { 56 if (pluginInfo.drupal.hasOwnProperty(pluginId) && (!CKEDITOR.plugins.registered || !CKEDITOR.plugins.registered[pluginId])) { 57 Drupal.wysiwyg.editor.instance.ckeditor.addPlugin(pluginId, pluginInfo.drupal[pluginId]); 58 } 59 } 60 // Register Font styles (versions 3.2.1 and above). 61 for (var format in settings) { 62 if (settings[format].stylesSet && (!CKEDITOR.stylesSet || !CKEDITOR.stylesSet.registered[format])) { 63 CKEDITOR.stylesSet.add(format, settings[format].stylesSet); 64 } 65 } 66}; 67 68/** 69 * Attach this editor to a target element. 70 */ 71Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) { 72 // Apply editor instance settings. 73 CKEDITOR.config.customConfig = ''; 74 75 if (!settings.height) { 76 settings.height = $('#' + params.field).height(); 77 } 78 // Handler for any change-related event. 79 function changed(ev) { 80 instanceMap[ev.editor.name].contentsChanged(); 81 } 82 settings.on = { 83 // Versions 4.x has a change event, 3.x does not. 84 change: function (ev) { 85 changed(ev); 86 }, 87 contentDom: function (ev) { 88 var version_parts = CKEDITOR.version.split('.'); 89 if (version_parts[0] === '3' || (version_parts[0] === '4' && parseInt(version_parts[1], 10) < 2)) { 90 ev.editor.on('key', function (ev) { 91 // Do not capture modifiers. 92 if (ev.data.ctrlKey || ev.data.metaKey) 93 return; 94 95 var keyCode = ev.data.keyCode; 96 // Filter out movement keys and related. 97 if (keyCode == 8 || keyCode == 13 || keyCode == 32 98 || (keyCode >= 46 && keyCode <= 90) || (keyCode >= 96 && keyCode <= 111) 99 || (keyCode >= 186 && keyCode <= 222) || keyCode == 229) { 100 changed(ev); 101 } 102 }); 103 ev.editor.on('paste', changed); 104 ev.editor.on('saveSnapshot', function (ev) { 105 if (instanceMap[ev.editor.name].firstSaveSnapshot) { 106 // The first save snapshot event is triggered when the editor is 107 // focused and before anything has changed. 108 instanceMap[ev.editor.name].firstSaveSnapshot = false;
109 return; 110 } 111 changed(ev); 112 }); 113 } 114 }, 115 instanceReady: function(ev) { 116 var editor = ev.editor; 117 // Get a list of block, list and table tags from CKEditor's XHTML DTD. 118 // @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Output_Formatting. 119 var dtd = CKEDITOR.dtd; 120 var tags = CKEDITOR.tools.extend({}, dtd.$block, dtd.$listItem, dtd.$tableContent); 121 // Set source formatting rules for each listed tag except <pre>. 122 // Linebreaks can be inserted before or after opening and closing tags. 123 if (settings.simple_source_formatting) { 124 // Mimic FCKeditor output, by breaking lines between tags. 125 for (var tag in tags) { 126 if (tag == 'pre') { 127 continue; 128 } 129 this.dataProcessor.writer.setRules(tag, { 130 indent: true, 131 breakBeforeOpen: true, 132 breakAfterOpen: false, 133 breakBeforeClose: false, 134 breakAfterClose: true 135 }); 136 } 137 } 138 else { 139 // CKEditor adds default formatting to <br>, so we want to remove that 140 // here too. 141 tags.br = 1; 142 // No indents or linebreaks; 143 for (var tag in tags) { 144 if (tag == 'pre') { 145 continue; 146 } 147 this.dataProcessor.writer.setRules(tag, { 148 indent: false, 149 breakBeforeOpen: false, 150 breakAfterOpen: false, 151 breakBeforeClose: false, 152 breakAfterClose: false 153 }); 154 } 155 } 156 }, 157 158 pluginsLoaded: function(ev) { 159 var wysiwygInstance = instanceMap[this.name]; 160 var enabledPlugins = wysiwygInstance.pluginInfo.instances.drupal; 161 // Override the conversion methods to let Drupal plugins modify the data. 162 var editor = ev.editor; 163 if (editor.dataProcessor && enabledPlugins) { 164 editor.dataProcessor.toHtml = CKEDITOR.tools.override(editor.dataProcessor.toHtml, function(originalToHtml) { 165 // Convert raw data for display in WYSIWYG mode. 166 return function(data, fixForBody) { 167 for (var plugin in enabledPlugins) { 168 if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') { 169 data = Drupal.wysiwyg.plugins[plugin].attach(data, wysiwygInstance.pluginInfo.global.drupal[plugin], editor.name); 170 data = wysiwygInstance.prepareContent(data); 171 } 172 } 173 return originalToHtml.call(this, data, fixForBody); 174 }; 175 }); 176 editor.dataProcessor.toDataFormat = CKEDITOR.tools.override(editor.dataProcessor.toDataFormat, function(originalToDataFormat) { 177 // Convert WYSIWYG mode content to raw data. 178 return function(data, fixForBody) { 179 data = originalToDataFormat.call(this, data, fixForBody); 180 for (var plugin in enabledPlugins) { 181 if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') { 182 data = Drupal.wysiwyg.plugins[plugin].detach(data, wysiwygInstance.pluginInfo.global.drupal[plugin], editor.name); 183 } 184 } 185 return data; 186 }; 187 }); 188 } 189 }, 190 191 selectionChange: function (event) { 192 var wysiwygInstance = instanceMap[this.name]; 193 var enabledPlugins = wysiwygInstance.pluginInfo.instances.drupal; 194 for (var name in enabledPlugins) { 195 var plugin = Drupal.wysiwyg.plugins[name]; 196 if ($.isFunction(plugin.isNode)) { 197 var node = event.data.selection.getSelectedElement(); 198 var state = plugin.isNode(node ? node.$ : null) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; 199 event.editor.getCommand(name).setState(state); 200 } 201 } 202 }, 203 204 focus: function(ev) { 205 Drupal.wysiwyg.activeId = ev.editor.name; 206 }, 207 208 afterCommandExec: function(ev) { 209 // Fix Drupal toolbar obscuring editor toolbar in fullscreen mode. 210 if (ev.data.name != 'maximize') { 211 return; 212 } 213 if (ev.data.command.state == CKEDITOR.TRISTATE_ON) { 214 Drupal.wysiwyg.utilities.onFullscreenEnter(); 215 } 216 else { 217 Drupal.wysiwyg.utilities.onFullscreenExit(); 218 } 219 }, 220 221 destroy: function (event) { 222 // Free our reference to the private instance to not risk memory leaks. 223 delete instanceMap[this.name]; 224 } 225 }; 226 instanceMap[params.field] = this; 227 // Attach editor. 228 var editorInstance = CKEDITOR.replace(params.field, settings); 229}; 230 231/** 232 * Detach a single editor instance. 233 */ 234Drupal.wysiwyg.editor.detach.ckeditor = function (context, params, trigger) { 235 var method = (trigger == 'serialize') ? 'updateElement' : 'destroy'; 236 var instance = CKEDITOR.instances[params.field]; 237 if (!instance) { 238 return; 239 } 240 instance[method](); 241}; 242 243Drupal.wysiwyg.editor.instance.ckeditor = { 244 245 // Flag indicating if the first save snapshot event has fired. 246 firstSaveSnapshot: true, 247 248 addPlugin: function (pluginName, pluginSettings) { 249 CKEDITOR.plugins.add(pluginName, { 250 // Wrap Drupal plugin in a proxy plugin. 251 init: function(editor) { 252 if (pluginSettings.css) { 253 editor.on('mode', function(ev) { 254 if (ev.editor.mode == 'wysiwyg') { 255 // Inject CSS files directly into the editing area head tag. 256 var iframe = $('#cke_contents_' + ev.editor.name + ' iframe, #' + ev.editor.id + '_contents iframe'); 257 $('head', iframe.eq(0).contents()).append('<link rel="stylesheet" href="' + pluginSettings.css + '" type="text/css" >'); 258 } 259 }); 260 } 261 if (typeof Drupal.wysiwyg.plugins[pluginName].invoke == 'function') { 262 var pluginCommand = { 263 exec: function (editor) { 264 var data = { format: 'html', node: null, content: '' }; 265 var selection = editor.getSelection(); 266 if (selection) { 267 data.node = selection.getSelectedElement(); 268 if (data.node) { 269 data.node = data.node.$; 270 } 271 if (selection.getType() == CKEDITOR.SELECTION_TEXT) { 272 if (selection.getSelectedText) { 273 data.content = selection.getSelectedText(); 274 } 275 else { 276 // Pre v3.6.1. 277 if (CKEDITOR.env.ie) { 278 data.content = selection.getNative().createRange().text; 279 } 280 else { 281 data.content = selection.getNative().toString(); 282 } 283 } 284 } 285 else if (data.node) { 286 // content is supposed to contain the "outerHTML". 287 data.content = data.node.parentNode.innerHTML; 288 } 289 } 290 Drupal.wysiwyg.plugins[pluginName].invoke(data, pluginSettings, editor.name); 291 } 292 }; 293 editor.addCommand(pluginName, pluginCommand); 294 } 295 editor.ui.addButton(pluginName, { 296 label: pluginSettings.title, 297 command: pluginName, 298 icon: pluginSettings.icon 299 }); 300 301 // @todo Add button state handling. 302 } 303 }); 304 }, 305 prepareContent: function(content) { 306 // @todo Don't know if we need this yet. 307 return content; 308 }, 309 310 insert: function(content) { 311 content = this.prepareContent(content); 312 if (CKEDITOR.version.split('.')[0] === '3' && (CKEDITOR.env.webkit || CKEDITOR.env.chrome || CKEDITOR.env.opera || CKEDITOR.env.safari)) { 313 // Works around a WebKit bug which removes wrapper elements. 314 // @see https://drupal.org/node/1927968 315 var tmp = new CKEDITOR.dom.element('div'), children, skip = 0, item; 316 tmp.setHtml(content); 317 children = tmp.getChildren(); 318 skip = 0; 319 while (children.count() > skip) { 320 item = children.getItem(skip); 321 switch(item.type) { 322 case 1: 323 CKEDITOR.instances[this.field].insertElement(item); 324 break; 325 case 3: 326 CKEDITOR.instances[this.field].insertText(item.getText()); 327 skip++; 328 break; 329 case 8: 330 CKEDITOR.instances[this.field].insertHtml(item.getOuterHtml()); 331 skip++; 332 break; 333 default: 334 skip++; 335 } 336 } 337 } 338 else { 339 CKEDITOR.instances[this.field].insertHtml(content); 340 } 341 }, 342 343 setContent: function (content) { 344 CKEDITOR.instances[this.field].setData(content); 345 }, 346 347 getContent: function () { 348 return CKEDITOR.instances[this.field].getData(); 349 }, 350 351 isFullscreen: function () { 352 var cmd = CKEDITOR.instances[this.field].commands.maximize; 353 return !!(cmd && cmd.state == CKEDITOR.TRISTATE_ON); 354 } 355}; 356 357})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.