001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.parsers.bbcode;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.parsers.bbcode.BBCodeTranslator;
020    import com.liferay.portal.kernel.util.GetterUtil;
021    import com.liferay.portal.kernel.util.HtmlUtil;
022    import com.liferay.portal.kernel.util.IntegerWrapper;
023    import com.liferay.portal.kernel.util.StringBundler;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.kernel.util.StringUtil;
026    
027    import java.util.Collection;
028    import java.util.HashMap;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.Stack;
032    import java.util.regex.Matcher;
033    import java.util.regex.Pattern;
034    
035    /**
036     * @author Iliyan Peychev
037     */
038    public class HtmlBBCodeTranslatorImpl implements BBCodeTranslator {
039    
040            public HtmlBBCodeTranslatorImpl() {
041                    _listStyles = new HashMap<String, String>();
042    
043                    _listStyles.put("a", "list-style: lower-alpha inside;");
044                    _listStyles.put("A", "list-style: upper-alpha inside;");
045                    _listStyles.put("1", "list-style: decimal inside;");
046                    _listStyles.put("i", "list-style: lower-roman inside;");
047                    _listStyles.put("I", "list-style: upper-roman inside;");
048    
049                    _excludeNewLineTypes = new HashMap<String, Integer>();
050    
051                    _excludeNewLineTypes.put("*", BBCodeParser.TYPE_TAG_START_END);
052                    _excludeNewLineTypes.put("li", BBCodeParser.TYPE_TAG_START_END);
053                    _excludeNewLineTypes.put("table", BBCodeParser.TYPE_TAG_END);
054                    _excludeNewLineTypes.put("td", BBCodeParser.TYPE_TAG_START_END);
055                    _excludeNewLineTypes.put("th", BBCodeParser.TYPE_TAG_START_END);
056                    _excludeNewLineTypes.put("tr", BBCodeParser.TYPE_TAG_START_END);
057    
058                    _bbCodeCharacters = new HashMap<String, String>();
059    
060                    _bbCodeCharacters.put("&", "&amp;");
061                    _bbCodeCharacters.put("<", "&lt;");
062                    _bbCodeCharacters.put(">", "&gt;");
063                    _bbCodeCharacters.put("\"", "&#034;");
064                    _bbCodeCharacters.put("'", "&#039;");
065                    _bbCodeCharacters.put("/", "&#047;");
066                    _bbCodeCharacters.put("`", "&#096;");
067                    _bbCodeCharacters.put("[", "&#91;");
068                    _bbCodeCharacters.put("]", "&#93;");
069                    _bbCodeCharacters.put("(", "&#40;");
070                    _bbCodeCharacters.put(")", "&#41;");
071    
072                    for (int i = 0; i < _EMOTICONS.length; i++) {
073                            String[] emoticon = _EMOTICONS[i];
074    
075                            _emoticonDescriptions[i] = emoticon[2];
076                            _emoticonFiles[i] = emoticon[0];
077                            _emoticonSymbols[i] = emoticon[1];
078    
079                            String image = emoticon[0];
080    
081                            emoticon[0] =
082                                    "<img alt=\"emoticon\" src=\"@theme_images_path@/emoticons/" +
083                                            image + "\" >";
084                    }
085            }
086    
087            public String[] getEmoticonDescriptions() {
088                    return _emoticonDescriptions;
089            }
090    
091            public String[] getEmoticonFiles() {
092                    return _emoticonFiles;
093            }
094    
095            public String[][] getEmoticons() {
096                    return _EMOTICONS;
097            }
098    
099            public String[] getEmoticonSymbols() {
100                    return _emoticonSymbols;
101            }
102    
103            public String getHTML(String bbcode) {
104                    try {
105                            bbcode = parse(bbcode);
106                    }
107                    catch (Exception e) {
108                            _log.error("Unable to parse: " + bbcode, e);
109    
110                            bbcode = HtmlUtil.escape(bbcode);
111                    }
112    
113                    return bbcode;
114            }
115    
116            public String parse(String text) {
117                    StringBundler sb = new StringBundler();
118    
119                    List<BBCodeItem> bbCodeItems = _bbCodeParser.parse(text);
120                    Stack<String> tags = new Stack<String>();
121                    IntegerWrapper marker = new IntegerWrapper();
122    
123                    for (; marker.getValue() < bbCodeItems.size(); marker.increment()) {
124                            BBCodeItem bbCodeItem = bbCodeItems.get(marker.getValue());
125    
126                            int type = bbCodeItem.getType();
127    
128                            if (type == BBCodeParser.TYPE_DATA) {
129                                    handleData(sb, bbCodeItems, tags, marker, bbCodeItem);
130                            }
131                            else if (type == BBCodeParser.TYPE_TAG_END) {
132                                    handleTagEnd(sb, tags, bbCodeItem);
133                            }
134                            else if (type == BBCodeParser.TYPE_TAG_START) {
135                                    handleTagStart(sb, bbCodeItems, tags, marker, bbCodeItem);
136                            }
137                    }
138    
139                    return sb.toString();
140            }
141    
142            protected String escapeQuote(String quote) {
143                    StringBuilder sb = new StringBuilder();
144    
145                    int index = 0;
146    
147                    Matcher matcher = _bbCodePattern.matcher(quote);
148    
149                    Collection<String> values = _bbCodeCharacters.values();
150    
151                    while (matcher.find()) {
152                            String match = matcher.group();
153    
154                            int matchStartIndex = matcher.start();
155    
156                            int nextSemicolonIndex = quote.indexOf(
157                                    StringPool.SEMICOLON, matchStartIndex);
158    
159                            sb.append(quote.substring(index, matchStartIndex));
160    
161                            boolean entityFound = false;
162    
163                            if (nextSemicolonIndex >= 0) {
164                                    String value = quote.substring(
165                                            matchStartIndex, nextSemicolonIndex + 1);
166    
167                                    if (values.contains(value)) {
168                                            sb.append(value);
169    
170                                            index = matchStartIndex + value.length();
171    
172                                            entityFound = true;
173                                    }
174                            }
175    
176                            if (!entityFound) {
177                                    String escapedValue = _bbCodeCharacters.get(match);
178    
179                                    sb.append(escapedValue);
180    
181                                    index = matchStartIndex + match.length();
182                            }
183                    }
184    
185                    if (index < quote.length()) {
186                            sb.append(quote.substring(index, quote.length()));
187                    }
188    
189                    return sb.toString();
190            }
191    
192            protected String extractData(
193                    List<BBCodeItem> bbCodeItems, IntegerWrapper marker, String tag,
194                    int type, boolean consume) {
195    
196                    StringBundler sb = new StringBundler();
197    
198                    int index = marker.getValue() + 1;
199    
200                    BBCodeItem bbCodeItem = null;
201    
202                    do {
203                            bbCodeItem = bbCodeItems.get(index++);
204    
205                            if ((bbCodeItem.getType() & type) > 0) {
206                                    sb.append(bbCodeItem.getValue());
207                            }
208    
209                    }
210                    while ((bbCodeItem.getType() != BBCodeParser.TYPE_TAG_END) &&
211                               !tag.equals(bbCodeItem.getValue()));
212    
213                    if (consume) {
214                            marker.setValue(index - 1);
215                    }
216    
217                    return sb.toString();
218            }
219    
220            protected void handleBold(StringBundler sb, Stack<String> tags) {
221                    handleSimpleTag(sb, tags, "strong");
222            }
223    
224            protected void handleCode(
225                    StringBundler sb, List<BBCodeItem> bbCodeItems, IntegerWrapper marker) {
226    
227                    sb.append("<div class=\"code\">");
228    
229                    String code = extractData(
230                            bbCodeItems, marker, "code", BBCodeParser.TYPE_DATA, true);
231    
232                    code = HtmlUtil.escape(code);
233                    code = code.replaceAll(StringPool.TAB, StringPool.FOUR_SPACES);
234    
235                    String[] lines = code.split("\r?\n");
236    
237                    String digits = String.valueOf(lines.length + 1);
238    
239                    for (int i = 0; i < lines.length; i++) {
240                            String index = String.valueOf(i + 1);
241    
242                            sb.append("<span class=\"code-lines\">");
243    
244                            for (int j = 0; j < digits.length() - index.length(); j++) {
245                                    sb.append(StringPool.NBSP);
246                            }
247    
248                            lines[i] = StringUtil.replace(
249                                    lines[i], StringPool.THREE_SPACES, "&nbsp; &nbsp;");
250                            lines[i] = StringUtil.replace(
251                                    lines[i], StringPool.DOUBLE_SPACE, "&nbsp; ");
252    
253                            sb.append(index);
254                            sb.append("</span>");
255                            sb.append(lines[i]);
256    
257                            if (index.length() < lines.length) {
258                                    sb.append("<br />");
259                            }
260                    }
261    
262                    sb.append("</div>");
263            }
264    
265            protected void handleColor(
266                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
267    
268                    sb.append("<span style=\"color: ");
269    
270                    String color = bbCodeItem.getAttribute();
271    
272                    if (color == null) {
273                            color = "inherit";
274                    }
275                    else {
276                            Matcher matcher = _colorPattern.matcher(color);
277    
278                            if (!matcher.matches()) {
279                                    color = "inherit";
280                            }
281                    }
282    
283                    sb.append(color);
284    
285                    sb.append("\">");
286    
287                    tags.push("</span>");
288            }
289    
290            protected void handleData(
291                    StringBundler sb, List<BBCodeItem> bbCodeItems, Stack<String> tags,
292                    IntegerWrapper marker, BBCodeItem bbCodeItem) {
293    
294                    String value = HtmlUtil.escape(bbCodeItem.getValue());
295    
296                    value = handleNewLine(bbCodeItems, tags, marker, value);
297    
298                    for (int i = 0; i < _EMOTICONS.length; i++) {
299                            String[] emoticon = _EMOTICONS[i];
300    
301                            value = StringUtil.replace(value, emoticon[1], emoticon[0]);
302                    }
303    
304                    sb.append(value);
305            }
306    
307            protected void handleEmail(
308                    StringBundler sb, List<BBCodeItem> bbCodeItems, Stack<String> tags,
309                    IntegerWrapper marker, BBCodeItem bbCodeItem) {
310    
311                    sb.append("<a href=\"");
312    
313                    String href = bbCodeItem.getAttribute();
314    
315                    if (href == null) {
316                            href = extractData(
317                                    bbCodeItems, marker, "email", BBCodeParser.TYPE_DATA, false);
318                    }
319    
320                    if (!href.startsWith("mailto:")) {
321                            href = "mailto:" + href;
322                    }
323    
324                    sb.append(HtmlUtil.escapeHREF(href));
325    
326                    sb.append("\">");
327    
328                    tags.push("</a>");
329            }
330    
331            protected void handleFontFamily(
332                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
333    
334                    sb.append("<span style=\"font-family: ");
335                    sb.append(HtmlUtil.escapeAttribute(bbCodeItem.getAttribute()));
336                    sb.append("\">");
337    
338                    tags.push("</span>");
339            }
340    
341            protected void handleFontSize(
342                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
343    
344                    sb.append("<span style=\"font-size: ");
345    
346                    int size = GetterUtil.getInteger(bbCodeItem.getAttribute());
347    
348                    if ((size >= 1) && (size <= _fontSizes.length)) {
349                            sb.append(_fontSizes[size - 1]);
350                    }
351                    else {
352                            sb.append(_fontSizes[1]);
353                    }
354    
355                    sb.append("px\">");
356    
357                    tags.push("</span>");
358            }
359    
360            protected void handleImage(
361                    StringBundler sb, List<BBCodeItem> bbCodeItems, IntegerWrapper marker) {
362    
363                    sb.append("<img src=\"");
364    
365                    String src = extractData(
366                            bbCodeItems, marker, "img", BBCodeParser.TYPE_DATA, true);
367    
368                    Matcher matcher = _imagePattern.matcher(src);
369    
370                    if (matcher.matches()) {
371                            sb.append(HtmlUtil.escapeAttribute(src));
372                    }
373    
374                    sb.append("\" />");
375            }
376    
377            protected void handleItalic(StringBundler sb, Stack<String> tags) {
378                    handleSimpleTag(sb, tags, "em");
379            }
380    
381            protected void handleList(
382                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
383    
384                    String listStyle = null;
385    
386                    String tag = null;
387    
388                    String listAttribute = bbCodeItem.getAttribute();
389    
390                    if (listAttribute != null) {
391                            listStyle = _listStyles.get(listAttribute);
392    
393                            tag = "ol";
394                    }
395                    else {
396                            tag = "ul style=\"list-style: disc inside;\"";
397                    }
398    
399                    if (listStyle == null) {
400                            sb.append("<");
401                            sb.append(tag);
402                            sb.append(">");
403                    }
404                    else {
405                            sb.append("<");
406                            sb.append(tag);
407                            sb.append(" style=\"");
408                            sb.append(listStyle);
409                            sb.append("\">");
410                    }
411    
412                    tags.push("</" + tag + ">");
413            }
414    
415            protected void handleListItem(StringBundler sb, Stack<String> tags) {
416                    handleSimpleTag(sb, tags, "li");
417            }
418    
419            protected String handleNewLine(
420                    List<BBCodeItem> bbCodeItems, Stack<String> tags, IntegerWrapper marker,
421                    String data) {
422    
423                    BBCodeItem bbCodeItem = null;
424    
425                    if ((marker.getValue() + 1) < bbCodeItems.size()) {
426                            if (data.matches("\\A\r?\n\\z")) {
427                                    bbCodeItem = bbCodeItems.get(marker.getValue() + 1);
428    
429                                    if (bbCodeItem != null) {
430                                            String value = bbCodeItem.getValue();
431    
432                                            if (_excludeNewLineTypes.containsKey(value)) {
433                                                    int type = bbCodeItem.getType();
434    
435                                                    int excludeNewLineType = _excludeNewLineTypes.get(
436                                                            value);
437    
438                                                    if ((type & excludeNewLineType) > 0) {
439                                                            data = StringPool.BLANK;
440                                                    }
441                                            }
442                                    }
443                            }
444                            else if (data.matches("(?s).*\r?\n\\z")) {
445                                    bbCodeItem = bbCodeItems.get(marker.getValue() + 1);
446    
447                                    if ((bbCodeItem != null) &&
448                                            (bbCodeItem.getType() == BBCodeParser.TYPE_TAG_END)) {
449    
450                                            String value = bbCodeItem.getValue();
451    
452                                            if (value.equals("*")) {
453                                                    data = data.substring(0, data.length() - 1);
454                                            }
455                                    }
456                            }
457                    }
458    
459                    if (data.length() > 0) {
460                            data = data.replaceAll("\r?\n", "<br />");
461                    }
462    
463                    return data;
464            }
465    
466            protected void handleQuote(
467                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
468    
469                    String quote = bbCodeItem.getAttribute();
470    
471                    if ((quote != null) && (quote.length() > 0)) {
472                            sb.append("<div class=\"quote-title\">");
473                            sb.append(escapeQuote(quote));
474                            sb.append(":</div>");
475                    }
476    
477                    sb.append("<div class=\"quote\"><div class=\"quote-content\">");
478    
479                    tags.push("</div></div>");
480            }
481    
482            protected void handleSimpleTag(
483                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
484    
485                    handleSimpleTag(sb, tags, bbCodeItem.getValue());
486            }
487    
488            protected void handleSimpleTag(
489                    StringBundler sb, Stack<String> tags, String tag) {
490    
491                    sb.append("<");
492                    sb.append(tag);
493                    sb.append(">");
494    
495                    tags.push("</" + tag + ">");
496            }
497    
498            protected void handleStrikeThrough(StringBundler sb, Stack<String> tags) {
499                    handleSimpleTag(sb, tags, "strike");
500            }
501    
502            protected void handleTable(StringBundler sb, Stack<String> tags) {
503                    handleSimpleTag(sb, tags, "table");
504            }
505    
506            protected void handleTableCell(StringBundler sb, Stack<String> tags) {
507                    handleSimpleTag(sb, tags, "td");
508            }
509    
510            protected void handleTableHeader(StringBundler sb, Stack<String> tags) {
511                    handleSimpleTag(sb, tags, "th");
512            }
513    
514            protected void handleTableRow(StringBundler sb, Stack<String> tags) {
515                    handleSimpleTag(sb, tags, "tr");
516            }
517    
518            protected void handleTagEnd(
519                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
520    
521                    String tag = bbCodeItem.getValue();
522    
523                    if (isValidTag(tag)) {
524                            sb.append(tags.pop());
525                    }
526            }
527    
528            protected void handleTagStart(
529                    StringBundler sb, List<BBCodeItem> bbCodeItems, Stack<String> tags,
530                    IntegerWrapper marker, BBCodeItem bbCodeItem) {
531    
532                    String tag = bbCodeItem.getValue();
533    
534                    if (!isValidTag(tag)) {
535                            return;
536                    }
537    
538                    if (tag.equals("b")) {
539                            handleBold(sb, tags);
540                    }
541                    else if (tag.equals("center") || tag.equals("justify") ||
542                                     tag.equals("left") || tag.equals("right")) {
543    
544                            handleTextAlign(sb, tags, bbCodeItem);
545                    }
546                    else if (tag.equals("code")) {
547                            handleCode(sb, bbCodeItems, marker);
548                    }
549                    else if (tag.equals("color") || tag.equals("colour")) {
550                            handleColor(sb, tags, bbCodeItem);
551                    }
552                    else if (tag.equals("email")) {
553                            handleEmail(sb, bbCodeItems, tags, marker, bbCodeItem);
554                    }
555                    else if (tag.equals("font")) {
556                            handleFontFamily(sb, tags, bbCodeItem);
557                    }
558                    else if (tag.equals("i")) {
559                            handleItalic(sb, tags);
560                    }
561                    else if (tag.equals("img")) {
562                            handleImage(sb, bbCodeItems, marker);
563                    }
564                    else if (tag.equals("li") || tag.equals("*")) {
565                            handleListItem(sb, tags);
566                    }
567                    else if (tag.equals("list")) {
568                            handleList(sb, tags, bbCodeItem);
569                    }
570                    else if (tag.equals("q") || tag.equals("quote")) {
571                            handleQuote(sb, tags, bbCodeItem);
572                    }
573                    else if (tag.equals("s")) {
574                            handleStrikeThrough(sb, tags);
575                    }
576                    else if (tag.equals("size")) {
577                            handleFontSize(sb, tags, bbCodeItem);
578                    }
579                    else if (tag.equals("table")) {
580                            handleTable(sb, tags);
581                    }
582                    else if (tag.equals("td")) {
583                            handleTableCell(sb, tags);
584                    }
585                    else if (tag.equals("th")) {
586                            handleTableHeader(sb, tags);
587                    }
588                    else if (tag.equals("tr")) {
589                            handleTableRow(sb, tags);
590                    }
591                    else if (tag.equals("url")) {
592                            handleURL(sb, bbCodeItems, tags, marker, bbCodeItem);
593                    }
594                    else {
595                            handleSimpleTag(sb, tags, bbCodeItem);
596                    }
597            }
598    
599            protected void handleTextAlign(
600                    StringBundler sb, Stack<String> tags, BBCodeItem bbCodeItem) {
601    
602                    sb.append("<p style=\"text-align: ");
603                    sb.append(bbCodeItem.getValue());
604                    sb.append("\">");
605    
606                    tags.push("</p>");
607            }
608    
609            protected void handleURL(
610                    StringBundler sb, List<BBCodeItem> bbCodeItems, Stack<String> tags,
611                    IntegerWrapper marker, BBCodeItem bbCodeItem) {
612    
613                    sb.append("<a href=\"");
614    
615                    String href = bbCodeItem.getAttribute();
616    
617                    if (href == null) {
618                            href = extractData(
619                                    bbCodeItems, marker, "url", BBCodeParser.TYPE_DATA, false);
620                    }
621    
622                    Matcher matcher = _urlPattern.matcher(href);
623    
624                    if (matcher.matches()) {
625                            sb.append(HtmlUtil.escapeHREF(href));
626                    }
627    
628                    sb.append("\">");
629    
630                    tags.push("</a>");
631            }
632    
633            protected boolean isValidTag(String tag) {
634                    if ((tag != null) && (tag.length() > 0)) {
635                            Matcher matcher = _tagPattern.matcher(tag);
636    
637                            return matcher.matches();
638                    }
639    
640                    return false;
641            }
642    
643            private static final String[][] _EMOTICONS = {
644                    {"happy.gif", ":)", "happy"},
645                    {"smile.gif", ":D", "smile"},
646                    {"cool.gif", "B)", "cool"},
647                    {"sad.gif", ":(", "sad"},
648                    {"tongue.gif", ":P", "tongue"},
649                    {"laugh.gif", ":lol:", "laugh"},
650                    {"kiss.gif", ":#", "kiss"},
651                    {"blush.gif", ":*)", "blush"},
652                    {"bashful.gif", ":bashful:", "bashful"},
653                    {"smug.gif", ":smug:", "smug"},
654                    {"blink.gif", ":blink:", "blink"},
655                    {"huh.gif", ":huh:", "huh"},
656                    {"mellow.gif", ":mellow:", "mellow"},
657                    {"unsure.gif", ":unsure:", "unsure"},
658                    {"mad.gif", ":mad:", "mad"},
659                    {"oh_my.gif", ":O", "oh-my-goodness"},
660                    {"roll_eyes.gif", ":rolleyes:", "roll-eyes"},
661                    {"angry.gif", ":angry:", "angry"},
662                    {"suspicious.gif", "8o", "suspicious"},
663                    {"big_grin.gif", ":grin:", "grin"},
664                    {"in_love.gif", ":love:", "in-love"},
665                    {"bored.gif", ":bored:", "bored"},
666                    {"closed_eyes.gif", "-_-", "closed-eyes"},
667                    {"cold.gif", ":cold:", "cold"},
668                    {"sleep.gif", ":sleep:", "sleep"},
669                    {"glare.gif", ":glare:", "glare"},
670                    {"darth_vader.gif", ":vader:", "darth-vader"},
671                    {"dry.gif", ":dry:", "dry"},
672                    {"exclamation.gif", ":what:", "what"},
673                    {"girl.gif", ":girl:", "girl"},
674                    {"karate_kid.gif", ":kid:", "karate-kid"},
675                    {"ninja.gif", ":ph34r:", "ninja"},
676                    {"pac_man.gif", ":V", "pac-man"},
677                    {"wacko.gif", ":wacko:", "wacko"},
678                    {"wink.gif", ":wink:", "wink"},
679                    {"wub.gif", ":wub:", "wub"}
680            };
681    
682            private static Log _log = LogFactoryUtil.getLog(
683                    HtmlBBCodeTranslatorImpl.class);
684    
685            private Map<String, String> _bbCodeCharacters;
686            private BBCodeParser _bbCodeParser = new BBCodeParser();
687            private Pattern _bbCodePattern = Pattern.compile("[]&<>'\"`\\[()]");
688            private Pattern _colorPattern = Pattern.compile(
689                    "^(:?aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple" +
690                            "|red|silver|teal|white|yellow|#(?:[0-9a-f]{3})?[0-9a-f]{3})$",
691                    Pattern.CASE_INSENSITIVE);
692            private String[] _emoticonDescriptions = new String[_EMOTICONS.length];
693            private String[] _emoticonFiles = new String[_EMOTICONS.length];
694            private String[] _emoticonSymbols = new String[_EMOTICONS.length];
695            private Map<String, Integer> _excludeNewLineTypes;
696            private int[] _fontSizes = {10, 12, 16, 18, 24, 32, 48};
697            private Pattern _imagePattern = Pattern.compile(
698                    "^(?:https?://|/)[-;/?:@&=+$,_.!~*'()%0-9a-z]{1,512}$",
699                    Pattern.CASE_INSENSITIVE);
700            private Map<String, String> _listStyles;
701            private Pattern _tagPattern = Pattern.compile(
702                    "^/?(?:b|center|code|colou?r|email|i|img|justify|left|pre|q|quote|" +
703                            "right|\\*|s|size|table|tr|th|td|li|list|font|u|url)$",
704                    Pattern.CASE_INSENSITIVE);
705            private Pattern _urlPattern = Pattern.compile(
706                    "^[-;/?:@&=+$,_.!~*'()%0-9a-z#]{1,512}$", Pattern.CASE_INSENSITIVE);
707    
708    }