bool FixUrlFilter::filter(QList<HtmlToken> &tokens, int options) const { Q_UNUSED(options) QList<HtmlToken> out; QString name; bool remove = false; foreach (const HtmlToken &token, tokens) { if (token.type == HtmlToken::StartTag && token.tag == LS("a")) { HtmlATag tag(token); if (tag.url.startsWith(LS("chat://channel/"))) { ClientChannel user = ChatUrls::channel(QUrl(tag.url)); if (user) name = user->name(); } out.append(token); } else if (!name.isEmpty()) { if (name != token.text) { out.append(HtmlToken(name)); out.append(HtmlToken(HtmlToken::Tag, LS("</a>"))); remove = true; if (token.text.startsWith(name)) out.append(HtmlToken(LC(' ') + token.text.mid(name.size()))); else out.append(LC(' ') + token.text); } else out.append(token); name.clear(); } else if (token.type == HtmlToken::EndTag && token.tag == LS("a") && remove) { remove = false; } else out.append(token); } tokens = out; return true; }
void EmoticonsFilter::parse(QList<HtmlToken> &tokens, const QString &text, int pos) const { if (text.isEmpty()) return; if (pos == -1 || m_count > 6) { tokens.append(HtmlToken(text)); return; } if (pos < text.size() - 1 && text.at(pos) == LC(' ')) pos++; // Возможно в этой позиции находится начало смайла. if (m_emoticons->index().contains(text.at(pos))) { QString t = m_emoticons->find(text, pos); // Если текст не пустой, смайл найден. if (!t.isEmpty()) { // Смайл находится в конце строки. if (pos + t.size() == text.size()) { if (pos) tokens.append(HtmlToken(text.left(pos))); make(tokens, t); return; } // Смайл находится в внутри строки и содержит после себя пробел. else if (text.at(pos + t.size()) == LC(' ')) { if (pos) tokens.append(HtmlToken(text.left(pos))); make(tokens, t); parse(tokens, text.mid(pos + t.size())); return; } } } parse(tokens, text, text.indexOf(LC(' '), pos + 1)); }
void EmoticonsFilter::make(QList<HtmlToken> &tokens, const QString &text) const { Emoticon emoticon = m_emoticons->get(text); if (!emoticon) { tokens.append(HtmlToken(text)); return; } m_count++; HtmlToken a(HtmlToken::Tag, HtmlATag(LS("emoticon:") + ChatId::toBase32(text.toUtf8()), text).toText()); tokens.append(a); QString img = QString(LS("<img class=\"emoticon\" title=\"%1\" alt=\"%1\" src=\"%2\" width=\"%3\" height=\"%4\" />")) .arg(text) .arg(QUrl::fromLocalFile(emoticon->file()).toString()) .arg(emoticon->width()) .arg(emoticon->height()); HtmlToken tag(img); tag.parent = LS("a"); tokens.append(tag); tokens.append(a.toEndTag()); }
/*! * Оптимизация и дополнительная фильтрация токенов. */ void HtmlFilter::optimize(QList<HtmlToken> &tokens) const { int index = -1; for (int i = 0; i < tokens.size(); ++i) { if (!tokens.at(i).simple) { index = i; break; } } if (index == -1) return; HtmlToken token = tokens.at(index); if (token.tag == LS("a") || token.tag == LS("font")) { tokens[index].simple = true; int gt = endTag(token.tag, tokens, index); // Если закрывающий тег не найден, удаляем тег. if (gt == -1) { tokens.removeAt(index); return optimize(tokens); } // Если закрывающий следует сразу после открывающего, удаляем тег и закрывающий тег. if (gt - index == 1) { tokens.removeAt(index); if (index < tokens.size()) tokens.removeAt(index); return optimize(tokens); } // Удаляем все не текстовые токены внутри тега. for (int i = gt - 1; i > index; --i) { if (tokens.at(i).type != HtmlToken::Text) { tokens.removeAt(i); gt--; } else { tokens[i].parent = token.tag; } } AbstractTag *tag = 0; if (token.tag == LS("a")) tag = new HtmlATag(token); else if (token.tag == LS("font")) tag = new HtmlFontTag(token); if (!tag->valid || gt - index == 1) { tokens.removeAt(gt); tokens.removeAt(index); } else tokens[index].text = tag->toText(); delete tag; return optimize(tokens); } /// Тег span в зависимости от css стилей преобразуется в теги b, i, u, s и font /// и полностью удаляется из текста. Тег font используется для установки цвета элемента. else if (token.tag == LS("span")) { QList<HtmlToken> tags; const Range range = attrBody(LS("style"), token.text); if (range.first != -1) { QString value; if (cssValue(LS("font-weight"), token.text, range, value) && (value == LS("bold") || value == LS("bolder") || value == LS("600") || value == LS("700") || value == LS("800") || value == LS("900"))) tags.append(HtmlToken(HtmlToken::Tag, LS("<b>"))); if (cssValue(LS("font-style"), token.text, range, value) && value == LS("italic")) tags.append(HtmlToken(HtmlToken::Tag, LS("<i>"))); if (cssValue(LS("text-decoration"), token.text, range, value)) { if (value == LS("underline")) tags.append(HtmlToken(HtmlToken::Tag, LS("<u>"))); else if (value == LS("line-through")) tags.append(HtmlToken(HtmlToken::Tag, LS("<s>"))); } if (cssValue(LS("color"), token.text, range, value) && colorValue(value)) { HtmlToken token(HtmlToken::Tag, LS("<font color=\"") + value + LS("\">")); token.simple = true; tags.append(token); } } tokens.removeAt(index); int gt = endTag(token.tag, tokens, index); if (gt == -1) return optimize(tokens); tokens.removeAt(gt); if (index == gt) return optimize(tokens); gt++; foreach (HtmlToken tag, tags) { tokens.insert(index, tag); tokens.insert(gt, tag.toEndTag()); index++; gt++; }
/*! * Поиск ссылок в тексте и автоматическое преобразование их в html ссылки. */ void LinksFilter::parse(QList<HtmlToken> &tokens, const QString &text) const { int index = -1; int last = -1; QString url; /// - http/https/ftp полный список в \p m_scheme. for (int i = 0; i < m_scheme.size(); ++i) { index = text.indexOf(m_scheme.at(i)); if (index != -1) { if (index > 0) tokens.append(HtmlToken(text.left(index))); url = this->url(text, index, last); makeUrl(tokens, url, url); if (last != -1) return parse(tokens, text.mid(last)); return; } } /// - Ссылки вида www.exampe.com в преобразуются в http. index = text.indexOf(LS("www.")); if (index != -1) { url = this->url(text, index, last); if (url.count(LC('.')) > 1) { if (index > 0) tokens.append(HtmlToken(text.left(index))); makeUrl(tokens, LS("http://") + url, url); if (last != -1) return parse(tokens, text.mid(last)); return; } if (last != -1) { tokens.append(HtmlToken(text.left(last))); return parse(tokens, text.mid(last)); } } /// - Ссылки вида [email protected] преобразуются в mailto. index = text.indexOf(LC('@')); if (index != -1) { int start = text.lastIndexOf(LC(' '), index); QString name = text.mid(start + 1, index - start - 1); last = -1; if (!name.isEmpty()) { url = this->url(text, index, last); if (url.contains(LC('.'))) { if (index > 0) { tokens.append(HtmlToken(text.left(index - name.size()))); makeUrl(tokens, LS("mailto:") + name + url, name + url); if (last != -1) return parse(tokens, text.mid(last)); return; } } } if (last != -1) { tokens.append(HtmlToken(text.left(last))); return parse(tokens, text.mid(last)); } } tokens.append(HtmlToken(text)); }