// Copyright 2009, Squish Tech, LLC. #include #include #include "libxmljs.h" #include "xml_text.h" #include "xml_document.h" #include "xml_attribute.h" #include "xml_xpath_context.h" namespace libxmljs { Nan::Persistent XmlText::constructor_template; // doc, name, content NAN_METHOD(XmlText::New) { Nan::HandleScope scope; // if we were created for an existing xml node, then we don't need // to create a new node on the document if (info.Length() == 0) { return info.GetReturnValue().Set(info.Holder()); } XmlDocument* document = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); assert(document); v8::Local contentOpt; if (info[1]->IsString()) { contentOpt = info[1]; } Nan::Utf8String contentRaw(contentOpt); const char* content = (contentRaw.length()) ? *contentRaw : NULL; xmlChar* encodedContent = content ? xmlEncodeSpecialChars(document->xml_obj, (const xmlChar*) content) : NULL; xmlNode* textNode = xmlNewDocText(document->xml_obj, encodedContent); if (encodedContent) { xmlFree(encodedContent); } XmlText* element = new XmlText(textNode); textNode->_private = element; element->Wrap(info.Holder()); // this prevents the document from going away Nan::Set(info.Holder(), Nan::New("document").ToLocalChecked(), info[0]); return info.GetReturnValue().Set(info.Holder()); } NAN_METHOD(XmlText::NextElement) { Nan::HandleScope scope; XmlText *element = Nan::ObjectWrap::Unwrap(info.Holder()); assert(element); return info.GetReturnValue().Set(element->get_next_element()); } NAN_METHOD(XmlText::PrevElement) { Nan::HandleScope scope; XmlText *element = Nan::ObjectWrap::Unwrap(info.Holder()); assert(element); return info.GetReturnValue().Set(element->get_prev_element()); } NAN_METHOD(XmlText::Text) { Nan::HandleScope scope; XmlText *element = Nan::ObjectWrap::Unwrap(info.Holder()); assert(element); if (info.Length() == 0) { return info.GetReturnValue().Set(element->get_content()); } else { element->set_content(*Nan::Utf8String(info[0])); } return info.GetReturnValue().Set(info.Holder()); } NAN_METHOD(XmlText::AddPrevSibling) { XmlText* text = Nan::ObjectWrap::Unwrap(info.Holder()); assert(text); XmlNode* new_sibling = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); assert(new_sibling); xmlNode *imported_sibling = text->import_node(new_sibling->xml_obj); if (imported_sibling == NULL) { return Nan::ThrowError("Could not add sibling. Failed to copy node to new Document."); } else if ((new_sibling->xml_obj == imported_sibling) && text->prev_sibling_will_merge(imported_sibling)) { imported_sibling = xmlCopyNode(imported_sibling, 0); } text->add_prev_sibling(imported_sibling); return info.GetReturnValue().Set(info[0]); } NAN_METHOD(XmlText::AddNextSibling) { XmlText* text = Nan::ObjectWrap::Unwrap(info.Holder()); assert(text); XmlNode* new_sibling = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); assert(new_sibling); xmlNode *imported_sibling = text->import_node(new_sibling->xml_obj); if (imported_sibling == NULL) { return Nan::ThrowError("Could not add sibling. Failed to copy node to new Document."); } else if ((new_sibling->xml_obj == imported_sibling) && text->next_sibling_will_merge(imported_sibling)) { imported_sibling = xmlCopyNode(imported_sibling, 0); } text->add_next_sibling(imported_sibling); return info.GetReturnValue().Set(info[0]); } NAN_METHOD(XmlText::Replace) { XmlText* element = Nan::ObjectWrap::Unwrap(info.Holder()); assert(element); if (info[0]->IsString()) { element->replace_text(*Nan::Utf8String(info[0])); } else { XmlText* new_sibling = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); assert(new_sibling); xmlNode *imported_sibling = element->import_node(new_sibling->xml_obj); if (imported_sibling == NULL) { return Nan::ThrowError("Could not replace. Failed to copy node to new Document."); } element->replace_element(imported_sibling); } return info.GetReturnValue().Set(info[0]); } void XmlText::set_content(const char* content) { xmlChar *encoded = xmlEncodeSpecialChars(xml_obj->doc, (const xmlChar*)content); xmlNodeSetContent(xml_obj, encoded); xmlFree(encoded); } v8::Local XmlText::get_content() { Nan::EscapableHandleScope scope; xmlChar* content = xmlNodeGetContent(xml_obj); if (content) { v8::Local ret_content = Nan::New((const char *)content).ToLocalChecked(); xmlFree(content); return scope.Escape(ret_content); } return scope.Escape(Nan::New("").ToLocalChecked()); } v8::Local XmlText::get_next_element() { Nan::EscapableHandleScope scope; xmlNode* sibling = xml_obj->next; if (!sibling) return scope.Escape(Nan::Null()); while (sibling && sibling->type != XML_ELEMENT_NODE) sibling = sibling->next; if (sibling) { return scope.Escape(XmlText::New(sibling)); } return scope.Escape(Nan::Null()); } v8::Local XmlText::get_prev_element() { Nan::EscapableHandleScope scope; xmlNode* sibling = xml_obj->prev; if (!sibling) return scope.Escape(Nan::Null()); while (sibling && sibling->type != XML_ELEMENT_NODE) { sibling = sibling->prev; } if (sibling) { return scope.Escape(XmlText::New(sibling)); } return scope.Escape(Nan::Null()); } v8::Local XmlText::New(xmlNode* node) { Nan::EscapableHandleScope scope; if (node->_private) { return scope.Escape(static_cast(node->_private)->handle()); } XmlText* element = new XmlText(node); v8::Local obj = Nan::NewInstance(Nan::GetFunction(Nan::New(constructor_template)).ToLocalChecked()).ToLocalChecked(); element->Wrap(obj); return scope.Escape(obj); } XmlText::XmlText(xmlNode* node) : XmlNode(node) { } void XmlText::add_prev_sibling(xmlNode* element) { xmlAddPrevSibling(xml_obj, element); } void XmlText::add_next_sibling(xmlNode* element) { xmlAddNextSibling(xml_obj, element); } void XmlText::replace_element(xmlNode* element) { xmlReplaceNode(xml_obj, element); } void XmlText::replace_text(const char* content) { xmlNodePtr txt = xmlNewDocText(xml_obj->doc, (const xmlChar*)content); xmlReplaceNode(xml_obj, txt); } bool XmlText::next_sibling_will_merge(xmlNode *child) { return (child->type == XML_TEXT_NODE); } bool XmlText::prev_sibling_will_merge(xmlNode *child) { return (child->type == XML_TEXT_NODE); } void XmlText::Initialize(v8::Local target) { Nan::HandleScope scope; v8::Local tmpl = Nan::New(New); constructor_template.Reset(tmpl); tmpl->Inherit(Nan::New(XmlNode::constructor_template)); tmpl->InstanceTemplate()->SetInternalFieldCount(1); Nan::SetPrototypeMethod(tmpl, "nextElement", XmlText::NextElement); Nan::SetPrototypeMethod(tmpl, "prevElement", XmlText::PrevElement); Nan::SetPrototypeMethod(tmpl, "text", XmlText::Text); Nan::SetPrototypeMethod(tmpl, "replace", XmlText::Replace); Nan::Set(target, Nan::New("Text").ToLocalChecked(), Nan::GetFunction(tmpl).ToLocalChecked()); } } // namespace libxmljs