annotate static/js/tiny_mce/plugins/fullpage/editor_plugin_src.js @ 887:9a15f7c27526

Actually save model object upon change. This commit was tested on the comments model. Additional logging added. Added check for Markdown image references. Added TODOs after observing behavior on comments.
author Brian Neal <bgneal@gmail.com>
date Tue, 03 Feb 2015 21:09:44 -0600
parents 6c182ceb7147
children
rev   line source
bgneal@312 1 /**
bgneal@312 2 * editor_plugin_src.js
bgneal@312 3 *
bgneal@312 4 * Copyright 2009, Moxiecode Systems AB
bgneal@312 5 * Released under LGPL License.
bgneal@312 6 *
bgneal@312 7 * License: http://tinymce.moxiecode.com/license
bgneal@312 8 * Contributing: http://tinymce.moxiecode.com/contributing
bgneal@312 9 */
bgneal@312 10
bgneal@312 11 (function() {
bgneal@442 12 var each = tinymce.each, Node = tinymce.html.Node;
bgneal@442 13
bgneal@312 14 tinymce.create('tinymce.plugins.FullPagePlugin', {
bgneal@312 15 init : function(ed, url) {
bgneal@312 16 var t = this;
bgneal@312 17
bgneal@312 18 t.editor = ed;
bgneal@312 19
bgneal@312 20 // Register commands
bgneal@312 21 ed.addCommand('mceFullPageProperties', function() {
bgneal@312 22 ed.windowManager.open({
bgneal@312 23 file : url + '/fullpage.htm',
bgneal@312 24 width : 430 + parseInt(ed.getLang('fullpage.delta_width', 0)),
bgneal@312 25 height : 495 + parseInt(ed.getLang('fullpage.delta_height', 0)),
bgneal@312 26 inline : 1
bgneal@312 27 }, {
bgneal@312 28 plugin_url : url,
bgneal@442 29 data : t._htmlToData()
bgneal@312 30 });
bgneal@312 31 });
bgneal@312 32
bgneal@312 33 // Register buttons
bgneal@312 34 ed.addButton('fullpage', {title : 'fullpage.desc', cmd : 'mceFullPageProperties'});
bgneal@312 35
bgneal@312 36 ed.onBeforeSetContent.add(t._setContent, t);
bgneal@312 37 ed.onGetContent.add(t._getContent, t);
bgneal@312 38 },
bgneal@312 39
bgneal@312 40 getInfo : function() {
bgneal@312 41 return {
bgneal@312 42 longname : 'Fullpage',
bgneal@312 43 author : 'Moxiecode Systems AB',
bgneal@312 44 authorurl : 'http://tinymce.moxiecode.com',
bgneal@312 45 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullpage',
bgneal@312 46 version : tinymce.majorVersion + "." + tinymce.minorVersion
bgneal@312 47 };
bgneal@312 48 },
bgneal@312 49
bgneal@312 50 // Private plugin internal methods
bgneal@312 51
bgneal@442 52 _htmlToData : function() {
bgneal@442 53 var headerFragment = this._parseHeader(), data = {}, nodes, elm, matches, editor = this.editor;
bgneal@312 54
bgneal@442 55 function getAttr(elm, name) {
bgneal@442 56 var value = elm.attr(name);
bgneal@312 57
bgneal@442 58 return value || '';
bgneal@442 59 };
bgneal@312 60
bgneal@442 61 // Default some values
bgneal@442 62 data.fontface = editor.getParam("fullpage_default_fontface", "");
bgneal@442 63 data.fontsize = editor.getParam("fullpage_default_fontsize", "");
bgneal@312 64
bgneal@442 65 // Parse XML PI
bgneal@442 66 elm = headerFragment.firstChild;
bgneal@442 67 if (elm.type == 7) {
bgneal@442 68 data.xml_pi = true;
bgneal@442 69 matches = /encoding="([^"]+)"/.exec(elm.value);
bgneal@442 70 if (matches)
bgneal@442 71 data.docencoding = matches[1];
bgneal@442 72 }
bgneal@312 73
bgneal@442 74 // Parse doctype
bgneal@442 75 elm = headerFragment.getAll('#doctype')[0];
bgneal@442 76 if (elm)
bgneal@442 77 data.doctype = '<!DOCTYPE' + elm.value + ">";
bgneal@442 78
bgneal@442 79 // Parse title element
bgneal@442 80 elm = headerFragment.getAll('title')[0];
bgneal@442 81 if (elm && elm.firstChild) {
bgneal@442 82 data.metatitle = elm.firstChild.value;
bgneal@442 83 }
bgneal@442 84
bgneal@442 85 // Parse meta elements
bgneal@442 86 each(headerFragment.getAll('meta'), function(meta) {
bgneal@442 87 var name = meta.attr('name'), httpEquiv = meta.attr('http-equiv'), matches;
bgneal@442 88
bgneal@442 89 if (name)
bgneal@442 90 data['meta' + name.toLowerCase()] = meta.attr('content');
bgneal@442 91 else if (httpEquiv == "Content-Type") {
bgneal@442 92 matches = /charset\s*=\s*(.*)\s*/gi.exec(meta.attr('content'));
bgneal@442 93
bgneal@442 94 if (matches)
bgneal@442 95 data.docencoding = matches[1];
bgneal@442 96 }
bgneal@442 97 });
bgneal@442 98
bgneal@442 99 // Parse html attribs
bgneal@442 100 elm = headerFragment.getAll('html')[0];
bgneal@442 101 if (elm)
bgneal@442 102 data.langcode = getAttr(elm, 'lang') || getAttr(elm, 'xml:lang');
bgneal@442 103
bgneal@442 104 // Parse stylesheet
bgneal@442 105 elm = headerFragment.getAll('link')[0];
bgneal@442 106 if (elm && elm.attr('rel') == 'stylesheet')
bgneal@442 107 data.stylesheet = elm.attr('href');
bgneal@442 108
bgneal@442 109 // Parse body parts
bgneal@442 110 elm = headerFragment.getAll('body')[0];
bgneal@442 111 if (elm) {
bgneal@442 112 data.langdir = getAttr(elm, 'dir');
bgneal@442 113 data.style = getAttr(elm, 'style');
bgneal@442 114 data.visited_color = getAttr(elm, 'vlink');
bgneal@442 115 data.link_color = getAttr(elm, 'link');
bgneal@442 116 data.active_color = getAttr(elm, 'alink');
bgneal@442 117 }
bgneal@442 118
bgneal@442 119 return data;
bgneal@442 120 },
bgneal@442 121
bgneal@442 122 _dataToHtml : function(data) {
bgneal@442 123 var headerFragment, headElement, html, elm, value, dom = this.editor.dom;
bgneal@442 124
bgneal@442 125 function setAttr(elm, name, value) {
bgneal@442 126 elm.attr(name, value ? value : undefined);
bgneal@442 127 };
bgneal@442 128
bgneal@442 129 function addHeadNode(node) {
bgneal@442 130 if (headElement.firstChild)
bgneal@442 131 headElement.insert(node, headElement.firstChild);
bgneal@442 132 else
bgneal@442 133 headElement.append(node);
bgneal@442 134 };
bgneal@442 135
bgneal@442 136 headerFragment = this._parseHeader();
bgneal@442 137 headElement = headerFragment.getAll('head')[0];
bgneal@442 138 if (!headElement) {
bgneal@442 139 elm = headerFragment.getAll('html')[0];
bgneal@442 140 headElement = new Node('head', 1);
bgneal@442 141
bgneal@442 142 if (elm.firstChild)
bgneal@442 143 elm.insert(headElement, elm.firstChild, true);
bgneal@442 144 else
bgneal@442 145 elm.append(headElement);
bgneal@442 146 }
bgneal@442 147
bgneal@442 148 // Add/update/remove XML-PI
bgneal@442 149 elm = headerFragment.firstChild;
bgneal@442 150 if (data.xml_pi) {
bgneal@442 151 value = 'version="1.0"';
bgneal@442 152
bgneal@442 153 if (data.docencoding)
bgneal@442 154 value += ' encoding="' + data.docencoding + '"';
bgneal@442 155
bgneal@442 156 if (elm.type != 7) {
bgneal@442 157 elm = new Node('xml', 7);
bgneal@442 158 headerFragment.insert(elm, headerFragment.firstChild, true);
bgneal@442 159 }
bgneal@442 160
bgneal@442 161 elm.value = value;
bgneal@442 162 } else if (elm && elm.type == 7)
bgneal@442 163 elm.remove();
bgneal@442 164
bgneal@442 165 // Add/update/remove doctype
bgneal@442 166 elm = headerFragment.getAll('#doctype')[0];
bgneal@442 167 if (data.doctype) {
bgneal@442 168 if (!elm) {
bgneal@442 169 elm = new Node('#doctype', 10);
bgneal@442 170
bgneal@442 171 if (data.xml_pi)
bgneal@442 172 headerFragment.insert(elm, headerFragment.firstChild);
bgneal@442 173 else
bgneal@442 174 addHeadNode(elm);
bgneal@442 175 }
bgneal@442 176
bgneal@442 177 elm.value = data.doctype.substring(9, data.doctype.length - 1);
bgneal@442 178 } else if (elm)
bgneal@442 179 elm.remove();
bgneal@442 180
bgneal@442 181 // Add/update/remove title
bgneal@442 182 elm = headerFragment.getAll('title')[0];
bgneal@442 183 if (data.metatitle) {
bgneal@442 184 if (!elm) {
bgneal@442 185 elm = new Node('title', 1);
bgneal@442 186 elm.append(new Node('#text', 3)).value = data.metatitle;
bgneal@442 187 addHeadNode(elm);
bgneal@442 188 }
bgneal@442 189 }
bgneal@442 190
bgneal@442 191 // Add meta encoding
bgneal@442 192 if (data.docencoding) {
bgneal@442 193 elm = null;
bgneal@442 194 each(headerFragment.getAll('meta'), function(meta) {
bgneal@442 195 if (meta.attr('http-equiv') == 'Content-Type')
bgneal@442 196 elm = meta;
bgneal@442 197 });
bgneal@442 198
bgneal@442 199 if (!elm) {
bgneal@442 200 elm = new Node('meta', 1);
bgneal@442 201 elm.attr('http-equiv', 'Content-Type');
bgneal@442 202 elm.shortEnded = true;
bgneal@442 203 addHeadNode(elm);
bgneal@442 204 }
bgneal@442 205
bgneal@442 206 elm.attr('content', 'text/html; charset=' + data.docencoding);
bgneal@442 207 }
bgneal@442 208
bgneal@442 209 // Add/update/remove meta
bgneal@442 210 each('keywords,description,author,copyright,robots'.split(','), function(name) {
bgneal@442 211 var nodes = headerFragment.getAll('meta'), i, meta, value = data['meta' + name];
bgneal@442 212
bgneal@442 213 for (i = 0; i < nodes.length; i++) {
bgneal@442 214 meta = nodes[i];
bgneal@442 215
bgneal@442 216 if (meta.attr('name') == name) {
bgneal@442 217 if (value)
bgneal@442 218 meta.attr('content', value);
bgneal@442 219 else
bgneal@442 220 meta.remove();
bgneal@442 221
bgneal@442 222 return;
bgneal@312 223 }
bgneal@312 224 }
bgneal@442 225
bgneal@442 226 if (value) {
bgneal@442 227 elm = new Node('meta', 1);
bgneal@442 228 elm.attr('name', name);
bgneal@442 229 elm.attr('content', value);
bgneal@442 230 elm.shortEnded = true;
bgneal@442 231
bgneal@442 232 addHeadNode(elm);
bgneal@442 233 }
bgneal@442 234 });
bgneal@442 235
bgneal@442 236 // Add/update/delete link
bgneal@442 237 elm = headerFragment.getAll('link')[0];
bgneal@442 238 if (elm && elm.attr('rel') == 'stylesheet') {
bgneal@442 239 if (data.stylesheet)
bgneal@442 240 elm.attr('href', data.stylesheet);
bgneal@442 241 else
bgneal@442 242 elm.remove();
bgneal@442 243 } else if (data.stylesheet) {
bgneal@442 244 elm = new Node('link', 1);
bgneal@442 245 elm.attr({
bgneal@442 246 rel : 'stylesheet',
bgneal@442 247 text : 'text/css',
bgneal@442 248 href : data.stylesheet
bgneal@442 249 });
bgneal@442 250 elm.shortEnded = true;
bgneal@442 251
bgneal@442 252 addHeadNode(elm);
bgneal@312 253 }
bgneal@442 254
bgneal@442 255 // Update body attributes
bgneal@442 256 elm = headerFragment.getAll('body')[0];
bgneal@442 257 if (elm) {
bgneal@442 258 setAttr(elm, 'dir', data.langdir);
bgneal@442 259 setAttr(elm, 'style', data.style);
bgneal@442 260 setAttr(elm, 'vlink', data.visited_color);
bgneal@442 261 setAttr(elm, 'link', data.link_color);
bgneal@442 262 setAttr(elm, 'alink', data.active_color);
bgneal@442 263
bgneal@442 264 // Update iframe body as well
bgneal@442 265 dom.setAttribs(this.editor.getBody(), {
bgneal@442 266 style : data.style,
bgneal@442 267 dir : data.dir,
bgneal@442 268 vLink : data.visited_color,
bgneal@442 269 link : data.link_color,
bgneal@442 270 aLink : data.active_color
bgneal@442 271 });
bgneal@442 272 }
bgneal@442 273
bgneal@442 274 // Set html attributes
bgneal@442 275 elm = headerFragment.getAll('html')[0];
bgneal@442 276 if (elm) {
bgneal@442 277 setAttr(elm, 'lang', data.langcode);
bgneal@442 278 setAttr(elm, 'xml:lang', data.langcode);
bgneal@442 279 }
bgneal@442 280
bgneal@442 281 // Serialize header fragment and crop away body part
bgneal@442 282 html = new tinymce.html.Serializer({
bgneal@442 283 validate: false,
bgneal@442 284 indent: true,
bgneal@442 285 apply_source_formatting : true,
bgneal@442 286 indent_before: 'head,html,body,meta,title,script,link,style',
bgneal@442 287 indent_after: 'head,html,body,meta,title,script,link,style'
bgneal@442 288 }).serialize(headerFragment);
bgneal@442 289
bgneal@442 290 this.head = html.substring(0, html.indexOf('</body>'));
bgneal@312 291 },
bgneal@312 292
bgneal@442 293 _parseHeader : function() {
bgneal@442 294 // Parse the contents with a DOM parser
bgneal@442 295 return new tinymce.html.DomParser({
bgneal@442 296 validate: false,
bgneal@442 297 root_name: '#document'
bgneal@442 298 }).parse(this.head);
bgneal@312 299 },
bgneal@312 300
bgneal@312 301 _setContent : function(ed, o) {
bgneal@442 302 var self = this, startPos, endPos, content = o.content, headerFragment, styles = '', dom = self.editor.dom, elm;
bgneal@442 303
bgneal@442 304 function low(s) {
bgneal@442 305 return s.replace(/<\/?[A-Z]+/g, function(a) {
bgneal@442 306 return a.toLowerCase();
bgneal@442 307 })
bgneal@442 308 };
bgneal@312 309
bgneal@312 310 // Ignore raw updated if we already have a head, this will fix issues with undo/redo keeping the head/foot separate
bgneal@442 311 if (o.format == 'raw' && self.head)
bgneal@312 312 return;
bgneal@312 313
bgneal@312 314 if (o.source_view && ed.getParam('fullpage_hide_in_source_view'))
bgneal@312 315 return;
bgneal@312 316
bgneal@312 317 // Parse out head, body and footer
bgneal@442 318 content = content.replace(/<(\/?)BODY/gi, '<$1body');
bgneal@442 319 startPos = content.indexOf('<body');
bgneal@312 320
bgneal@442 321 if (startPos != -1) {
bgneal@442 322 startPos = content.indexOf('>', startPos);
bgneal@442 323 self.head = low(content.substring(0, startPos + 1));
bgneal@312 324
bgneal@442 325 endPos = content.indexOf('</body', startPos);
bgneal@442 326 if (endPos == -1)
bgneal@442 327 endPos = content.length;
bgneal@312 328
bgneal@442 329 o.content = content.substring(startPos + 1, endPos);
bgneal@442 330 self.foot = low(content.substring(endPos));
bgneal@442 331 } else {
bgneal@442 332 self.head = this._getDefaultHeader();
bgneal@442 333 self.foot = '\n</body>\n</html>';
bgneal@442 334 }
bgneal@312 335
bgneal@442 336 // Parse header and update iframe
bgneal@442 337 headerFragment = self._parseHeader();
bgneal@442 338 each(headerFragment.getAll('style'), function(node) {
bgneal@442 339 if (node.firstChild)
bgneal@442 340 styles += node.firstChild.value;
bgneal@442 341 });
bgneal@312 342
bgneal@442 343 elm = headerFragment.getAll('body')[0];
bgneal@442 344 if (elm) {
bgneal@442 345 dom.setAttribs(self.editor.getBody(), {
bgneal@442 346 style : elm.attr('style') || '',
bgneal@442 347 dir : elm.attr('dir') || '',
bgneal@442 348 vLink : elm.attr('vlink') || '',
bgneal@442 349 link : elm.attr('link') || '',
bgneal@442 350 aLink : elm.attr('alink') || ''
bgneal@442 351 });
bgneal@442 352 }
bgneal@312 353
bgneal@442 354 if (styles)
bgneal@442 355 dom.add(self.editor.getDoc().getElementsByTagName('head')[0], 'style', {id : 'fullpage_styles'}, styles);
bgneal@442 356 else
bgneal@442 357 dom.remove('fullpage_styles');
bgneal@442 358 },
bgneal@312 359
bgneal@442 360 _getDefaultHeader : function() {
bgneal@442 361 var header = '', editor = this.editor, value, styles = '';
bgneal@312 362
bgneal@442 363 if (editor.getParam('fullpage_default_xml_pi'))
bgneal@442 364 header += '<?xml version="1.0" encoding="' + editor.getParam('fullpage_default_encoding', 'ISO-8859-1') + '" ?>\n';
bgneal@312 365
bgneal@442 366 header += editor.getParam('fullpage_default_doctype', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">');
bgneal@442 367 header += '\n<html>\n<head>\n';
bgneal@312 368
bgneal@442 369 if (value = editor.getParam('fullpage_default_title'))
bgneal@442 370 header += '<title>' + v + '</title>\n';
bgneal@312 371
bgneal@442 372 if (value = editor.getParam('fullpage_default_encoding'))
bgneal@442 373 header += '<meta http-equiv="Content-Type" content="text/html; charset=' + value + '" />\n';
bgneal@442 374
bgneal@442 375 if (value = editor.getParam('fullpage_default_font_family'))
bgneal@442 376 styles += 'font-family: ' + value + ';';
bgneal@442 377
bgneal@442 378 if (value = editor.getParam('fullpage_default_font_size'))
bgneal@442 379 styles += 'font-size: ' + value + ';';
bgneal@442 380
bgneal@442 381 if (value = editor.getParam('fullpage_default_text_color'))
bgneal@442 382 styles += 'color: ' + value + ';';
bgneal@442 383
bgneal@442 384 header += '</head>\n<body' + (styles ? ' style="' + styles + '"' : '') + '>\n';
bgneal@442 385
bgneal@442 386 return header;
bgneal@312 387 },
bgneal@312 388
bgneal@312 389 _getContent : function(ed, o) {
bgneal@442 390 var self = this;
bgneal@312 391
bgneal@312 392 if (!o.source_view || !ed.getParam('fullpage_hide_in_source_view'))
bgneal@442 393 o.content = tinymce.trim(self.head) + '\n' + tinymce.trim(o.content) + '\n' + tinymce.trim(self.foot);
bgneal@312 394 }
bgneal@312 395 });
bgneal@312 396
bgneal@312 397 // Register plugin
bgneal@312 398 tinymce.PluginManager.add('fullpage', tinymce.plugins.FullPagePlugin);
bgneal@442 399 })();