blob: 69b0254cae189ffc9baa40859c2cf7803a60a992 [file] [log] [blame]
function Formatter () {}
// Video format tags supported by the [video] shortcode: https://codex.wordpress.org/Video_Shortcode
// mp4, m4v and webm prioritized since they're supported by the stock player as of Android API 23
Formatter.videoShortcodeFormats = ["mp4", "m4v", "webm", "ogv", "wmv", "flv"];
Formatter.htmlToVisual = function(html) {
var mutatedHTML = wp.loadText(html);
// Perform extra transformations to properly wrap captioned images in paragraphs
mutatedHTML = mutatedHTML.replace(/^\[caption([^\]]*\])/igm, '<p>[caption$1');
mutatedHTML = mutatedHTML.replace(/([^\n>])\[caption/igm, '$1<br />\n[caption');
mutatedHTML = mutatedHTML.replace(/\[\/caption\]\n(?=<|$)/igm, '[/caption]</p>\n');
mutatedHTML = mutatedHTML.replace(/\[\/caption\]\n(?=[^<])/igm, '[/caption]<br />\n');
return Formatter.applyVisualFormatting(mutatedHTML);
}
Formatter.convertPToDiv = function(html) {
// Replace the paragraph tags we get from wpload with divs
var mutatedHTML = html.replace(/(<p(?=[>\s]))/igm, '<div').replace(/<\/p>/igm, '</div>');
// Replace break tags around media items with paragraphs
// The break tags appear when text and media are separated by only a line break rather than a paragraph break,
// which can happen when inserting media inline and switching to HTML mode and back, or by deleting line breaks
// in HTML mode
mutatedHTML = mutatedHTML.replace(/<br \/>(?=\s*(<img|<a href|<label|<video|<span class="edit-container"))/igm,
'</div><div>');
mutatedHTML = mutatedHTML.replace(/(<img [^<>]*>|<\/a>|<\/label>|<\/video>|<\/span>)<br \/>/igm,
function replaceBrWithDivs(match) { return match.substr(0, match.length - 6) + '</div><div>'; });
// Append paragraph-wrapped break tag under media at the end of a post
mutatedHTML = mutatedHTML.replace(/(<img [^<>]*>|<\/a>|<\/label>|<\/video>|<\/span>)[^<>]*<\/div>\s$/igm,
function replaceBrWithDivs(match) { return match + '<div><br></div>'; });
return mutatedHTML;
}
Formatter.visualToHtml = function(html) {
return wp.saveText(html);
return Formatter.removeVisualFormatting(mutatedHTML);
}
Formatter.convertDivToP = function(html) {
return html.replace(/(<div(?=[>\s]))/igm, '<p').replace(/<\/div>/igm, '</p>');
}
/**
* @brief Applies editor specific visual formatting.
*
* @param html The markup to format
*
* @return Returns the string with the visual formatting applied.
*/
Formatter.applyVisualFormatting = function(html) {
var str = wp.shortcode.replace('caption', html, Formatter.applyCaptionFormatting);
str = wp.shortcode.replace('wpvideo', str, Formatter.applyVideoPressFormattingCallback);
str = wp.shortcode.replace('video', str, Formatter.applyVideoFormattingCallback);
// More tag
str = str.replace(/<!--more(.*?)-->/igm, "<hr class=\"more-tag\" wp-more-data=\"$1\">")
str = str.replace(/<!--nextpage-->/igm, "<hr class=\"nextpage-tag\">")
return str;
}
/**
* @brief Adds visual formatting to a caption shortcodes.
*
* @param html The markup containing caption shortcodes to process.
*
* @return The html with caption shortcodes replaced with editor specific markup.
* See shortcode.js::next or details
*/
Formatter.applyCaptionFormatting = function(match) {
var attrs = match.attrs.named;
// The empty 'onclick' is important. It prevents the cursor jumping to the end
// of the content body when `-webkit-user-select: none` is set and the caption is tapped.
var out = '<label class="wp-temp" data-wp-temp="caption" onclick="">';
out += '<span class="wp-caption"';
if (attrs.width) {
out += ' style="width:' + attrs.width + 'px; max-width:100% !important;"';
}
for (var key in attrs) {
out += " data-caption-" + key + '="' + attrs[key] + '"';
}
out += '>';
out += match.content;
out += '</span>';
out += '</label>';
return out;
}
Formatter.applyVideoPressFormattingCallback = function(match) {
if (match.attrs.numeric.length == 0) {
return match.content;
}
var videopressID = match.attrs.numeric[0];
var posterSVG = '"svg/wpposter.svg"';
// The empty 'onclick' is important. It prevents the cursor jumping to the end
// of the content body when `-webkit-user-select: none` is set and the video is tapped.
var out = '<video data-wpvideopress="' + videopressID + '" webkit-playsinline src="" preload="metadata" poster='
+ posterSVG +' onclick="" onerror="ZSSEditor.sendVideoPressInfoRequest(\'' + videopressID +'\');"></video>';
// Wrap video in edit-container node for a permanent delete button overlay
var containerStart = '<span class="edit-container" contenteditable="false"><span class="delete-overlay"></span>';
out = containerStart + out + '</span>';
return out;
}
Formatter.applyVideoFormattingCallback = function(match) {
// Find the tag containing the video source
var srcTag = "";
if (match.attrs.named['src']) {
srcTag = "src";
} else {
for (var i = 0; i < Formatter.videoShortcodeFormats.length; i++) {
var format = Formatter.videoShortcodeFormats[i];
if (match.attrs.named[format]) {
srcTag = format;
break;
}
}
}
if (srcTag.length == 0) {
return match.content;
}
var out = '<video webkit-playsinline src="' + match.attrs.named[srcTag] + '"';
// Preserve all existing tags
for (var item in match.attrs.named) {
if (item != srcTag) {
out += ' ' + item + '="' + match.attrs.named[item] + '"';
}
}
if (!match.attrs.named['preload']) {
out += ' preload="metadata"';
}
out += ' onclick="" controls="controls"></video>';
// Wrap video in edit-container node for a permanent delete button overlay
var containerStart = '<span class="edit-container" contenteditable="false"><span class="delete-overlay"></span>';
out = containerStart + out + '</span>';
return out;
}
if (typeof module !== 'undefined' && module.exports != null) {
exports.Formatter = Formatter;
}