text:
#include "ui/UIRichText.h"

#include <sstream>
#include <vector>
#include <locale>

#include "platform/CCFileUtils.h"
#include "platform/CCApplication.h"
#include "base/CCEventListenerTouch.h"
#include "base/CCEventDispatcher.h"
#include "base/CCDirector.h"
#include "2d/CCLabel.h"
#include "2d/CCSprite.h"
#include "base/ccUTF8.h"
#include "ui/UIHelper.h"

#include "platform/CCSAXParser.h"

USING_NS_CC;
using namespace cocos2d::ui;

class ListenerComponent : public Component
{
public:
    static const std::string COMPONENT_NAME;    /*!< component name */

    static ListenerComponent* create(Node* parent, const std::string& url, const RichText::OpenUrlHandler handleOpenUrl = nullptr)
    {
        auto component = new (std::nothrow) ListenerComponent(parent, url, handleOpenUrl);
        component->autorelease();
        return component;
    }

    explicit ListenerComponent(Node* parent, const std::string& url, const RichText::OpenUrlHandler handleOpenUrl)
    : _parent(parent)
    , _url(url)
    , _handleOpenUrl(handleOpenUrl)
    {
        setName(ListenerComponent::COMPONENT_NAME);

        _touchListener = cocos2d::EventListenerTouchAllAtOnce::create();
        _touchListener->onTouchesEnded = CC_CALLBACK_2(ListenerComponent::onTouchesEnded, this);

        Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(_touchListener, _parent);
        _touchListener->retain();
    }

    virtual ~ListenerComponent()
    {
        Director::getInstance()->getEventDispatcher()->removeEventListener(_touchListener);
        _touchListener->release();
    }

    void onTouchesEnded(const std::vector<Touch*>& touches, Event* /*event*/)
    {
        for (const auto& touch: touches)
        {
            // FIXME: Node::getBoundBox() doesn't return it in local coordinates... so create one manually.
            Rect localRect = Rect(Vec2::ZERO, _parent->getContentSize());
            if (localRect.containsPoint(_parent->convertTouchToNodeSpace(touch))) {
                if (_handleOpenUrl) {
                    _handleOpenUrl(_url);
                }
            }
        }
    }

    void setOpenUrlHandler(const RichText::OpenUrlHandler& handleOpenUrl)
    {
        _handleOpenUrl = handleOpenUrl;
    }

private:
    Node* _parent;      // weak ref.
    std::string _url;
    RichText::OpenUrlHandler _handleOpenUrl;
    EventDispatcher* _eventDispatcher;  // weak ref.
    EventListenerTouchAllAtOnce* _touchListener;    // strong ref.
};
const std::string ListenerComponent::COMPONENT_NAME("cocos2d_ui_UIRichText_ListenerComponent");

bool RichElement::init(int tag, const Color3B &color, GLubyte opacity)
{
    _tag = tag;
    _color = color;
    _opacity = opacity;
    return true;
}

bool RichElement::equalType(Type type)
{
    return (_type == type);
}

void RichElement::setColor(const Color3B& color)
{
    _color = color;
}

RichElementText* RichElementText::create(int tag, const Color3B &color, GLubyte opacity, const std::string& text,
                                         const std::string& fontName, float fontSize, uint32_t flags, const std::string& url,
                                         const Color3B& outlineColor, int outlineSize ,
                                         const Color3B& shadowColor, const cocos2d::Size& shadowOffset, int shadowBlurRadius,
                                         const Color3B& glowColor)
{
    RichElementText* element = new (std::nothrow) RichElementText();
    if (element && element->init(tag, color, opacity, text, fontName, fontSize, flags, url,
                                 outlineColor, outlineSize, shadowColor, shadowOffset, shadowBlurRadius, glowColor))
    {
        element->autorelease();
        return element;
    }
    CC_SAFE_DELETE(element);
    return nullptr;
}

bool RichElementText::init(int tag, const Color3B &color, GLubyte opacity, const std::string& text,
                           const std::string& fontName, float fontSize, uint32_t flags, const std::string& url,
                           const Color3B& outlineColor, int outlineSize ,
                           const Color3B& shadowColor, const cocos2d::Size& shadowOffset, int shadowBlurRadius,
                           const Color3B& glowColor)
{
    if (RichElement::init(tag, color, opacity))
    {
        _text = text;
        _fontName = fontName;
        _fontSize = fontSize;
        _flags = flags;
        _url = url;
        _outlineColor = outlineColor;
        _outlineSize = outlineSize;
        _shadowColor = shadowColor;
        _shadowOffset = shadowOffset;
        _shadowBlurRadius = shadowBlurRadius;
        _glowColor = glowColor;
        return true;
    }
    return false;
}

RichElementImage* RichElementImage::create(int tag, const Color3B &color, GLubyte opacity, const std::string& filePath, const std::string& url)
{
    RichElementImage* element = new (std::nothrow) RichElementImage();
    if (element && element->init(tag, color, opacity, filePath, url))
    {
        element->autorelease();
        return element;
    }
    CC_SAFE_DELETE(element);
    return nullptr;
}

bool RichElementImage::init(int tag, const Color3B &color, GLubyte opacity, const std::string& filePath, const std::string& url)
{
    if (RichElement::init(tag, color, opacity))
    {
        _filePath = filePath;
        _width = -1;
        _height = -1;
        _url = url;
        return true;
    }
    return false;
}

void RichElementImage::setWidth(int width)
{
    _width = width;
}

void RichElementImage::setHeight(int height)
{
    _height = height;
}

void RichElementImage::setUrl(const std::string& url)
{
    _url = url;
}

RichElementCustomNode* RichElementCustomNode::create(int tag, const Color3B &color, GLubyte opacity, cocos2d::Node *customNode)
{
    RichElementCustomNode* element = new (std::nothrow) RichElementCustomNode();
    if (element && element->init(tag, color, opacity, customNode))
    {
        element->autorelease();
        return element;
    }
    CC_SAFE_DELETE(element);
    return nullptr;
}

bool RichElementCustomNode::init(int tag, const Color3B &color, GLubyte opacity, cocos2d::Node *customNode)
{
    if (RichElement::init(tag, color, opacity))
    {
        _customNode = customNode;
        _customNode->retain();
        return true;
    }
    return false;
}

RichElementNewLine* RichElementNewLine::create(int tag, const Color3B& color, GLubyte opacity)
{
    RichElementNewLine* element = new (std::nothrow) RichElementNewLine();
    if (element && element->init(tag, color, opacity))
    {
        element->autorelease();
        return element;
    }
    CC_SAFE_DELETE(element);
    return nullptr;
}

/** @brief parse a XML. */
class MyXMLVisitor : public SAXDelegator
{
public:
    /** @brief underline or strikethrough */
    enum class StyleLine {
        NONE,
        UNDERLINE,          /*!< underline */
        STRIKETHROUGH       /*!< a typographical presentation of words with a horizontal line through their center */
    };

    /** @brief outline, shadow or glow */
    enum class StyleEffect {
        NONE,
        OUTLINE,            /*!< outline effect enabled */
        SHADOW,             /*!< shadow effect enabled */
        GLOW                /*!< glow effect enabled @discussion Limiting use to only when the Label created with true type font. */
    };

    /** @brief the attributes of text tag */
    struct Attributes
    {
        std::string face;                       /*!< font name */
        std::string url;                        /*!< url is a attribute of a anchor tag */
        float fontSize;                         /*!< font size */
        Color3B color;                          /*!< font color */
        bool hasColor;                          /*!< or color is specified? */
        bool bold;                              /*!< bold text */
        bool italics;                           /*!< italic text */
        StyleLine line;                         /*!< underline or strikethrough */
        StyleEffect effect;                     /*!< outline, shadow or glow */
        Color3B outlineColor;                   /*!< the color of the outline */
        int outlineSize;                        /*!< the outline effect size value */
        Color3B shadowColor;                    /*!< the shadow effect color value */
        cocos2d::Size shadowOffset;             /*!< shadow effect offset value */
        int shadowBlurRadius;                   /*!< the shadow effect blur radius */
        Color3B glowColor;                      /*!< the glow effect color value */

        void setColor(const Color3B& acolor)
        {
            color = acolor;
            hasColor = true;
        }
        Attributes()
        : bold(false)
        , italics(false)
        , line(StyleLine::NONE)
        , hasColor(false)
        , fontSize(-1)
        , effect(StyleEffect::NONE)
        {
        }
    };

private:
    std::vector<Attributes> _fontElements;

    RichText* _richText;

    struct TagBehavior {
        bool                isFontElement;
        RichText::VisitEnterHandler   handleVisitEnter;
    };
    typedef std::unordered_map<std::string, TagBehavior> TagTables;

    static TagTables        _tagTables;

public:
    explicit MyXMLVisitor(RichText* richText);
    virtual ~MyXMLVisitor();

    Color3B getColor() const;

    float getFontSize() const;

    std::string getFace() const;

    std::string getURL() const;

    bool getBold() const;

    bool getItalics() const;

    bool getUnderline() const;

    bool getStrikethrough() const;

    std::tuple<bool, Color3B, int> getOutline() const;

    std::tuple<bool, Color3B, cocos2d::Size, int> getShadow() const;

    std::tuple<bool, Color3B> getGlow() const;

    void startElement(void *ctx, const char *name, const char **atts) override;

    void endElement(void *ctx, const char *name) override;

    void textHandler(void *ctx, const char *s, size_t len) override;

    void pushBackFontElement(const Attributes& attribs);

    void popBackFontElement();

    void pushBackElement(RichElement* element);

    static void setTagDescription(const std::string& tag, bool isFontElement, RichText::VisitEnterHandler handleVisitEnter);

    static void removeTagDescription(const std::string& tag);

private:
    ValueMap tagAttrMapWithXMLElement(const char ** attrs);
};

MyXMLVisitor::TagTables MyXMLVisitor::_tagTables;

MyXMLVisitor::MyXMLVisitor(RichText* richText)
: _richText(richText)
, _fontElements(20)
{
    MyXMLVisitor::setTagDescription("font", true, [](const ValueMap& tagAttrValueMap) {
        // supported attributes:
        // size, color, align, face
        ValueMap attrValueMap;

        if (tagAttrValueMap.find("size") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_FONT_SIZE] = tagAttrValueMap.at("size").asString();
        }
        if (tagAttrValueMap.find("color") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_FONT_COLOR_STRING] = tagAttrValueMap.at("color").asString();
        }
        if (tagAttrValueMap.find("face") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_FONT_FACE] = tagAttrValueMap.at("face").asString();
        }

        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("b", true, [](const ValueMap& /*tagAttrValueMap*/) {
        // no supported attributes
        ValueMap attrValueMap;
        attrValueMap[RichText::KEY_TEXT_BOLD] = true;
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("i", true, [](const ValueMap& /*tagAttrValueMap*/) {
        // no supported attributes
        ValueMap attrValueMap;
        attrValueMap[RichText::KEY_TEXT_ITALIC] = true;
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("del", true, [](const ValueMap& /*tagAttrValueMap*/) {
        // no supported attributes
        ValueMap attrValueMap;
        attrValueMap[RichText::KEY_TEXT_LINE] = RichText::VALUE_TEXT_LINE_DEL;
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("u", true, [](const ValueMap& /*tagAttrValueMap*/) {
        // no supported attributes
        ValueMap attrValueMap;
        attrValueMap[RichText::KEY_TEXT_LINE] = RichText::VALUE_TEXT_LINE_UNDER;
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("small", true, [](const ValueMap& /*tagAttrValueMap*/) {
        ValueMap attrValueMap;
        attrValueMap[RichText::KEY_FONT_SMALL] = true;
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("big", true, [](const ValueMap& /*tagAttrValueMap*/) {
        ValueMap attrValueMap;
        attrValueMap[RichText::KEY_FONT_BIG] = true;
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("img", false, [](const ValueMap& tagAttrValueMap) {
        // supported attributes:
        // src, height, width
        std::string src;
        int height = -1;
        int width = -1;

        if (tagAttrValueMap.find("src") != tagAttrValueMap.end()) {
            src = tagAttrValueMap.at("src").asString();
        }
        if (tagAttrValueMap.find("height") != tagAttrValueMap.end()) {
            height = tagAttrValueMap.at("height").asInt();
        }
        if (tagAttrValueMap.find("width") != tagAttrValueMap.end()) {
            width = tagAttrValueMap.at("width").asInt();
        }

        RichElementImage* elementImg = nullptr;
        if (src.length()) {
            elementImg = RichElementImage::create(0, Color3B::WHITE, 255, src);
            if (0 <= height) elementImg->setHeight(height);
            if (0 <= width)  elementImg->setWidth(width);
        }
        return make_pair(ValueMap(), elementImg);
    });

    MyXMLVisitor::setTagDescription("a", true, [](const ValueMap& tagAttrValueMap) {
        // supported attributes:
        ValueMap attrValueMap;

        if (tagAttrValueMap.find("href") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_URL] = tagAttrValueMap.at("href").asString();
        }
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("br", false, [](const ValueMap& /*tagAttrValueMap*/)  {
        RichElementNewLine* richElement = RichElementNewLine::create(0, Color3B::WHITE, 255);
        return make_pair(ValueMap(), richElement);
    });

    MyXMLVisitor::setTagDescription("outline", true, [](const ValueMap& tagAttrValueMap) {
        // supported attributes:
        // color, size
        ValueMap attrValueMap;

        attrValueMap[RichText::KEY_TEXT_STYLE] = RichText::VALUE_TEXT_STYLE_OUTLINE;
        if (tagAttrValueMap.find("color") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_TEXT_OUTLINE_COLOR] = tagAttrValueMap.at("color").asString();
        }
        if (tagAttrValueMap.find("size") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_TEXT_OUTLINE_SIZE] = tagAttrValueMap.at("size").asString();
        }
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("shadow", true, [](const ValueMap& tagAttrValueMap) {
        // supported attributes:
        // color, offsetWidth, offsetHeight, blurRadius
        ValueMap attrValueMap;

        attrValueMap[RichText::KEY_TEXT_STYLE] = RichText::VALUE_TEXT_STYLE_SHADOW;
        if (tagAttrValueMap.find("color") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_TEXT_SHADOW_COLOR] = tagAttrValueMap.at("color").asString();
        }
        if (tagAttrValueMap.find("offsetWidth") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_TEXT_SHADOW_OFFSET_WIDTH] = tagAttrValueMap.at("offsetWidth").asString();
        }
        if (tagAttrValueMap.find("offsetHeight") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_TEXT_SHADOW_OFFSET_HEIGHT] = tagAttrValueMap.at("offsetHeight").asString();
        }
        if (tagAttrValueMap.find("blurRadius") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_TEXT_SHADOW_BLUR_RADIUS] = tagAttrValueMap.at("blurRadius").asString();
        }
        return make_pair(attrValueMap, nullptr);
    });

    MyXMLVisitor::setTagDescription("glow", true, [](const ValueMap& tagAttrValueMap) {
        // supported attributes:
        // color
        ValueMap attrValueMap;

        attrValueMap[RichText::KEY_TEXT_STYLE] = RichText::VALUE_TEXT_STYLE_GLOW;
        if (tagAttrValueMap.find("color") != tagAttrValueMap.end()) {
            attrValueMap[RichText::KEY_TEXT_GLOW_COLOR] = tagAttrValueMap.at("color").asString();
        }
        return make_pair(attrValueMap, nullptr);
    });
}

MyXMLVisitor::~MyXMLVisitor()
{
}

Color3B MyXMLVisitor::getColor() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->hasColor)
            return i->color;
    }
    return Color3B::WHITE;
}

float MyXMLVisitor::getFontSize() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->fontSize != -1)
            return i->fontSize;
    }
    return 12;
}

std::string MyXMLVisitor::getFace() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->face.size() != 0)
            return i->face;
    }
    return "fonts/Marker Felt.ttf";
}

std::string MyXMLVisitor::getURL() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->url.size() != 0)
            return i->url;
    }
    return "";
}

bool MyXMLVisitor::getBold() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->bold)
            return true;
    }
    return false;
}

bool MyXMLVisitor::getItalics() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->italics)
            return true;
    }
    return false;
}

bool MyXMLVisitor::getUnderline() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->line == StyleLine::UNDERLINE)
            return true;
    }
    return false;
}

bool MyXMLVisitor::getStrikethrough() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->line == StyleLine::STRIKETHROUGH)
            return true;
    }
    return false;
}

std::tuple<bool, Color3B, int> MyXMLVisitor::getOutline() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->effect == StyleEffect::OUTLINE)
            return std::make_tuple(true, i->outlineColor, i->outlineSize);
    }
    return std::make_tuple(false, Color3B::WHITE, -1);
}

std::tuple<bool, Color3B, cocos2d::Size, int> MyXMLVisitor::getShadow() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->effect == StyleEffect::SHADOW)
            return std::make_tuple(true, i->shadowColor, i->shadowOffset, i->shadowBlurRadius);
    }
    return std::make_tuple(false, Color3B::BLACK, Size(2.0, -2.0), 0);
}

std::tuple<bool, Color3B> MyXMLVisitor::getGlow() const
{
    for (auto i = _fontElements.rbegin(), iRend = _fontElements.rend(); i != iRend; ++i)
    {
        if (i->effect == StyleEffect::GLOW)
            return std::make_tuple(true, i->glowColor);
    }
    return std::make_tuple(false, Color3B::WHITE);
}

void MyXMLVisitor::startElement(void* /*ctx*/, const char *elementName, const char **atts)
{
    auto it = _tagTables.find(elementName);
    if (it != _tagTables.end()) {
        auto tagBehavior = it->second;
        if (tagBehavior.handleVisitEnter != nullptr) {
            ValueMap&& tagAttrValueMap = tagAttrMapWithXMLElement(atts);
            auto result = tagBehavior.handleVisitEnter(tagAttrValueMap);
            ValueMap& attrValueMap = result.first;
            RichElement* richElement = result.second;
            if (!attrValueMap.empty()) {
                Attributes attributes;

                if (attrValueMap.find(RichText::KEY_FONT_SIZE) != attrValueMap.end()) {
                    attributes.fontSize = attrValueMap.at(RichText::KEY_FONT_SIZE).asFloat();
                }
                if (attrValueMap.find(RichText::KEY_FONT_SMALL) != attrValueMap.end()) {
                    attributes.fontSize = getFontSize() * 0.8f;
                }
                if (attrValueMap.find(RichText::KEY_FONT_BIG) != attrValueMap.end()) {
                    attributes.fontSize = getFontSize() * 1.25f;
                }
                if (attrValueMap.find(RichText::KEY_FONT_COLOR_STRING) != attrValueMap.end()) {
                    attributes.setColor(_richText->color3BWithString(attrValueMap.at(RichText::KEY_FONT_COLOR_STRING).asString()));
                }
                if (attrValueMap.find(RichText::KEY_FONT_FACE) != attrValueMap.end()) {
                    attributes.face = attrValueMap.at(RichText::KEY_FONT_FACE).asString();
                }
                if (attrValueMap.find(RichText::KEY_TEXT_BOLD) != attrValueMap.end()) {
                    attributes.bold = true;
                }
                if (attrValueMap.find(RichText::KEY_TEXT_ITALIC) != attrValueMap.end()) {
                    attributes.italics = true;
                }
                if (attrValueMap.find(RichText::KEY_TEXT_LINE) != attrValueMap.end()) {
                    auto keyTextLine = attrValueMap.at(RichText::KEY_TEXT_LINE).asString();
                    if (keyTextLine == RichText::VALUE_TEXT_LINE_DEL) {
                        attributes.line = StyleLine::STRIKETHROUGH;
                    }
                    else if (keyTextLine == RichText::VALUE_TEXT_LINE_UNDER) {
                        attributes.line = StyleLine::UNDERLINE;
                    }
                }
                if (attrValueMap.find(RichText::KEY_URL) != attrValueMap.end()) {
                    attributes.url = attrValueMap.at(RichText::KEY_URL).asString();
                    attributes.setColor(_richText->getAnchorFontColor3B());
                    if (_richText->isAnchorTextBoldEnabled()) {
                        attributes.bold = true;
                    }
                    if (_richText->isAnchorTextItalicEnabled()) {
                        attributes.italics = true;
                    }
                    if (_richText->isAnchorTextUnderlineEnabled()) {
                        attributes.line = StyleLine::UNDERLINE;
                    }
                    if (_richText->isAnchorTextDelEnabled()) {
                        attributes.line = StyleLine::STRIKETHROUGH;
                    }
                    if (_richText->isAnchorTextOutlineEnabled()) {
                        attributes.effect = StyleEffect::OUTLINE;
                        attributes.outlineColor = _richText->getAnchorTextOutlineColor3B();
                        attributes.outlineSize = _richText->getAnchorTextOutlineSize();
                    }
                    if (_richText->isAnchorTextShadowEnabled()) {
                        attributes.effect = StyleEffect::SHADOW;
                        attributes.shadowColor = _richText->getAnchorTextShadowColor3B();
                        attributes.shadowOffset = _richText->getAnchorTextShadowOffset();
                        attributes.shadowBlurRadius = _richText->getAnchorTextShadowBlurRadius();
                    }
                    if (_richText->isAnchorTextGlowEnabled()) {
                        attributes.effect = StyleEffect::GLOW;
                        attributes.glowColor = _richText->getAnchorTextGlowColor3B();
                    }
                }
                if (attrValueMap.find(RichText::KEY_TEXT_STYLE) != attrValueMap.end()) {
                    auto keyTextStyle = attrValueMap.at(RichText::KEY_TEXT_STYLE).asString();
                    if (keyTextStyle == RichText::VALUE_TEXT_STYLE_OUTLINE) {
                        attributes.effect = StyleEffect::OUTLINE;
                        if (attrValueMap.find(RichText::KEY_TEXT_OUTLINE_COLOR) != attrValueMap.end()) {
                            attributes.outlineColor = _richText->color3BWithString(attrValueMap.at(RichText::KEY_TEXT_OUTLINE_COLOR).asString());
                        }
                        if (attrValueMap.find(RichText::KEY_TEXT_OUTLINE_SIZE) != attrValueMap.end()) {
                            attributes.outlineSize = attrValueMap.at(RichText::KEY_TEXT_OUTLINE_SIZE).asInt();
                        }
                    }
                    else if (keyTextStyle == RichText::VALUE_TEXT_STYLE_SHADOW) {
                        attributes.effect = StyleEffect::SHADOW;
                        if (attrValueMap.find(RichText::KEY_TEXT_SHADOW_COLOR) != attrValueMap.end()) {
                            attributes.shadowColor = _richText->color3BWithString(attrValueMap.at(RichText::KEY_TEXT_SHADOW_COLOR).asString());
                        }
                        if ((attrValueMap.find(RichText::KEY_TEXT_SHADOW_OFFSET_WIDTH) != attrValueMap.end())
                                && (attrValueMap.find(RichText::KEY_TEXT_SHADOW_OFFSET_HEIGHT) != attrValueMap.end())) {
                            attributes.shadowOffset = Size(attrValueMap.at(RichText::KEY_TEXT_SHADOW_OFFSET_WIDTH).asFloat(),
                                                           attrValueMap.at(RichText::KEY_TEXT_SHADOW_OFFSET_HEIGHT).asFloat());
                        }
                        if (attrValueMap.find(RichText::KEY_TEXT_SHADOW_BLUR_RADIUS) != attrValueMap.end()) {
                            attributes.shadowBlurRadius = attrValueMap.at(RichText::KEY_TEXT_SHADOW_BLUR_RADIUS).asInt();
                        }
                    }
                    else if (keyTextStyle == RichText::VALUE_TEXT_STYLE_GLOW) {
                        attributes.effect = StyleEffect::GLOW;
                        if (attrValueMap.find(RichText::KEY_TEXT_GLOW_COLOR) != attrValueMap.end()) {
                            attributes.glowColor = _richText->color3BWithString(attrValueMap.at(RichText::KEY_TEXT_GLOW_COLOR).asString());
                        }
                    }
                }

                pushBackFontElement(attributes);
            }
            if (richElement) {
                if (richElement->equalType(RichElement::Type::IMAGE)) {
                    richElement->setColor(getColor());
                    auto* richElementImage = static_cast<RichElementImage*>(richElement);
                    richElementImage->setUrl(getURL());
                }
                else if (richElement->equalType(RichElement::Type::NEWLINE)) {
                    richElement->setColor(getColor());
                }
                pushBackElement(richElement);
            }
        }
    }
}

void MyXMLVisitor::endElement(void* /*ctx*/, const char *elementName)
{
    auto it = _tagTables.find(elementName);
    if (it != _tagTables.end()) {
        auto tagBehavior = it->second;
        if (tagBehavior.isFontElement) {
            popBackFontElement();
        }
    }
}

void MyXMLVisitor::textHandler(void* /*ctx*/, const char *str, size_t len)
{
    std::string text(str, len);
    auto color = getColor();
    auto face = getFace();
    auto fontSize = getFontSize();
    auto italics = getItalics();
    auto underline = getUnderline();
    auto strikethrough = getStrikethrough();
    auto bold = getBold();
    auto url = getURL();
    auto outline = getOutline();
    auto shadow = getShadow();
    auto glow = getGlow();

    uint32_t flags = 0;
    if (italics)
        flags |= RichElementText::ITALICS_FLAG;
    if (bold)
        flags |= RichElementText::BOLD_FLAG;
    if (underline)
        flags |= RichElementText::UNDERLINE_FLAG;
    if (strikethrough)
        flags |= RichElementText::STRIKETHROUGH_FLAG;
    if (url.size() > 0)
        flags |= RichElementText::URL_FLAG;
    if (std::get<0>(outline))
        flags |= RichElementText::OUTLINE_FLAG;
    if (std::get<0>(shadow))
        flags |= RichElementText::SHADOW_FLAG;
    if (std::get<0>(glow))
        flags |= RichElementText::GLOW_FLAG;

    auto element = RichElementText::create(0, color, 255, text, face, fontSize, flags, url,
        std::get<1>(outline), std::get<2>(outline),
        std::get<1>(shadow), std::get<2>(shadow), std::get<3>(shadow),
        std::get<1>(glow));
    _richText->pushBackElement(element);
}

void MyXMLVisitor::pushBackFontElement(const MyXMLVisitor::Attributes& attribs)
{
    _fontElements.push_back(attribs);
}

void MyXMLVisitor::popBackFontElement()
{
    _fontElements.pop_back();
}

void MyXMLVisitor::pushBackElement(RichElement* element)
{
    _richText->pushBackElement(element);
}

void MyXMLVisitor::setTagDescription(const std::string& tag, bool isFontElement, RichText::VisitEnterHandler handleVisitEnter)
{
    MyXMLVisitor::_tagTables[tag] = {isFontElement, handleVisitEnter};
}

void MyXMLVisitor::removeTagDescription(const std::string& tag)
{
    MyXMLVisitor::_tagTables.erase(tag);
}

ValueMap MyXMLVisitor::tagAttrMapWithXMLElement(const char ** attrs)
{
    ValueMap tagAttrValueMap;
    for (const char** attr = attrs; *attr != nullptr; attr = (attrs += 2)) {
        if (attr[0] && attr[1]) {
            tagAttrValueMap[attr[0]] = attr[1];
        }
    }
    return tagAttrValueMap;
}

const std::string RichText::KEY_VERTICAL_SPACE("KEY_VERTICAL_SPACE");
const std::string RichText::KEY_WRAP_MODE("KEY_WRAP_MODE");
const std::string RichText::KEY_FONT_COLOR_STRING("KEY_FONT_COLOR_STRING");
const std::string RichText::KEY_FONT_SIZE("KEY_FONT_SIZE");
const std::string RichText::KEY_FONT_SMALL("KEY_FONT_SMALL");
const std::string RichText::KEY_FONT_BIG("KEY_FONT_BIG");
const std::string RichText::KEY_FONT_FACE("KEY_FONT_FACE");
const std::string RichText::KEY_TEXT_BOLD("KEY_TEXT_BOLD");
const std::string RichText::KEY_TEXT_ITALIC("KEY_TEXT_ITALIC");
const std::string RichText::KEY_TEXT_LINE("KEY_TEXT_LINE");
const std::string RichText::VALUE_TEXT_LINE_NONE("VALUE_TEXT_LINE_NONE");
const std::string RichText::VALUE_TEXT_LINE_DEL("VALUE_TEXT_LINE_DEL");
const std::string RichText::VALUE_TEXT_LINE_UNDER("VALUE_TEXT_LINE_UNDER");
const std::string RichText::KEY_TEXT_STYLE("KEY_TEXT_STYLE");
const std::string RichText::VALUE_TEXT_STYLE_NONE("VALUE_TEXT_STYLE_NONE");
const std::string RichText::VALUE_TEXT_STYLE_OUTLINE("VALUE_TEXT_STYLE_OUTLINE");
const std::string RichText::VALUE_TEXT_STYLE_SHADOW("VALUE_TEXT_STYLE_SHADOW");
const std::string RichText::VALUE_TEXT_STYLE_GLOW("VALUE_TEXT_STYLE_GLOW");
const std::string RichText::KEY_TEXT_OUTLINE_COLOR("KEY_TEXT_OUTLINE_COLOR");
const std::string RichText::KEY_TEXT_OUTLINE_SIZE("KEY_TEXT_OUTLINE_SIZE");
const std::string RichText::KEY_TEXT_SHADOW_COLOR("KEY_TEXT_SHADOW_COLOR");
const std::string RichText::KEY_TEXT_SHADOW_OFFSET_WIDTH("KEY_TEXT_SHADOW_OFFSET_WIDTH");
const std::string RichText::KEY_TEXT_SHADOW_OFFSET_HEIGHT("KEY_TEXT_SHADOW_OFFSET_HEIGHT");
const std::string RichText::KEY_TEXT_SHADOW_BLUR_RADIUS("KEY_TEXT_SHADOW_BLUR_RADIUS");
const std::string RichText::KEY_TEXT_GLOW_COLOR("KEY_TEXT_GLOW_COLOR");
const std::string RichText::KEY_URL("KEY_URL");
const std::string RichText::KEY_ANCHOR_FONT_COLOR_STRING("KEY_ANCHOR_FONT_COLOR_STRING");
const std::string RichText::KEY_ANCHOR_TEXT_BOLD("KEY_ANCHOR_TEXT_BOLD");
const std::string RichText::KEY_ANCHOR_TEXT_ITALIC("KEY_ANCHOR_TEXT_ITALIC");
const std::string RichText::KEY_ANCHOR_TEXT_LINE("KEY_ANCHOR_TEXT_LINE");
const std::string RichText::KEY_ANCHOR_TEXT_STYLE("KEY_ANCHOR_TEXT_STYLE");
const std::string RichText::KEY_ANCHOR_TEXT_OUTLINE_COLOR("KEY_ANCHOR_TEXT_OUTLINE_COLOR");
const std::string RichText::KEY_ANCHOR_TEXT_OUTLINE_SIZE("KEY_ANCHOR_TEXT_OUTLINE_SIZE");
const std::string RichText::KEY_ANCHOR_TEXT_SHADOW_COLOR("KEY_ANCHOR_TEXT_SHADOW_COLOR");
const std::string RichText::KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH("KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH");
const std::string RichText::KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT("KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT");
const std::string RichText::KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS("KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS");
const std::string RichText::KEY_ANCHOR_TEXT_GLOW_COLOR("KEY_ANCHOR_TEXT_GLOW_COLOR");

RichText::RichText()
    : _formatTextDirty(true)
    , _leftSpaceWidth(0.0f)
{
    _defaults[KEY_VERTICAL_SPACE] = 0.0f;
    _defaults[KEY_WRAP_MODE] = static_cast<int>(WrapMode::WRAP_PER_WORD);
    _defaults[KEY_FONT_COLOR_STRING] = "#ffffff";
    _defaults[KEY_FONT_SIZE] = 12.0f;
    _defaults[KEY_FONT_FACE] = "Verdana";
    _defaults[KEY_ANCHOR_FONT_COLOR_STRING] = "#0000FF";
    _defaults[KEY_ANCHOR_TEXT_BOLD] = false;
    _defaults[KEY_ANCHOR_TEXT_ITALIC] = false;
    _defaults[KEY_ANCHOR_TEXT_LINE] = VALUE_TEXT_LINE_NONE;
    _defaults[KEY_ANCHOR_TEXT_STYLE] = VALUE_TEXT_STYLE_NONE;
}

RichText::~RichText()
{
    _richElements.clear();
}

RichText* RichText::create()
{
    RichText* widget = new (std::nothrow) RichText();
    if (widget && widget->init())
    {
        widget->autorelease();
        return widget;
    }
    CC_SAFE_DELETE(widget);
    return nullptr;
}

RichText* RichText::createWithXML(const std::string& xml, const ValueMap& defaults, const OpenUrlHandler& handleOpenUrl)
{
    RichText* widget = new (std::nothrow) RichText();
    if (widget && widget->initWithXML(xml, defaults, handleOpenUrl))
    {
        widget->autorelease();
        return widget;
    }
    CC_SAFE_DELETE(widget);
    return nullptr;
}

bool RichText::init()
{
    if (Widget::init())
    {
        return true;
    }
    return false;
}

bool RichText::initWithXML(const std::string& origxml, const ValueMap& defaults, const OpenUrlHandler& handleOpenUrl)
{
    static std::function<std::string(RichText*)> startTagFont = [](RichText* richText) {
        std::string fontFace = richText->getFontFace();
        std::stringstream ss;
        ss << richText->getFontSize();
        std::string fontSize = ss.str();
        std::string fontColor = richText->getFontColor();
        return "<font face=\"" + fontFace + "\" size=\"" + fontSize + "\" color=\"" + fontColor + "\">";
    };
    if (Widget::init())
    {
        setDefaults(defaults);
        setOpenUrlHandler(handleOpenUrl);

        // solves to issues:
        //  - creates defaults values
        //  - makes sure that the xml well formed and starts with an element
        std::string xml = startTagFont(this);
        xml += origxml;
        xml += "</font>";

        MyXMLVisitor visitor(this);
        SAXParser parser;
        parser.setDelegator(&visitor);
        return parser.parseIntrusive(&xml.front(), xml.length());
    }
    return false;
}

void RichText::initRenderer()
{
}

void RichText::insertElement(RichElement *element, int index)
{
    _richElements.insert(index, element);
    _formatTextDirty = true;
}

void RichText::pushBackElement(RichElement *element)
{
    _richElements.pushBack(element);
    _formatTextDirty = true;
}

void RichText::removeElement(int index)
{
    _richElements.erase(index);
    _formatTextDirty = true;
}

void RichText::removeElement(RichElement *element)
{
    _richElements.eraseObject(element);
    _formatTextDirty = true;
}

RichText::WrapMode RichText::getWrapMode() const
{
    return static_cast<RichText::WrapMode>(_defaults.at(KEY_WRAP_MODE).asInt());
}

void RichText::setWrapMode(RichText::WrapMode wrapMode)
{
    if (static_cast<RichText::WrapMode>(_defaults.at(KEY_WRAP_MODE).asInt()) != wrapMode)
    {
        _defaults[KEY_WRAP_MODE] = static_cast<int>(wrapMode);
        _formatTextDirty = true;
    }
}

void RichText::setFontColor(const std::string& color)
{
    _defaults[KEY_FONT_COLOR_STRING] = color;
}

std::string RichText::getFontColor()
{
    return _defaults.at(KEY_FONT_COLOR_STRING).asString();
}

cocos2d::Color3B RichText::getFontColor3B()
{
    return color3BWithString(getFontColor());
}

void RichText::setFontSize(float size)
{
    _defaults[KEY_FONT_SIZE] = size;
}

float RichText::getFontSize()
{
    return _defaults.at(KEY_FONT_SIZE).asFloat();
}

void RichText::setFontFace(const std::string& face)
{
    _defaults[KEY_FONT_FACE] = face;
}

std::string RichText::getFontFace()
{
    return _defaults.at(KEY_FONT_FACE).asString();
}

void RichText::setAnchorFontColor(const std::string& color)
{
    _defaults[KEY_ANCHOR_FONT_COLOR_STRING] = color;
}

std::string RichText::getAnchorFontColor()
{
    return _defaults.at(KEY_ANCHOR_FONT_COLOR_STRING).asString();
}

cocos2d::Color3B RichText::getAnchorFontColor3B()
{
    return color3BWithString(getAnchorFontColor());
}

void RichText::setAnchorTextBold(bool enable)
{
    _defaults[KEY_ANCHOR_TEXT_BOLD] = enable;
}

bool RichText::isAnchorTextBoldEnabled()
{
    return _defaults[KEY_ANCHOR_TEXT_BOLD].asBool();
}

void RichText::setAnchorTextItalic(bool enable)
{
    _defaults[KEY_ANCHOR_TEXT_ITALIC] = enable;
}

bool RichText::isAnchorTextItalicEnabled()
{
    return _defaults[KEY_ANCHOR_TEXT_ITALIC].asBool();
}

void RichText::setAnchorTextDel(bool enable)
{
    if (enable)
        _defaults[KEY_ANCHOR_TEXT_LINE] = VALUE_TEXT_LINE_DEL;
    else if (_defaults[KEY_ANCHOR_TEXT_LINE].asString() == VALUE_TEXT_LINE_DEL)
        _defaults[KEY_ANCHOR_TEXT_LINE] = VALUE_TEXT_LINE_NONE;
}

bool RichText::isAnchorTextDelEnabled()
{
    return (_defaults[KEY_ANCHOR_TEXT_LINE].asString() == VALUE_TEXT_LINE_DEL);
}

void RichText::setAnchorTextUnderline(bool enable)
{
    if (enable)
        _defaults[KEY_ANCHOR_TEXT_LINE] = VALUE_TEXT_LINE_UNDER;
    else if (_defaults[KEY_ANCHOR_TEXT_LINE].asString() == VALUE_TEXT_LINE_UNDER)
        _defaults[KEY_ANCHOR_TEXT_LINE] = VALUE_TEXT_LINE_NONE;
}

bool RichText::isAnchorTextUnderlineEnabled()
{
    return (_defaults[KEY_ANCHOR_TEXT_LINE].asString() == VALUE_TEXT_LINE_UNDER);
}

void RichText::setAnchorTextOutline(bool enable, const Color3B& outlineColor, int outlineSize)
{
    if (enable)
        _defaults[KEY_ANCHOR_TEXT_STYLE] = VALUE_TEXT_STYLE_OUTLINE;
    else if (_defaults[KEY_ANCHOR_TEXT_STYLE].asString() == VALUE_TEXT_STYLE_OUTLINE)
        _defaults[KEY_ANCHOR_TEXT_STYLE] = VALUE_TEXT_STYLE_NONE;
    _defaults[KEY_ANCHOR_TEXT_OUTLINE_COLOR] = stringWithColor3B(outlineColor);
    _defaults[KEY_ANCHOR_TEXT_OUTLINE_SIZE] = outlineSize;
}

bool RichText::isAnchorTextOutlineEnabled()
{
    return (_defaults[KEY_ANCHOR_TEXT_STYLE].asString() == VALUE_TEXT_STYLE_OUTLINE);
}

Color3B RichText::getAnchorTextOutlineColor3B()
{
    if (_defaults.find(KEY_ANCHOR_TEXT_OUTLINE_COLOR) != _defaults.end()) {
        return color3BWithString(_defaults.at(KEY_ANCHOR_TEXT_OUTLINE_COLOR).asString());
    }
    return Color3B();
}

int RichText::getAnchorTextOutlineSize()
{
    if (_defaults.find(KEY_ANCHOR_TEXT_OUTLINE_SIZE) != _defaults.end()) {
        return _defaults.at(KEY_ANCHOR_TEXT_OUTLINE_SIZE).asInt();
    }
    return -1;
}

void RichText::setAnchorTextShadow(bool enable, const Color3B& shadowColor, const Size& offset, int blurRadius)
{
    if (enable)
        _defaults[KEY_ANCHOR_TEXT_STYLE] = VALUE_TEXT_STYLE_SHADOW;
    else if (_defaults[KEY_ANCHOR_TEXT_STYLE].asString() == VALUE_TEXT_STYLE_SHADOW)
        _defaults[KEY_ANCHOR_TEXT_STYLE] = VALUE_TEXT_STYLE_NONE;
    _defaults[KEY_ANCHOR_TEXT_SHADOW_COLOR] = stringWithColor3B(shadowColor);
    _defaults[KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH] = offset.width;
    _defaults[KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT] = offset.height;
    _defaults[KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS] = blurRadius;
}

bool RichText::isAnchorTextShadowEnabled()
{
    return (_defaults[KEY_ANCHOR_TEXT_STYLE].asString() == VALUE_TEXT_STYLE_SHADOW);
}

Color3B RichText::getAnchorTextShadowColor3B()
{
    if (_defaults.find(KEY_ANCHOR_TEXT_SHADOW_COLOR) != _defaults.end()) {
        return color3BWithString(_defaults.at(KEY_ANCHOR_TEXT_SHADOW_COLOR).asString());
    }
    return Color3B();
}

Size RichText::getAnchorTextShadowOffset()
{
    float width = 2.0f;
    float height = -2.0f;
    if (_defaults.find(KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH) != _defaults.end()) {
        width = _defaults.at(KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH).asFloat();
    }
    if (_defaults.find(KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT) != _defaults.end()) {
        height = _defaults.at(KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT).asFloat();
    }
    return Size(width, height);
}

int RichText::getAnchorTextShadowBlurRadius()
{
    if (_defaults.find(KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS) != _defaults.end()) {
        return _defaults.at(KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS).asInt();
    }
    return 0;
}

void RichText::setAnchorTextGlow(bool enable, const Color3B& glowColor)
{
    if (enable)
        _defaults[KEY_ANCHOR_TEXT_STYLE] = VALUE_TEXT_STYLE_GLOW;
    else if (_defaults[KEY_ANCHOR_TEXT_STYLE].asString() == VALUE_TEXT_STYLE_GLOW)
        _defaults[KEY_ANCHOR_TEXT_STYLE] = VALUE_TEXT_STYLE_NONE;
    _defaults[KEY_ANCHOR_TEXT_GLOW_COLOR] = stringWithColor3B(glowColor);
}

bool RichText::isAnchorTextGlowEnabled()
{
    return (_defaults[KEY_ANCHOR_TEXT_STYLE].asString() == VALUE_TEXT_STYLE_GLOW);
}

Color3B RichText::getAnchorTextGlowColor3B()
{
    if (_defaults.find(KEY_ANCHOR_TEXT_GLOW_COLOR) != _defaults.end()) {
        return color3BWithString(_defaults.at(KEY_ANCHOR_TEXT_GLOW_COLOR).asString());
    }
    return Color3B();
}

void RichText::setDefaults(const ValueMap& defaults)
{
    if (defaults.find(KEY_VERTICAL_SPACE) != defaults.end()) {
        _defaults[KEY_VERTICAL_SPACE] = defaults.at(KEY_VERTICAL_SPACE).asFloat();
    }
    if (defaults.find(KEY_WRAP_MODE) != defaults.end()) {
        _defaults[KEY_WRAP_MODE] = defaults.at(KEY_WRAP_MODE).asInt();
    }
    if (defaults.find(KEY_FONT_COLOR_STRING) != defaults.end()) {
        _defaults[KEY_FONT_COLOR_STRING] = defaults.at(KEY_FONT_COLOR_STRING).asString();
    }
    if (defaults.find(KEY_FONT_SIZE) != defaults.end()) {
        _defaults[KEY_FONT_SIZE] = defaults.at(KEY_FONT_SIZE).asFloat();
    }
    if (defaults.find(KEY_FONT_FACE) != defaults.end()) {
        _defaults[KEY_FONT_FACE] = defaults.at(KEY_FONT_FACE).asString();
    }
    if (defaults.find(KEY_ANCHOR_FONT_COLOR_STRING) != defaults.end()) {
        _defaults[KEY_ANCHOR_FONT_COLOR_STRING] = defaults.at(KEY_ANCHOR_FONT_COLOR_STRING).asString();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_BOLD) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_BOLD] = defaults.at(KEY_ANCHOR_TEXT_BOLD).asBool();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_ITALIC) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_ITALIC] = defaults.at(KEY_ANCHOR_TEXT_ITALIC).asBool();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_LINE) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_LINE] = defaults.at(KEY_ANCHOR_TEXT_LINE).asString();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_STYLE) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_STYLE] = defaults.at(KEY_ANCHOR_TEXT_STYLE).asString();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_OUTLINE_COLOR) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_OUTLINE_COLOR] = defaults.at(KEY_ANCHOR_TEXT_OUTLINE_COLOR).asString();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_OUTLINE_SIZE) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_OUTLINE_SIZE] = defaults.at(KEY_ANCHOR_TEXT_OUTLINE_SIZE).asInt();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_SHADOW_COLOR) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_SHADOW_COLOR] = defaults.at(KEY_ANCHOR_TEXT_SHADOW_COLOR).asString();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH] = defaults.at(KEY_ANCHOR_TEXT_SHADOW_OFFSET_WIDTH).asFloat();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT] = defaults.at(KEY_ANCHOR_TEXT_SHADOW_OFFSET_HEIGHT).asFloat();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS] = defaults.at(KEY_ANCHOR_TEXT_SHADOW_BLUR_RADIUS).asInt();
    }
    if (defaults.find(KEY_ANCHOR_TEXT_GLOW_COLOR) != defaults.end()) {
        _defaults[KEY_ANCHOR_TEXT_GLOW_COLOR] = defaults.at(KEY_ANCHOR_TEXT_GLOW_COLOR).asString();
    }
}

ValueMap RichText::getDefaults() const
{
    ValueMap defaults;
    return defaults;
}

cocos2d::Color3B RichText::color3BWithString(const std::string& color)
{
    if (color.length() == 4) {
        int r, g, b;
        sscanf(color.c_str(), "%*c%1x%1x%1x", &r, &g, &b);
        r += r * 16;
        g += g * 16;
        b += b * 16;
        return Color3B(r, g, b);
    }
    else if (color.length() == 7) {
        int r, g, b;
        sscanf(color.c_str(), "%*c%2x%2x%2x", &r, &g, &b);
        return Color3B(r, g, b);
    }
    else if (color.length() == 9) {
        int r, g, b, a;
        sscanf(color.c_str(), "%*c%2x%2x%2x%2x", &r, &g, &b, &a);
        return Color3B(r, g, b);
    }
    return Color3B::WHITE;
}

std::string RichText::stringWithColor3B(const cocos2d::Color3B& color3b)
{
    int r = color3b.r;
    int g = color3b.g;
    int b = color3b.b;
    char buf[8];
    snprintf(buf, sizeof(buf), "#%02x%02x%02x", r, g, b);
    return std::string(buf, 7);
}

std::string RichText::stringWithColor4B(const cocos2d::Color4B& color4b)
{
    int r = color4b.r;
    int g = color4b.g;
    int b = color4b.b;
    int a = color4b.a;
    char buf[10];
    snprintf(buf, sizeof(buf), "#%02x%02x%02x%02x", r, g, b, a);
    return std::string(buf, 9);
}

void RichText::setTagDescription(const std::string& tag, bool isFontElement, VisitEnterHandler handleVisitEnter)
{
    MyXMLVisitor::setTagDescription(tag, isFontElement, handleVisitEnter);
}

void RichText::removeTagDescription(const std::string& tag)
{
    MyXMLVisitor::removeTagDescription(tag);
}

void RichText::openUrl(const std::string& url)
{
    if (_handleOpenUrl) {
        _handleOpenUrl(url);
    }
    else {
        Application::getInstance()->openURL(url);
    }
}

void RichText::setOpenUrlHandler(const OpenUrlHandler& handleOpenUrl)
{
    _handleOpenUrl = handleOpenUrl;
}

void RichText::formatText()
{
    if (_formatTextDirty)
    {
        this->removeAllProtectedChildren();
        _elementRenders.clear();
        if (_ignoreSize)
        {
            addNewLine();
            for (ssize_t i=0, size = _richElements.size(); i<size; ++i)
            {
                RichElement* element = _richElements.at(i);
                Node* elementRenderer = nullptr;
                switch (element->_type)
                {
                    case RichElement::Type::TEXT:
                    {
                        RichElementText* elmtText = static_cast<RichElementText*>(element);
                        Label* label;
                        if (FileUtils::getInstance()->isFileExist(elmtText->_fontName))
                        {
                             label = Label::createWithTTF(elmtText->_text, elmtText->_fontName, elmtText->_fontSize);
                        }
                        else
                        {
                            label = Label::createWithSystemFont(elmtText->_text, elmtText->_fontName, elmtText->_fontSize);
                        }
                        if (elmtText->_flags & RichElementText::ITALICS_FLAG)
                            label->enableItalics();
                        if (elmtText->_flags & RichElementText::BOLD_FLAG)
                            label->enableBold();
                        if (elmtText->_flags & RichElementText::UNDERLINE_FLAG)
                            label->enableUnderline();
                        if (elmtText->_flags & RichElementText::STRIKETHROUGH_FLAG)
                            label->enableStrikethrough();
                        if (elmtText->_flags & RichElementText::URL_FLAG)
                            label->addComponent(ListenerComponent::create(label, elmtText->_url,
                                                                          std::bind(&RichText::openUrl, this, std::placeholders::_1)));
                        if (elmtText->_flags & RichElementText::OUTLINE_FLAG) {
                            label->enableOutline(Color4B(elmtText->_outlineColor), elmtText->_outlineSize);
                        }
                        if (elmtText->_flags & RichElementText::SHADOW_FLAG) {
                            label->enableShadow(Color4B(elmtText->_shadowColor),
                                                elmtText->_shadowOffset,
                                                elmtText->_shadowBlurRadius);
                        }
                        if (elmtText->_flags & RichElementText::GLOW_FLAG) {
                            label->enableGlow(Color4B(elmtText->_glowColor));
                        }
                        elementRenderer = label;
                        break;
                    }
                    case RichElement::Type::IMAGE:
                    {
                        RichElementImage* elmtImage = static_cast<RichElementImage*>(element);
                        elementRenderer = Sprite::create(elmtImage->_filePath);
                        if (elementRenderer && (elmtImage->_height != -1 || elmtImage->_width != -1))
                        {
                            auto currentSize = elementRenderer->getContentSize();
                            if (elmtImage->_width != -1)
                                elementRenderer->setScaleX(elmtImage->_width / currentSize.width);
                            if (elmtImage->_height != -1)
                                elementRenderer->setScaleY(elmtImage->_height / currentSize.height);
                            elementRenderer->setContentSize(Size(currentSize.width * elementRenderer->getScaleX(),
                                                                 currentSize.height * elementRenderer->getScaleY()));
                            elementRenderer->addComponent(ListenerComponent::create(elementRenderer,
                                                                                elmtImage->_url,
                                                                                std::bind(&RichText::openUrl, this, std::placeholders::_1)));
                        }
                        break;
                    }
                    case RichElement::Type::CUSTOM:
                    {
                        RichElementCustomNode* elmtCustom = static_cast<RichElementCustomNode*>(element);
                        elementRenderer = elmtCustom->_customNode;
                        break;
                    }
                    case RichElement::Type::NEWLINE:
                    {
                        addNewLine();
                        break;
                    }
                    default:
                        break;
                }

                if (elementRenderer)
                {
                    elementRenderer->setColor(element->_color);
                    elementRenderer->setOpacity(element->_opacity);
                    pushToContainer(elementRenderer);
                }
            }
        }
        else
        {
            addNewLine();
            for (ssize_t i=0, size = _richElements.size(); i<size; ++i)
            {
                RichElement* element = static_cast<RichElement*>(_richElements.at(i));
                switch (element->_type)
                {
                    case RichElement::Type::TEXT:
                    {
                        RichElementText* elmtText = static_cast<RichElementText*>(element);
                        handleTextRenderer(elmtText->_text, elmtText->_fontName, elmtText->_fontSize, elmtText->_color,
                                           elmtText->_opacity, elmtText->_flags, elmtText->_url,
                                           elmtText->_outlineColor, elmtText->_outlineSize,
                                           elmtText->_shadowColor, elmtText->_shadowOffset, elmtText->_shadowBlurRadius,
                                           elmtText->_glowColor);
                        break;
                    }
                    case RichElement::Type::IMAGE:
                    {
                        RichElementImage* elmtImage = static_cast<RichElementImage*>(element);
                        handleImageRenderer(elmtImage->_filePath, elmtImage->_color, elmtImage->_opacity, elmtImage->_width, elmtImage->_height, elmtImage->_url);
                        break;
                    }
                    case RichElement::Type::CUSTOM:
                    {
                        RichElementCustomNode* elmtCustom = static_cast<RichElementCustomNode*>(element);
                        handleCustomRenderer(elmtCustom->_customNode);
                        break;
                    }
                    case RichElement::Type::NEWLINE:
                    {
                        addNewLine();
                        break;
                    }
                    default:
                        break;
                }
            }
        }
        formarRenderers();
        _formatTextDirty = false;
    }
}

static int getPrevWord(const std::string& text, int idx)
{
    // start from idx-1
    for (int i=idx-1; i>=0; --i)
    {
		if (!std::isalnum(text[i], std::locale()))
            return i;
    }
    return -1;
}

static bool isWrappable(const std::string& text)
{
    for (size_t i = 0, size = text.length(); i < size; ++i)
    {
        if (!std::isalnum(text[i], std::locale()))
            return true;
    }
    return false;
}

int RichText::findSplitPositionForWord(cocos2d::Label* label, const std::string& text)
{
    auto originalLeftSpaceWidth = _leftSpaceWidth + label->getContentSize().width;

    bool startingNewLine = (_customSize.width == originalLeftSpaceWidth);
    if (!isWrappable(text))
    {
        if (startingNewLine)
            return (int) text.length();
        return 0;
    }

    for(int idx = (int)text.size()-1; idx >=0; )
    {
        int newidx = getPrevWord(text, idx);
        if (newidx >=0)
        {
            idx = newidx;
            auto leftStr = Helper::getSubStringOfUTF8String(text, 0, idx);
            label->setString(leftStr);
            if (label->getContentSize().width <= originalLeftSpaceWidth)
                return idx;
        }
        else
        {
            if (startingNewLine)
                return idx;
            return 0;
        }
    }

    // no spaces... return the original label + size
    label->setString(text);
    return (int)text.size();
}

int RichText::findSplitPositionForChar(cocos2d::Label* label, const std::string& text)
{
    float textRendererWidth = label->getContentSize().width;

    float overstepPercent = (-_leftSpaceWidth) / textRendererWidth;
    std::string curText = text;
    size_t stringLength = StringUtils::getCharacterCountInUTF8String(text);

    // rough estimate
    int leftLength = stringLength * (1.0f - overstepPercent);

    // The adjustment of the new line position
    auto originalLeftSpaceWidth = _leftSpaceWidth + textRendererWidth;
    auto leftStr = Helper::getSubStringOfUTF8String(curText, 0, leftLength);
    label->setString(leftStr);
    auto leftWidth = label->getContentSize().width;
    if (originalLeftSpaceWidth < leftWidth) {
        // Have protruding
        for (;;) {
            leftLength--;
            leftStr = Helper::getSubStringOfUTF8String(curText, 0, leftLength);
            label->setString(leftStr);
            leftWidth = label->getContentSize().width;
            if (leftWidth <= originalLeftSpaceWidth) {
                break;
            }
            else if (leftLength <= 0) {
                break;
            }
        }
    }
    else if (leftWidth < originalLeftSpaceWidth) {
        // A wide margin
        for (;;) {
            leftLength++;
            leftStr = Helper::getSubStringOfUTF8String(curText, 0, leftLength);
            label->setString(leftStr);
            leftWidth = label->getContentSize().width;
            if (originalLeftSpaceWidth < leftWidth) {
                leftLength--;
                break;
            }
            else if (static_cast<int>(stringLength) <= leftLength) {
                break;
            }
        }
    }

    if (leftLength < 0)
        leftLength = (int)text.size()-1;
    return leftLength;
}

void RichText::handleTextRenderer(const std::string& text, const std::string& fontName, float fontSize, const Color3B &color,
                                  GLubyte opacity, uint32_t flags, const std::string& url,
                                  const Color3B& outlineColor, int outlineSize ,
                                  const Color3B& shadowColor, const cocos2d::Size& shadowOffset, int shadowBlurRadius,
                                  const Color3B& glowColor)
{
    auto fileExist = FileUtils::getInstance()->isFileExist(fontName);
    Label* textRenderer = nullptr;
    if (fileExist)
    {
        textRenderer = Label::createWithTTF(text, fontName, fontSize);
    }
    else
    {
        textRenderer = Label::createWithSystemFont(text, fontName, fontSize);
    }
    if (flags & RichElementText::ITALICS_FLAG)
        textRenderer->enableItalics();
    if (flags & RichElementText::BOLD_FLAG)
        textRenderer->enableBold();
    if (flags & RichElementText::UNDERLINE_FLAG)
        textRenderer->enableUnderline();
    if (flags & RichElementText::STRIKETHROUGH_FLAG)
        textRenderer->enableStrikethrough();
    if (flags & RichElementText::URL_FLAG)
        textRenderer->addComponent(ListenerComponent::create(textRenderer,
                                                             url,
                                                             std::bind(&RichText::openUrl, this, std::placeholders::_1)));
    if (flags & RichElementText::OUTLINE_FLAG) {
        textRenderer->enableOutline(Color4B(outlineColor), outlineSize);
    }
    if (flags & RichElementText::SHADOW_FLAG) {
        textRenderer->enableShadow(Color4B(shadowColor), shadowOffset, shadowBlurRadius);
    }
    if (flags & RichElementText::GLOW_FLAG) {
        textRenderer->enableGlow(Color4B(glowColor));
    }

    float textRendererWidth = textRenderer->getContentSize().width;
    _leftSpaceWidth -= textRendererWidth;
    if (_leftSpaceWidth < 0.0f)
    {
        int leftLength = 0;
        if (static_cast<RichText::WrapMode>(_defaults.at(KEY_WRAP_MODE).asInt()) == WRAP_PER_WORD)
            leftLength = findSplitPositionForWord(textRenderer, text);
        else
            leftLength = findSplitPositionForChar(textRenderer, text);

        //The minimum cut length is 1, otherwise will cause the infinite loop.
//        if (0 == leftLength) leftLength = 1;
        std::string leftWords = Helper::getSubStringOfUTF8String(text, 0, leftLength);
        int rightStart = leftLength;
        if (std::isspace(text[rightStart], std::locale()))
            rightStart++;
        std::string cutWords = Helper::getSubStringOfUTF8String(text, rightStart, text.length() - leftLength);
        if (leftLength > 0)
        {
            Label* leftRenderer = nullptr;
            if (fileExist)
            {
                leftRenderer = Label::createWithTTF(Helper::getSubStringOfUTF8String(leftWords, 0, leftLength), fontName, fontSize);
            }
            else
            {
                leftRenderer = Label::createWithSystemFont(Helper::getSubStringOfUTF8String(leftWords, 0, leftLength), fontName, fontSize);
            }
            if (leftRenderer)
            {
                leftRenderer->setColor(color);
                leftRenderer->setOpacity(opacity);
                pushToContainer(leftRenderer);

                if (flags & RichElementText::ITALICS_FLAG)
                    leftRenderer->enableItalics();
                if (flags & RichElementText::BOLD_FLAG)
                    leftRenderer->enableBold();
                if (flags & RichElementText::UNDERLINE_FLAG)
                    leftRenderer->enableUnderline();
                if (flags & RichElementText::STRIKETHROUGH_FLAG)
                    leftRenderer->enableStrikethrough();
                if (flags & RichElementText::URL_FLAG)
                    leftRenderer->addComponent(ListenerComponent::create(leftRenderer,
                                                                         url,
                                                                         std::bind(&RichText::openUrl, this, std::placeholders::_1)));
                if (flags & RichElementText::OUTLINE_FLAG) {
                    leftRenderer->enableOutline(Color4B(outlineColor), outlineSize);
                }
                if (flags & RichElementText::SHADOW_FLAG) {
                    leftRenderer->enableShadow(Color4B(shadowColor), shadowOffset, shadowBlurRadius);
                }
                if (flags & RichElementText::GLOW_FLAG) {
                    leftRenderer->enableGlow(Color4B(glowColor));
                }
            }
        }

        addNewLine();
        handleTextRenderer(cutWords, fontName, fontSize, color, opacity, flags, url,
                           outlineColor, outlineSize,
                           shadowColor, shadowOffset, shadowBlurRadius,
                           glowColor);
    }
    else
    {
        textRenderer->setColor(color);
        textRenderer->setOpacity(opacity);
        pushToContainer(textRenderer);
    }
}

void RichText::handleImageRenderer(const std::string& filePath, const Color3B &/*color*/, GLubyte /*opacity*/, int width, int height, const std::string& url)
{
    Sprite* imageRenderer = Sprite::create(filePath);
    if (imageRenderer)
    {
        auto currentSize = imageRenderer->getContentSize();
        if (width != -1)
            imageRenderer->setScaleX(width / currentSize.width);
        if (height != -1)
            imageRenderer->setScaleY(height / currentSize.height);
        imageRenderer->setContentSize(Size(currentSize.width * imageRenderer->getScaleX(),
                                             currentSize.height * imageRenderer->getScaleY()));

        handleCustomRenderer(imageRenderer);
        imageRenderer->addComponent(ListenerComponent::create(imageRenderer,
                                                              url,
                                                              std::bind(&RichText::openUrl, this, std::placeholders::_1)));
    }
}

void RichText::handleCustomRenderer(cocos2d::Node *renderer)
{
    Size imgSize = renderer->getContentSize();
    _leftSpaceWidth -= imgSize.width;
    if (_leftSpaceWidth < 0.0f)
    {
        addNewLine();
        pushToContainer(renderer);
        _leftSpaceWidth -= imgSize.width;
    }
    else
    {
        pushToContainer(renderer);
    }
}

void RichText::addNewLine()
{
    _leftSpaceWidth = _customSize.width;
    _elementRenders.push_back(new Vector<Node*>());
}

void RichText::formarRenderers()
{
    if (_ignoreSize)
    {
        float newContentSizeWidth = 0.0f;
        float nextPosY = 0.0f;
        for (auto& element: _elementRenders)
        {
            Vector<Node*>* row = element;
            float nextPosX = 0.0f;
            float maxY = 0.0f;
            for (ssize_t j=0, size = row->size(); j<size; j++)
            {
                Node* l = row->at(j);
                l->setAnchorPoint(Vec2::ZERO);
                l->setPosition(nextPosX, nextPosY);
                this->addProtectedChild(l, 1);
                Size iSize = l->getContentSize();
                newContentSizeWidth += iSize.width;
                nextPosX += iSize.width;
                maxY = MAX(maxY, iSize.height);
            }
            nextPosY -= maxY;
        }
        this->setContentSize(Size(newContentSizeWidth, -nextPosY));
    }
    else
    {
        float newContentSizeHeight = 0.0f;
        float *maxHeights = new (std::nothrow) float[_elementRenders.size()];

        for (size_t i=0, size = _elementRenders.size(); i<size; i++)
        {
            Vector<Node*>* row = (_elementRenders[i]);
            float maxHeight = 0.0f;
            for (ssize_t j=0, size = row->size(); j<size; j++)
            {
                Node* l = row->at(j);
                maxHeight = MAX(l->getContentSize().height, maxHeight);
            }
            maxHeights[i] = maxHeight;
            newContentSizeHeight += maxHeights[i];
        }

        float nextPosY = _customSize.height;
        for (size_t i=0, size = _elementRenders.size(); i<size; i++)
        {
            Vector<Node*>* row = (_elementRenders[i]);
            float nextPosX = 0.0f;
            nextPosY -= (maxHeights[i] + _defaults.at(KEY_VERTICAL_SPACE).asFloat());

            for (ssize_t j=0, size = row->size(); j<size; j++)
            {
                Node* l = row->at(j);
                l->setAnchorPoint(Vec2::ZERO);
                l->setPosition(nextPosX, nextPosY);
                this->addProtectedChild(l, 1);
                nextPosX += l->getContentSize().width;
            }
        }
        delete [] maxHeights;
    }

    size_t length = _elementRenders.size();
    for (size_t i = 0; i<length; i++)
	{
        Vector<Node*>* l = _elementRenders[i];
        l->clear();
        delete l;
	}
    _elementRenders.clear();

    if (_ignoreSize)
    {
        Size s = getVirtualRendererSize();
        this->setContentSize(s);
    }
    else
    {
        this->setContentSize(_customSize);
    }
    updateContentSizeWithTextureSize(_contentSize);
}

void RichText::adaptRenderers()
{
    this->formatText();
}

void RichText::pushToContainer(cocos2d::Node *renderer)
{
    if (_elementRenders.size() <= 0)
    {
        return;
    }
    _elementRenders[_elementRenders.size()-1]->pushBack(renderer);
}

void RichText::setVerticalSpace(float space)
{
    _defaults[KEY_VERTICAL_SPACE] = space;
}

void RichText::ignoreContentAdaptWithSize(bool ignore)
{
    if (_ignoreSize != ignore)
    {
        _formatTextDirty = true;
        Widget::ignoreContentAdaptWithSize(ignore);
    }
}

std::string RichText::getDescription() const
{
    return "RichText";
}

redpajama_set_name: RedPajamaGithub


text:
Histochemistry
1. Infrared Light-Emitting Diode (LED) Effects on Orthodontic Tooth Movement
Resumo O presente estudo tem como objetivo analisar o efeito da fototerapia LED na presença da hialinização e reabsorção radicular durante o movimento dentário ortodôntico (MDO) em ratos, e a mensuração da quantidade de movimento dentário. Oitenta ratos foram alocados em dois grupos: LED e Controle (CON), os ratos foram irradiados com um LED infrav
Braz. Dent. J.. Publicado em: 22/07/2019
2. Comparative anatomy and histochemistry of the leaf blade of two species of Artocarpus
Abstract Abstract: In Brazil, there are two species of Artocarpus that were introduced: Artocarpus altilis (Parkinson) Fosberg, known as fruta-pão, and Artocarpus heterophyllus Lam., known as jaca. Both are used as food and medicine. The objective of this work was to conduct a comparative anatomical and histochemical study between A. altilis and A. heteroph
An. Acad. Bras. Ciênc.. Publicado em: 14/02/2019
3. Anatomy, histochemistry and oxalic acid content of the leaflets of Averrhoa bilimbi and Averrhoa carambola
Abstract Averrhoa bilimbi L. and A. carambola L., Oxalidaceae, are the only two species of the genus Averrhoa L. Their leaves are widely used in folk medicine as an adjuvant in the treatment of diabetes. Some species may contain, for example, calcium oxalate crystals, which may lead to risk in its use when there is predisposition of individuals with reduced
Rev. bras. farmacogn.. Publicado em: 2019-02
4. Cytotoxic effects of Euterpe oleraceae fruit oil (açaí) in rat liver and thyroid tissues
Abstract Euterpe oleracea Mart., Arecaceae, fruit (açaí) presents considerable potential for the development of new medicines due to its phytochemical composition and antioxidant activity. More recently, special attention has been given to the pharmacological potential of the fruit's oil. This study analysed the histological and histochemical effects of di
5. What are tilosomes? An update and new perspectives
ABSTRACT Characterization of tilosomes and study of their development revealed that they are similar to the cell wall ingrowths of transfer cells. Roots from Anathallis sclerophylla were used to examine the development, histochemistry and ultrastructure of tilosomes using light, confocal and electron microscopy (both SEM and TEM). Tilosomes initiate as cellu
Acta Bot. Bras.. Publicado em: 29/11/2018
6. Laparoscopic approach to pheochromocytoma in pregnancy: case report
ABSTRACT A 32-year-old 22-week pregnant hypertensive woman with sporadic episodes of headaches, sweating, and facial flushing was diagnosed with pheochromocytoma through biochemical and imaging tests. Perioperative management included a multidisciplinary approach, symptom stabilization with α blockade followed by β blockade, and tumor resection by laparosc
Int. braz j urol.. Publicado em: 2018-06
7. Morpho-anatomy of native species used as substitute of quina (Cinchona spp.) in Brazilian traditional medicine: Esenbeckia febrifuga
ABSTRACT Esenbeckia febrifuga (A.St.-Hil.) A. Juss. ex Mart., Rutaceae, is known by several popular names including quina-do-mato. This name is a reference to the use of its bark as febrifuge and in the past was employed as a substitute of Cinchona sp. for treatment of malaria symptoms. This confusion may have been reinforced by the fact that the bark of the
8. Anatomy and volatile oil chemistry of Eucalyptus saligna cultivated in South Brazil
ABSTRACT Eucalyptus saligna Sm., Myrtaceae, commonly known as Sydney blue gum, is often confused with several other species in the genus. The leaf volatile oils of the species have been reported to have antimicrobial, insecticidal, nematicidal, repellent and cytotoxicity properties. The present work provides anatomy as well as volatile oil chemistry of the s
9. Nutritious tissue in petals of Annonaceae and its function in pollination by scarab beetles
ABSTRACT The feeding of pollinating dynastid-scarab beetles on nutritious tissue of Annonaceae flowers results in macroscopically visible gnawing marks on petals. In the present paper, we present and discuss examples of such gnawing marks on Annonaceae from the Cerrado and the Amazon Forest in Brazil. The localization of gnawing marks on the petals and the h
10. Characterization of air-liquid interface culture of A549 alveolar epithelial cells
Alveolar epithelia play an essential role in maintaining the integrity and homeostasis of lungs, in which alveolar epithelial type II cells (AECII) are a cell type with stem cell potential for epithelial injury repair and regeneration. However, mechanisms behind the physiological and pathological roles of alveolar epithelia in human lungs remain largely unkn
Braz J Med Biol Res. Publicado em: 11/12/2017
11. Anatomical characterization of ultra-structures, biominerals and histolocalization of metabolites in leaves of Genipa americana
Abstract Inserted in one of the largest families among the Angiosperms, Genipa americana L., Rubiaceae, can be found in all Brazilian territory, presenting great medicinal importance, where several uses have been attributed. In view of this, this work has the purpose of analyzing the ultrastructural, biomineral, phytochemical and histochemical characteristic
12. Corona development and floral nectaries of Asclepiadeae (Asclepiadoideae, Apocynaceae)
ABSTRACT Flowers of Asclepiadoideae are notable for possessing numerous nectaries and elaborate coronas, where nectar can accumulate but is not necessarily produced. Given the complexity and importance of these structures for reproduction, this study aimed to analyze the ontogeny of the corona, the structure and position of nectaries and the histochemistry o
Acta Bot. Bras.. Publicado em: 2017-09

redpajama_set_name: RedPajamaCommonCrawl


text:
Cpsc Jetmax International Ltd Announce Recall To Repair Childrens Rocking Chairs Sold
CPSC, Jetmax International Ltd. Announce Recall to Repair Children's Rocking Chairs Sold at Target Stores
Release # 00-125
Jetmax Recall Hotline: (800) 880-0714
WASHINGTON, D.C. - In cooperation with the U.S. Consumer Product Safety Commission (CPSC), Jetmax International Ltd., of Stamford, Conn., is voluntarily recalling to repair about 48,000 children's rocking chairs. The rear legs of the chair can separate from the rocker's base causing it to collapse, and the child seated in the chair can fall.
Jetmax has received five reports of these chairs collapsing and children falling. One 13-month-old girl suffered a laceration requiring 18 stitches.
Jetmax imported the rocker exclusively for sale by Target stores. The wooden rocking chairs come in two styles: one for boys and one for girls. The boy's rocker has a yellow frame, red rockers (chair bases) and a blue canvas seat. The girl's rocker has a white frame, purple rockers (chair bases) and a pink canvas seat. There is no writing on the chairs, but the packaging reads, "restore™ & RESTYLE KIDS," "Wooden Folding Chair," "Ages 3 and Up," and "Made in China." The chairs are designed for children between 30 and 100 pounds. They are 23 inches long, 13 inches wide and 16.5 inches high.
Target stores nationwide sold the rocking chairs from November 1999 through March 2000 for about $20.
Consumers should immediately stop using these rockers, and contact Jetmax International to get a free repair kit with easy installation instructions. For more information and to get the repair kit, call Jetmax International at (800) 880-0714 between 9 a.m. and 5 p.m. ET Monday through Friday.