4 * Copyright 2009, Moxiecode Systems AB
5 * Released under LGPL License.
7 * License: http://tinymce.moxiecode.com/license
8 * Contributing: http://tinymce.moxiecode.com/contributing
12 var each = tinymce.each, Node = tinymce.html.Node;
14 tinymce.create('tinymce.plugins.FullPagePlugin', {
15 init : function(ed, url) {
21 ed.addCommand('mceFullPageProperties', function() {
22 ed.windowManager.open({
23 file : url + '/fullpage.htm',
24 width : 430 + parseInt(ed.getLang('fullpage.delta_width', 0)),
25 height : 495 + parseInt(ed.getLang('fullpage.delta_height', 0)),
29 data : t._htmlToData()
34 ed.addButton('fullpage', {title : 'fullpage.desc', cmd : 'mceFullPageProperties'});
36 ed.onBeforeSetContent.add(t._setContent, t);
37 ed.onGetContent.add(t._getContent, t);
40 getInfo : function() {
42 longname : 'Fullpage',
43 author : 'Moxiecode Systems AB',
44 authorurl : 'http://tinymce.moxiecode.com',
45 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullpage',
46 version : tinymce.majorVersion + "." + tinymce.minorVersion
50 // Private plugin internal methods
52 _htmlToData : function() {
53 var headerFragment = this._parseHeader(), data = {}, nodes, elm, matches, editor = this.editor;
55 function getAttr(elm, name) {
56 var value = elm.attr(name);
61 // Default some values
62 data.fontface = editor.getParam("fullpage_default_fontface", "");
63 data.fontsize = editor.getParam("fullpage_default_fontsize", "");
66 elm = headerFragment.firstChild;
69 matches = /encoding="([^"]+)"/.exec(elm.value);
71 data.docencoding = matches[1];
75 elm = headerFragment.getAll('#doctype')[0];
77 data.doctype = '<!DOCTYPE' + elm.value + ">";
79 // Parse title element
80 elm = headerFragment.getAll('title')[0];
81 if (elm && elm.firstChild) {
82 data.metatitle = elm.firstChild.value;
85 // Parse meta elements
86 each(headerFragment.getAll('meta'), function(meta) {
87 var name = meta.attr('name'), httpEquiv = meta.attr('http-equiv'), matches;
90 data['meta' + name.toLowerCase()] = meta.attr('content');
91 else if (httpEquiv == "Content-Type") {
92 matches = /charset\s*=\s*(.*)\s*/gi.exec(meta.attr('content'));
95 data.docencoding = matches[1];
100 elm = headerFragment.getAll('html')[0];
102 data.langcode = getAttr(elm, 'lang') || getAttr(elm, 'xml:lang');
105 elm = headerFragment.getAll('link')[0];
106 if (elm && elm.attr('rel') == 'stylesheet')
107 data.stylesheet = elm.attr('href');
110 elm = headerFragment.getAll('body')[0];
112 data.langdir = getAttr(elm, 'dir');
113 data.style = getAttr(elm, 'style');
114 data.visited_color = getAttr(elm, 'vlink');
115 data.link_color = getAttr(elm, 'link');
116 data.active_color = getAttr(elm, 'alink');
122 _dataToHtml : function(data) {
123 var headerFragment, headElement, html, elm, value, dom = this.editor.dom;
125 function setAttr(elm, name, value) {
126 elm.attr(name, value ? value : undefined);
129 function addHeadNode(node) {
130 if (headElement.firstChild)
131 headElement.insert(node, headElement.firstChild);
133 headElement.append(node);
136 headerFragment = this._parseHeader();
137 headElement = headerFragment.getAll('head')[0];
139 elm = headerFragment.getAll('html')[0];
140 headElement = new Node('head', 1);
143 elm.insert(headElement, elm.firstChild, true);
145 elm.append(headElement);
148 // Add/update/remove XML-PI
149 elm = headerFragment.firstChild;
151 value = 'version="1.0"';
153 if (data.docencoding)
154 value += ' encoding="' + data.docencoding + '"';
157 elm = new Node('xml', 7);
158 headerFragment.insert(elm, headerFragment.firstChild, true);
162 } else if (elm && elm.type == 7)
165 // Add/update/remove doctype
166 elm = headerFragment.getAll('#doctype')[0];
169 elm = new Node('#doctype', 10);
172 headerFragment.insert(elm, headerFragment.firstChild);
177 elm.value = data.doctype.substring(9, data.doctype.length - 1);
181 // Add/update/remove title
182 elm = headerFragment.getAll('title')[0];
183 if (data.metatitle) {
185 elm = new Node('title', 1);
186 elm.append(new Node('#text', 3)).value = data.metatitle;
192 if (data.docencoding) {
194 each(headerFragment.getAll('meta'), function(meta) {
195 if (meta.attr('http-equiv') == 'Content-Type')
200 elm = new Node('meta', 1);
201 elm.attr('http-equiv', 'Content-Type');
202 elm.shortEnded = true;
206 elm.attr('content', 'text/html; charset=' + data.docencoding);
209 // Add/update/remove meta
210 each('keywords,description,author,copyright,robots'.split(','), function(name) {
211 var nodes = headerFragment.getAll('meta'), i, meta, value = data['meta' + name];
213 for (i = 0; i < nodes.length; i++) {
216 if (meta.attr('name') == name) {
218 meta.attr('content', value);
227 elm = new Node('meta', 1);
228 elm.attr('name', name);
229 elm.attr('content', value);
230 elm.shortEnded = true;
236 // Add/update/delete link
237 elm = headerFragment.getAll('link')[0];
238 if (elm && elm.attr('rel') == 'stylesheet') {
240 elm.attr('href', data.stylesheet);
243 } else if (data.stylesheet) {
244 elm = new Node('link', 1);
248 href : data.stylesheet
250 elm.shortEnded = true;
255 // Update body attributes
256 elm = headerFragment.getAll('body')[0];
258 setAttr(elm, 'dir', data.langdir);
259 setAttr(elm, 'style', data.style);
260 setAttr(elm, 'vlink', data.visited_color);
261 setAttr(elm, 'link', data.link_color);
262 setAttr(elm, 'alink', data.active_color);
264 // Update iframe body as well
265 dom.setAttribs(this.editor.getBody(), {
268 vLink : data.visited_color,
269 link : data.link_color,
270 aLink : data.active_color
274 // Set html attributes
275 elm = headerFragment.getAll('html')[0];
277 setAttr(elm, 'lang', data.langcode);
278 setAttr(elm, 'xml:lang', data.langcode);
281 // Serialize header fragment and crop away body part
282 html = new tinymce.html.Serializer({
285 apply_source_formatting : true,
286 indent_before: 'head,html,body,meta,title,script,link,style',
287 indent_after: 'head,html,body,meta,title,script,link,style'
288 }).serialize(headerFragment);
290 this.head = html.substring(0, html.indexOf('</body>'));
293 _parseHeader : function() {
294 // Parse the contents with a DOM parser
295 return new tinymce.html.DomParser({
297 root_name: '#document'
301 _setContent : function(ed, o) {
302 var self = this, startPos, endPos, content = o.content, headerFragment, styles = '', dom = self.editor.dom, elm;
305 return s.replace(/<\/?[A-Z]+/g, function(a) {
306 return a.toLowerCase();
310 // Ignore raw updated if we already have a head, this will fix issues with undo/redo keeping the head/foot separate
311 if (o.format == 'raw' && self.head)
314 if (o.source_view && ed.getParam('fullpage_hide_in_source_view'))
317 // Parse out head, body and footer
318 content = content.replace(/<(\/?)BODY/gi, '<$1body');
319 startPos = content.indexOf('<body');
321 if (startPos != -1) {
322 startPos = content.indexOf('>', startPos);
323 self.head = low(content.substring(0, startPos + 1));
325 endPos = content.indexOf('</body', startPos);
327 endPos = content.length;
329 o.content = content.substring(startPos + 1, endPos);
330 self.foot = low(content.substring(endPos));
332 self.head = this._getDefaultHeader();
333 self.foot = '\n</body>\n</html>';
336 // Parse header and update iframe
337 headerFragment = self._parseHeader();
338 each(headerFragment.getAll('style'), function(node) {
340 styles += node.firstChild.value;
343 elm = headerFragment.getAll('body')[0];
345 dom.setAttribs(self.editor.getBody(), {
346 style : elm.attr('style') || '',
347 dir : elm.attr('dir') || '',
348 vLink : elm.attr('vlink') || '',
349 link : elm.attr('link') || '',
350 aLink : elm.attr('alink') || ''
355 dom.add(self.editor.getDoc().getElementsByTagName('head')[0], 'style', {id : 'fullpage_styles'}, styles);
357 dom.remove('fullpage_styles');
360 _getDefaultHeader : function() {
361 var header = '', editor = this.editor, value, styles = '';
363 if (editor.getParam('fullpage_default_xml_pi'))
364 header += '<?xml version="1.0" encoding="' + editor.getParam('fullpage_default_encoding', 'ISO-8859-1') + '" ?>\n';
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">');
367 header += '\n<html>\n<head>\n';
369 if (value = editor.getParam('fullpage_default_title'))
370 header += '<title>' + v + '</title>\n';
372 if (value = editor.getParam('fullpage_default_encoding'))
373 header += '<meta http-equiv="Content-Type" content="text/html; charset=' + value + '" />\n';
375 if (value = editor.getParam('fullpage_default_font_family'))
376 styles += 'font-family: ' + value + ';';
378 if (value = editor.getParam('fullpage_default_font_size'))
379 styles += 'font-size: ' + value + ';';
381 if (value = editor.getParam('fullpage_default_text_color'))
382 styles += 'color: ' + value + ';';
384 header += '</head>\n<body' + (styles ? ' style="' + styles + '"' : '') + '>\n';
389 _getContent : function(ed, o) {
392 if (!o.source_view || !ed.getParam('fullpage_hide_in_source_view'))
393 o.content = tinymce.trim(self.head) + '\n' + tinymce.trim(o.content) + '\n' + tinymce.trim(self.foot);
398 tinymce.PluginManager.add('fullpage', tinymce.plugins.FullPagePlugin);