aboutsummaryrefslogtreecommitdiff
path: root/libs/editor/WordPressEditor/src/main/assets/editor-utils-formatter.js
blob: 69b0254cae189ffc9baa40859c2cf7803a60a992 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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;
}