UNPKG

alpaca

Version:

Alpaca provides the easiest and fastest way to generate interactive forms for the web and mobile devices. It runs simply as HTML5 or more elaborately using Bootstrap, jQuery Mobile or jQuery UI. Alpaca uses Handlebars to process JSON schema and provide

491 lines (402 loc) 12.2 kB
/** * plugin.js * * Copyright, Moxiecode Systems AB * Released under LGPL License. * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ /*global tinymce:true */ tinymce.PluginManager.add('fullpage', function(editor) { var each = tinymce.each, Node = tinymce.html.Node; var head, foot; function showDialog() { var data = htmlToData(); editor.windowManager.open({ title: 'Document properties', data: data, defaults: {type: 'textbox', size: 40}, body: [ {name: 'title', label: 'Title'}, {name: 'keywords', label: 'Keywords'}, {name: 'description', label: 'Description'}, {name: 'robots', label: 'Robots'}, {name: 'author', label: 'Author'}, {name: 'docencoding', label: 'Encoding'} ], onSubmit: function(e) { dataToHtml(tinymce.extend(data, e.data)); } }); } function htmlToData() { var headerFragment = parseHeader(), data = {}, elm, matches; function getAttr(elm, name) { var value = elm.attr(name); return value || ''; } // Default some values data.fontface = editor.getParam("fullpage_default_fontface", ""); data.fontsize = editor.getParam("fullpage_default_fontsize", ""); // Parse XML PI elm = headerFragment.firstChild; if (elm.type == 7) { data.xml_pi = true; matches = /encoding="([^"]+)"/.exec(elm.value); if (matches) { data.docencoding = matches[1]; } } // Parse doctype elm = headerFragment.getAll('#doctype')[0]; if (elm) { data.doctype = '<!DOCTYPE' + elm.value + ">"; } // Parse title element elm = headerFragment.getAll('title')[0]; if (elm && elm.firstChild) { data.title = elm.firstChild.value; } // Parse meta elements each(headerFragment.getAll('meta'), function(meta) { var name = meta.attr('name'), httpEquiv = meta.attr('http-equiv'), matches; if (name) { data[name.toLowerCase()] = meta.attr('content'); } else if (httpEquiv == "Content-Type") { matches = /charset\s*=\s*(.*)\s*/gi.exec(meta.attr('content')); if (matches) { data.docencoding = matches[1]; } } }); // Parse html attribs elm = headerFragment.getAll('html')[0]; if (elm) { data.langcode = getAttr(elm, 'lang') || getAttr(elm, 'xml:lang'); } // Parse stylesheets data.stylesheets = []; tinymce.each(headerFragment.getAll('link'), function(link) { if (link.attr('rel') == 'stylesheet') { data.stylesheets.push(link.attr('href')); } }); // Parse body parts elm = headerFragment.getAll('body')[0]; if (elm) { data.langdir = getAttr(elm, 'dir'); data.style = getAttr(elm, 'style'); data.visited_color = getAttr(elm, 'vlink'); data.link_color = getAttr(elm, 'link'); data.active_color = getAttr(elm, 'alink'); } return data; } function dataToHtml(data) { var headerFragment, headElement, html, elm, value, dom = editor.dom; function setAttr(elm, name, value) { elm.attr(name, value ? value : undefined); } function addHeadNode(node) { if (headElement.firstChild) { headElement.insert(node, headElement.firstChild); } else { headElement.append(node); } } headerFragment = parseHeader(); headElement = headerFragment.getAll('head')[0]; if (!headElement) { elm = headerFragment.getAll('html')[0]; headElement = new Node('head', 1); if (elm.firstChild) { elm.insert(headElement, elm.firstChild, true); } else { elm.append(headElement); } } // Add/update/remove XML-PI elm = headerFragment.firstChild; if (data.xml_pi) { value = 'version="1.0"'; if (data.docencoding) { value += ' encoding="' + data.docencoding + '"'; } if (elm.type != 7) { elm = new Node('xml', 7); headerFragment.insert(elm, headerFragment.firstChild, true); } elm.value = value; } else if (elm && elm.type == 7) { elm.remove(); } // Add/update/remove doctype elm = headerFragment.getAll('#doctype')[0]; if (data.doctype) { if (!elm) { elm = new Node('#doctype', 10); if (data.xml_pi) { headerFragment.insert(elm, headerFragment.firstChild); } else { addHeadNode(elm); } } elm.value = data.doctype.substring(9, data.doctype.length - 1); } else if (elm) { elm.remove(); } // Add meta encoding elm = null; each(headerFragment.getAll('meta'), function(meta) { if (meta.attr('http-equiv') == 'Content-Type') { elm = meta; } }); if (data.docencoding) { if (!elm) { elm = new Node('meta', 1); elm.attr('http-equiv', 'Content-Type'); elm.shortEnded = true; addHeadNode(elm); } elm.attr('content', 'text/html; charset=' + data.docencoding); } else if (elm) { elm.remove(); } // Add/update/remove title elm = headerFragment.getAll('title')[0]; if (data.title) { if (!elm) { elm = new Node('title', 1); addHeadNode(elm); } else { elm.empty(); } elm.append(new Node('#text', 3)).value = data.title; } else if (elm) { elm.remove(); } // Add/update/remove meta each('keywords,description,author,copyright,robots'.split(','), function(name) { var nodes = headerFragment.getAll('meta'), i, meta, value = data[name]; for (i = 0; i < nodes.length; i++) { meta = nodes[i]; if (meta.attr('name') == name) { if (value) { meta.attr('content', value); } else { meta.remove(); } return; } } if (value) { elm = new Node('meta', 1); elm.attr('name', name); elm.attr('content', value); elm.shortEnded = true; addHeadNode(elm); } }); var currentStyleSheetsMap = {}; tinymce.each(headerFragment.getAll('link'), function(stylesheet) { if (stylesheet.attr('rel') == 'stylesheet') { currentStyleSheetsMap[stylesheet.attr('href')] = stylesheet; } }); // Add new tinymce.each(data.stylesheets, function(stylesheet) { if (!currentStyleSheetsMap[stylesheet]) { elm = new Node('link', 1); elm.attr({ rel: 'stylesheet', text: 'text/css', href: stylesheet }); elm.shortEnded = true; addHeadNode(elm); } delete currentStyleSheetsMap[stylesheet]; }); // Delete old tinymce.each(currentStyleSheetsMap, function(stylesheet) { stylesheet.remove(); }); // Update body attributes elm = headerFragment.getAll('body')[0]; if (elm) { setAttr(elm, 'dir', data.langdir); setAttr(elm, 'style', data.style); setAttr(elm, 'vlink', data.visited_color); setAttr(elm, 'link', data.link_color); setAttr(elm, 'alink', data.active_color); // Update iframe body as well dom.setAttribs(editor.getBody(), { style : data.style, dir : data.dir, vLink : data.visited_color, link : data.link_color, aLink : data.active_color }); } // Set html attributes elm = headerFragment.getAll('html')[0]; if (elm) { setAttr(elm, 'lang', data.langcode); setAttr(elm, 'xml:lang', data.langcode); } // No need for a head element if (!headElement.firstChild) { headElement.remove(); } // Serialize header fragment and crop away body part html = new tinymce.html.Serializer({ validate: false, indent: true, apply_source_formatting : true, indent_before: 'head,html,body,meta,title,script,link,style', indent_after: 'head,html,body,meta,title,script,link,style' }).serialize(headerFragment); head = html.substring(0, html.indexOf('</body>')); } function parseHeader() { // Parse the contents with a DOM parser return new tinymce.html.DomParser({ validate: false, root_name: '#document' }).parse(head); } function setContent(evt) { var startPos, endPos, content = evt.content, headerFragment, styles = '', dom = editor.dom, elm; if (evt.selection) { return; } function low(s) { return s.replace(/<\/?[A-Z]+/g, function(a) { return a.toLowerCase(); }); } // Ignore raw updated if we already have a head, this will fix issues with undo/redo keeping the head/foot separate if (evt.format == 'raw' && head) { return; } if (evt.source_view && editor.getParam('fullpage_hide_in_source_view')) { return; } // Fixed so new document/setContent('') doesn't remove existing header/footer except when it's in source code view if (content.length === 0 && !evt.source_view) { content = tinymce.trim(head) + '\n' + tinymce.trim(content) + '\n' + tinymce.trim(foot); } // Parse out head, body and footer content = content.replace(/<(\/?)BODY/gi, '<$1body'); startPos = content.indexOf('<body'); if (startPos != -1) { startPos = content.indexOf('>', startPos); head = low(content.substring(0, startPos + 1)); endPos = content.indexOf('</body', startPos); if (endPos == -1) { endPos = content.length; } evt.content = content.substring(startPos + 1, endPos); foot = low(content.substring(endPos)); } else { head = getDefaultHeader(); foot = '\n</body>\n</html>'; } // Parse header and update iframe headerFragment = parseHeader(); each(headerFragment.getAll('style'), function(node) { if (node.firstChild) { styles += node.firstChild.value; } }); elm = headerFragment.getAll('body')[0]; if (elm) { dom.setAttribs(editor.getBody(), { style: elm.attr('style') || '', dir: elm.attr('dir') || '', vLink: elm.attr('vlink') || '', link: elm.attr('link') || '', aLink: elm.attr('alink') || '' }); } dom.remove('fullpage_styles'); var headElm = editor.getDoc().getElementsByTagName('head')[0]; if (styles) { dom.add(headElm, 'style', { id : 'fullpage_styles' }, styles); // Needed for IE 6/7 elm = dom.get('fullpage_styles'); if (elm.styleSheet) { elm.styleSheet.cssText = styles; } } var currentStyleSheetsMap = {}; tinymce.each(headElm.getElementsByTagName('link'), function(stylesheet) { if (stylesheet.rel == 'stylesheet' && stylesheet.getAttribute('data-mce-fullpage')) { currentStyleSheetsMap[stylesheet.href] = stylesheet; } }); // Add new tinymce.each(headerFragment.getAll('link'), function(stylesheet) { var href = stylesheet.attr('href'); if (!currentStyleSheetsMap[href] && stylesheet.attr('rel') == 'stylesheet') { dom.add(headElm, 'link', { rel: 'stylesheet', text: 'text/css', href: href, 'data-mce-fullpage': '1' }); } delete currentStyleSheetsMap[href]; }); // Delete old tinymce.each(currentStyleSheetsMap, function(stylesheet) { stylesheet.parentNode.removeChild(stylesheet); }); } function getDefaultHeader() { var header = '', value, styles = ''; if (editor.getParam('fullpage_default_xml_pi')) { header += '<?xml version="1.0" encoding="' + editor.getParam('fullpage_default_encoding', 'ISO-8859-1') + '" ?>\n'; } header += editor.getParam('fullpage_default_doctype', '<!DOCTYPE html>'); header += '\n<html>\n<head>\n'; if ((value = editor.getParam('fullpage_default_title'))) { header += '<title>' + value + '</title>\n'; } if ((value = editor.getParam('fullpage_default_encoding'))) { header += '<meta http-equiv="Content-Type" content="text/html; charset=' + value + '" />\n'; } if ((value = editor.getParam('fullpage_default_font_family'))) { styles += 'font-family: ' + value + ';'; } if ((value = editor.getParam('fullpage_default_font_size'))) { styles += 'font-size: ' + value + ';'; } if ((value = editor.getParam('fullpage_default_text_color'))) { styles += 'color: ' + value + ';'; } header += '</head>\n<body' + (styles ? ' style="' + styles + '"' : '') + '>\n'; return header; } function getContent(evt) { if (!evt.selection && (!evt.source_view || !editor.getParam('fullpage_hide_in_source_view'))) { evt.content = tinymce.trim(head) + '\n' + tinymce.trim(evt.content) + '\n' + tinymce.trim(foot); } } editor.addCommand('mceFullPageProperties', showDialog); editor.addButton('fullpage', { title: 'Document properties', cmd : 'mceFullPageProperties' }); editor.addMenuItem('fullpage', { text: 'Document properties', cmd : 'mceFullPageProperties', context: 'file' }); editor.on('BeforeSetContent', setContent); editor.on('GetContent', getContent); });