1 | /**
|
---|
2 | * editor_plugin_src.js
|
---|
3 | *
|
---|
4 | * Copyright 2009, Moxiecode Systems AB
|
---|
5 | * Released under LGPL License.
|
---|
6 | *
|
---|
7 | * License: http://tinymce.moxiecode.com/license
|
---|
8 | * Contributing: http://tinymce.moxiecode.com/contributing
|
---|
9 | */
|
---|
10 |
|
---|
11 | (function() {
|
---|
12 | var each = tinymce.each, Node = tinymce.html.Node;
|
---|
13 |
|
---|
14 | tinymce.create('tinymce.plugins.FullPagePlugin', {
|
---|
15 | init : function(ed, url) {
|
---|
16 | var t = this;
|
---|
17 |
|
---|
18 | t.editor = ed;
|
---|
19 |
|
---|
20 | // Register commands
|
---|
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)),
|
---|
26 | inline : 1
|
---|
27 | }, {
|
---|
28 | plugin_url : url,
|
---|
29 | data : t._htmlToData()
|
---|
30 | });
|
---|
31 | });
|
---|
32 |
|
---|
33 | // Register buttons
|
---|
34 | ed.addButton('fullpage', {title : 'fullpage.desc', cmd : 'mceFullPageProperties'});
|
---|
35 |
|
---|
36 | ed.onBeforeSetContent.add(t._setContent, t);
|
---|
37 | ed.onGetContent.add(t._getContent, t);
|
---|
38 | },
|
---|
39 |
|
---|
40 | getInfo : function() {
|
---|
41 | return {
|
---|
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
|
---|
47 | };
|
---|
48 | },
|
---|
49 |
|
---|
50 | // Private plugin internal methods
|
---|
51 |
|
---|
52 | _htmlToData : function() {
|
---|
53 | var headerFragment = this._parseHeader(), data = {}, nodes, elm, matches, editor = this.editor;
|
---|
54 |
|
---|
55 | function getAttr(elm, name) {
|
---|
56 | var value = elm.attr(name);
|
---|
57 |
|
---|
58 | return value || '';
|
---|
59 | };
|
---|
60 |
|
---|
61 | // Default some values
|
---|
62 | data.fontface = editor.getParam("fullpage_default_fontface", "");
|
---|
63 | data.fontsize = editor.getParam("fullpage_default_fontsize", "");
|
---|
64 |
|
---|
65 | // Parse XML PI
|
---|
66 | elm = headerFragment.firstChild;
|
---|
67 | if (elm.type == 7) {
|
---|
68 | data.xml_pi = true;
|
---|
69 | matches = /encoding="([^"]+)"/.exec(elm.value);
|
---|
70 | if (matches)
|
---|
71 | data.docencoding = matches[1];
|
---|
72 | }
|
---|
73 |
|
---|
74 | // Parse doctype
|
---|
75 | elm = headerFragment.getAll('#doctype')[0];
|
---|
76 | if (elm)
|
---|
77 | data.doctype = '<!DOCTYPE' + elm.value + ">";
|
---|
78 |
|
---|
79 | // Parse title element
|
---|
80 | elm = headerFragment.getAll('title')[0];
|
---|
81 | if (elm && elm.firstChild) {
|
---|
82 | data.metatitle = elm.firstChild.value;
|
---|
83 | }
|
---|
84 |
|
---|
85 | // Parse meta elements
|
---|
86 | each(headerFragment.getAll('meta'), function(meta) {
|
---|
87 | var name = meta.attr('name'), httpEquiv = meta.attr('http-equiv'), matches;
|
---|
88 |
|
---|
89 | if (name)
|
---|
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'));
|
---|
93 |
|
---|
94 | if (matches)
|
---|
95 | data.docencoding = matches[1];
|
---|
96 | }
|
---|
97 | });
|
---|
98 |
|
---|
99 | // Parse html attribs
|
---|
100 | elm = headerFragment.getAll('html')[0];
|
---|
101 | if (elm)
|
---|
102 | data.langcode = getAttr(elm, 'lang') || getAttr(elm, 'xml:lang');
|
---|
103 |
|
---|
104 | // Parse stylesheet
|
---|
105 | elm = headerFragment.getAll('link')[0];
|
---|
106 | if (elm && elm.attr('rel') == 'stylesheet')
|
---|
107 | data.stylesheet = elm.attr('href');
|
---|
108 |
|
---|
109 | // Parse body parts
|
---|
110 | elm = headerFragment.getAll('body')[0];
|
---|
111 | if (elm) {
|
---|
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');
|
---|
117 | }
|
---|
118 |
|
---|
119 | return data;
|
---|
120 | },
|
---|
121 |
|
---|
122 | _dataToHtml : function(data) {
|
---|
123 | var headerFragment, headElement, html, elm, value, dom = this.editor.dom;
|
---|
124 |
|
---|
125 | function setAttr(elm, name, value) {
|
---|
126 | elm.attr(name, value ? value : undefined);
|
---|
127 | };
|
---|
128 |
|
---|
129 | function addHeadNode(node) {
|
---|
130 | if (headElement.firstChild)
|
---|
131 | headElement.insert(node, headElement.firstChild);
|
---|
132 | else
|
---|
133 | headElement.append(node);
|
---|
134 | };
|
---|
135 |
|
---|
136 | headerFragment = this._parseHeader();
|
---|
137 | headElement = headerFragment.getAll('head')[0];
|
---|
138 | if (!headElement) {
|
---|
139 | elm = headerFragment.getAll('html')[0];
|
---|
140 | headElement = new Node('head', 1);
|
---|
141 |
|
---|
142 | if (elm.firstChild)
|
---|
143 | elm.insert(headElement, elm.firstChild, true);
|
---|
144 | else
|
---|
145 | elm.append(headElement);
|
---|
146 | }
|
---|
147 |
|
---|
148 | // Add/update/remove XML-PI
|
---|
149 | elm = headerFragment.firstChild;
|
---|
150 | if (data.xml_pi) {
|
---|
151 | value = 'version="1.0"';
|
---|
152 |
|
---|
153 | if (data.docencoding)
|
---|
154 | value += ' encoding="' + data.docencoding + '"';
|
---|
155 |
|
---|
156 | if (elm.type != 7) {
|
---|
157 | elm = new Node('xml', 7);
|
---|
158 | headerFragment.insert(elm, headerFragment.firstChild, true);
|
---|
159 | }
|
---|
160 |
|
---|
161 | elm.value = value;
|
---|
162 | } else if (elm && elm.type == 7)
|
---|
163 | elm.remove();
|
---|
164 |
|
---|
165 | // Add/update/remove doctype
|
---|
166 | elm = headerFragment.getAll('#doctype')[0];
|
---|
167 | if (data.doctype) {
|
---|
168 | if (!elm) {
|
---|
169 | elm = new Node('#doctype', 10);
|
---|
170 |
|
---|
171 | if (data.xml_pi)
|
---|
172 | headerFragment.insert(elm, headerFragment.firstChild);
|
---|
173 | else
|
---|
174 | addHeadNode(elm);
|
---|
175 | }
|
---|
176 |
|
---|
177 | elm.value = data.doctype.substring(9, data.doctype.length - 1);
|
---|
178 | } else if (elm)
|
---|
179 | elm.remove();
|
---|
180 |
|
---|
181 | // Add/update/remove title
|
---|
182 | elm = headerFragment.getAll('title')[0];
|
---|
183 | if (data.metatitle) {
|
---|
184 | if (!elm) {
|
---|
185 | elm = new Node('title', 1);
|
---|
186 | elm.append(new Node('#text', 3)).value = data.metatitle;
|
---|
187 | addHeadNode(elm);
|
---|
188 | }
|
---|
189 | }
|
---|
190 |
|
---|
191 | // Add meta encoding
|
---|
192 | if (data.docencoding) {
|
---|
193 | elm = null;
|
---|
194 | each(headerFragment.getAll('meta'), function(meta) {
|
---|
195 | if (meta.attr('http-equiv') == 'Content-Type')
|
---|
196 | elm = meta;
|
---|
197 | });
|
---|
198 |
|
---|
199 | if (!elm) {
|
---|
200 | elm = new Node('meta', 1);
|
---|
201 | elm.attr('http-equiv', 'Content-Type');
|
---|
202 | elm.shortEnded = true;
|
---|
203 | addHeadNode(elm);
|
---|
204 | }
|
---|
205 |
|
---|
206 | elm.attr('content', 'text/html; charset=' + data.docencoding);
|
---|
207 | }
|
---|
208 |
|
---|
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];
|
---|
212 |
|
---|
213 | for (i = 0; i < nodes.length; i++) {
|
---|
214 | meta = nodes[i];
|
---|
215 |
|
---|
216 | if (meta.attr('name') == name) {
|
---|
217 | if (value)
|
---|
218 | meta.attr('content', value);
|
---|
219 | else
|
---|
220 | meta.remove();
|
---|
221 |
|
---|
222 | return;
|
---|
223 | }
|
---|
224 | }
|
---|
225 |
|
---|
226 | if (value) {
|
---|
227 | elm = new Node('meta', 1);
|
---|
228 | elm.attr('name', name);
|
---|
229 | elm.attr('content', value);
|
---|
230 | elm.shortEnded = true;
|
---|
231 |
|
---|
232 | addHeadNode(elm);
|
---|
233 | }
|
---|
234 | });
|
---|
235 |
|
---|
236 | // Add/update/delete link
|
---|
237 | elm = headerFragment.getAll('link')[0];
|
---|
238 | if (elm && elm.attr('rel') == 'stylesheet') {
|
---|
239 | if (data.stylesheet)
|
---|
240 | elm.attr('href', data.stylesheet);
|
---|
241 | else
|
---|
242 | elm.remove();
|
---|
243 | } else if (data.stylesheet) {
|
---|
244 | elm = new Node('link', 1);
|
---|
245 | elm.attr({
|
---|
246 | rel : 'stylesheet',
|
---|
247 | text : 'text/css',
|
---|
248 | href : data.stylesheet
|
---|
249 | });
|
---|
250 | elm.shortEnded = true;
|
---|
251 |
|
---|
252 | addHeadNode(elm);
|
---|
253 | }
|
---|
254 |
|
---|
255 | // Update body attributes
|
---|
256 | elm = headerFragment.getAll('body')[0];
|
---|
257 | if (elm) {
|
---|
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);
|
---|
263 |
|
---|
264 | // Update iframe body as well
|
---|
265 | dom.setAttribs(this.editor.getBody(), {
|
---|
266 | style : data.style,
|
---|
267 | dir : data.dir,
|
---|
268 | vLink : data.visited_color,
|
---|
269 | link : data.link_color,
|
---|
270 | aLink : data.active_color
|
---|
271 | });
|
---|
272 | }
|
---|
273 |
|
---|
274 | // Set html attributes
|
---|
275 | elm = headerFragment.getAll('html')[0];
|
---|
276 | if (elm) {
|
---|
277 | setAttr(elm, 'lang', data.langcode);
|
---|
278 | setAttr(elm, 'xml:lang', data.langcode);
|
---|
279 | }
|
---|
280 |
|
---|
281 | // Serialize header fragment and crop away body part
|
---|
282 | html = new tinymce.html.Serializer({
|
---|
283 | validate: false,
|
---|
284 | indent: true,
|
---|
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);
|
---|
289 |
|
---|
290 | this.head = html.substring(0, html.indexOf('</body>'));
|
---|
291 | },
|
---|
292 |
|
---|
293 | _parseHeader : function() {
|
---|
294 | // Parse the contents with a DOM parser
|
---|
295 | return new tinymce.html.DomParser({
|
---|
296 | validate: false,
|
---|
297 | root_name: '#document'
|
---|
298 | }).parse(this.head);
|
---|
299 | },
|
---|
300 |
|
---|
301 | _setContent : function(ed, o) {
|
---|
302 | var self = this, startPos, endPos, content = o.content, headerFragment, styles = '', dom = self.editor.dom, elm;
|
---|
303 |
|
---|
304 | function low(s) {
|
---|
305 | return s.replace(/<\/?[A-Z]+/g, function(a) {
|
---|
306 | return a.toLowerCase();
|
---|
307 | })
|
---|
308 | };
|
---|
309 |
|
---|
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)
|
---|
312 | return;
|
---|
313 |
|
---|
314 | if (o.source_view && ed.getParam('fullpage_hide_in_source_view'))
|
---|
315 | return;
|
---|
316 |
|
---|
317 | // Parse out head, body and footer
|
---|
318 | content = content.replace(/<(\/?)BODY/gi, '<$1body');
|
---|
319 | startPos = content.indexOf('<body');
|
---|
320 |
|
---|
321 | if (startPos != -1) {
|
---|
322 | startPos = content.indexOf('>', startPos);
|
---|
323 | self.head = low(content.substring(0, startPos + 1));
|
---|
324 |
|
---|
325 | endPos = content.indexOf('</body', startPos);
|
---|
326 | if (endPos == -1)
|
---|
327 | endPos = content.length;
|
---|
328 |
|
---|
329 | o.content = content.substring(startPos + 1, endPos);
|
---|
330 | self.foot = low(content.substring(endPos));
|
---|
331 | } else {
|
---|
332 | self.head = this._getDefaultHeader();
|
---|
333 | self.foot = '\n</body>\n</html>';
|
---|
334 | }
|
---|
335 |
|
---|
336 | // Parse header and update iframe
|
---|
337 | headerFragment = self._parseHeader();
|
---|
338 | each(headerFragment.getAll('style'), function(node) {
|
---|
339 | if (node.firstChild)
|
---|
340 | styles += node.firstChild.value;
|
---|
341 | });
|
---|
342 |
|
---|
343 | elm = headerFragment.getAll('body')[0];
|
---|
344 | if (elm) {
|
---|
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') || ''
|
---|
351 | });
|
---|
352 | }
|
---|
353 |
|
---|
354 | if (styles)
|
---|
355 | dom.add(self.editor.getDoc().getElementsByTagName('head')[0], 'style', {id : 'fullpage_styles'}, styles);
|
---|
356 | else
|
---|
357 | dom.remove('fullpage_styles');
|
---|
358 | },
|
---|
359 |
|
---|
360 | _getDefaultHeader : function() {
|
---|
361 | var header = '', editor = this.editor, value, styles = '';
|
---|
362 |
|
---|
363 | if (editor.getParam('fullpage_default_xml_pi'))
|
---|
364 | header += '<?xml version="1.0" encoding="' + editor.getParam('fullpage_default_encoding', 'ISO-8859-1') + '" ?>\n';
|
---|
365 |
|
---|
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';
|
---|
368 |
|
---|
369 | if (value = editor.getParam('fullpage_default_title'))
|
---|
370 | header += '<title>' + v + '</title>\n';
|
---|
371 |
|
---|
372 | if (value = editor.getParam('fullpage_default_encoding'))
|
---|
373 | header += '<meta http-equiv="Content-Type" content="text/html; charset=' + value + '" />\n';
|
---|
374 |
|
---|
375 | if (value = editor.getParam('fullpage_default_font_family'))
|
---|
376 | styles += 'font-family: ' + value + ';';
|
---|
377 |
|
---|
378 | if (value = editor.getParam('fullpage_default_font_size'))
|
---|
379 | styles += 'font-size: ' + value + ';';
|
---|
380 |
|
---|
381 | if (value = editor.getParam('fullpage_default_text_color'))
|
---|
382 | styles += 'color: ' + value + ';';
|
---|
383 |
|
---|
384 | header += '</head>\n<body' + (styles ? ' style="' + styles + '"' : '') + '>\n';
|
---|
385 |
|
---|
386 | return header;
|
---|
387 | },
|
---|
388 |
|
---|
389 | _getContent : function(ed, o) {
|
---|
390 | var self = this;
|
---|
391 |
|
---|
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);
|
---|
394 | }
|
---|
395 | });
|
---|
396 |
|
---|
397 | // Register plugin
|
---|
398 | tinymce.PluginManager.add('fullpage', tinymce.plugins.FullPagePlugin);
|
---|
399 | })();
|
---|