HTMLAgility Pack - OuterHtml Read-only? - html-agility-pack

Hey there, I am traversing all the links in my own code base, and changing them from <a href="x"> to <asp:HyperLink>'s for localization reasons. I'm using the HTMLAgilityPack for this (and other things) and I'd like to just change the OuterHtml object for the links I find..but it's read-only?
I'm new to the HAP, do I need to create a new node and delete the old one? Has anyone run into this?
Thanks!

I ended up using...
node.ParentNode.ReplaceChild(HtmlNode.CreateNode(HrefToAspLinkText(node.OuterHtml)), node)
hmm. worked, but ugly.

Why use the HTML Agility Pack if you're treating the whole document as a string. Instead look up tags and replace those then write back the document.
var doc = new HtmlDocument();
doc.LoadHtml(yourString); // or doc.Load(yourStream);
var links = doc.DocumentNode.Descendants("a");
foreach (var link in links)
{
link.Parent.Replace(ConvertLink(link), link);
}
string newDocument = doc.DocumentNode.OuterHtml; // Or doc.Save();
And your ConvertLink would look like this:
public HtmlNode ConvertLink(HtmlNode aTag)
{
var link = HtmlNode.Create("asp:HyperLink");
link.Attributes.Add(...);
return link;
}
(not compiled, so might need some tweaking).

Related

How to dynamically add view in page or layout

I can't figure out how to programatically add a view into a layout or page.
I need to add views at runtime without using static xml declaration since i need to fetch them from an http requested object... . I didn't find useful informations in the docs.
Anyone knows how to do?
I think you meant to dynamically add some view / controls to the page rather than to navigate into another page.
If so, you just need to add some controls into one of the layouts in your page (only containers [=layouts] can have multiple children.
so, your code (viewmodel/page controller) would look something like:
var layout = page.getViewById("Mycontainer");
// create dynamic content
var label = new Label();
label.text = "dynamic";
// connect to live view
layout.addChild(label)
In addition to having a page included inside your app (normal); you download the xml, css, & js to another directory and then navigate to it by then doing something like page.navigate('downloaded/page-name');
you can also do
var factoryFunc = function () {
var label = new labelModule.Label();
label.text = "Hello, world!";
var page = new pagesModule.Page();
page.content = label;
return page;
};
topmost.navigate(factoryFunc);
https://docs.nativescript.org/navigation#navigate-with-factory-function
You should check out this thread on the {N} forum.
The question is about dynamically loading a page and module from a remote server. The (possible) solution is given in this thread.

ckeditor how to allow for .insertHtml("<customTag myAttr='value'"></customTag>")

var currentDialog = CKEDITOR.dialog.getCurrent();
currentDialog._.editor.insertHtml("<customTag myAttr='var'></customTag>");
Throws an error, TypeError: Cannot read property 'isBlock' of undefined
If I try .insertHtml("<span>hello</span>") it works just fine.
How can I change ckeditor to allow me to specify my own custom html tags via .insertHtml()? I'd love to just change it to be something like <span class='custom'... or something like that, but I'm having to deal with legacy CMS articles. Using latest ckeditor. Thanks.
You need to modify CKEDITOR.dtd object so editor will know this tag and correctly parse HTML and process DOM:
CKEDITOR.dtd.customtag = { em:1 }; // List of tag names it can contain.
CKEDITOR.dtd.$block.customtag = 1; // Choose $block or $inline.
CKEDITOR.dtd.body.customtag = 1; // Body may contain customtag.
You need to allow for this tag and its styles/attrs/classes in Advanced Content Filter:
editor.filter.allow( 'customtag[myattr]', 'myfeature' );
Unfortunately, due to some caching, in certain situations you cannot modify DTD object after CKEditor is loaded - you need to modify it when it is created. So to do that:
Clone the CKEditor repository or CKEditor presets repository.
Modify core/dtd.js code.
And build your minified package following instructions in README.md - the only requirements are Java (sorry - Google Closure Compiler :P) and Bash.
PS. That error should not be thrown when unknown element is inserted, so I reported http://dev.ckeditor.com/ticket/10339 and to solve this inconvenience http://dev.ckeditor.com/ticket/10340.
I worked around this issue with a combination of createFromHtml() and insertElement()
CKEDITOR.replace('summary', { ... });
var editor = CKEDITOR.instances.summary;
editor.on('key', function(ev) {
if (ev.data.keyCode == 9) { // TAB
var tabHtml = '<span style="white-space:pre"> </span>';
var tabElement = CKEDITOR.dom.element.createFromHtml(tabHtml, editor.document);
editor.insertElement(tabElement);
}
}

Clickable Url in Twitter

i have implemented a simple twitter reader in my app. I am able to get the tweets of a
user. But, if there is a url in this tweet, i cant click on it, as its not detected as an URL.
Is there a possibility to implement this function, so that urls in the tweet are displayed
as clickable url, and then launch for example a webbrowser?
Thank you very much
I assume you are using a TextBlock to show the tweet text, correct? If so, change it to a RichTextBox and all you need to do is use Run for text and Hyperlink for the links!
Also, make sure you set the IsReadOnly property of the RichTextBox to true in order for it to work properly!
Next, parse the tweet text with a regular expression to find links, and use the Hiperlink class to create a clickable link on it, and Run on the remaining text!
Here's a sample function that will parse a tweet and build the content for a RichTextBox:
private Block ParseTweet(string tweetText)
{
var paragraph = new Paragraph();
var lastIndex = 0;
foreach (Match m in Regex.Matches(tweetText, #"(http(s)?://)?([\w-]+\.)+[\w-]+(/\S\w[\w- ;,./?%&=]\S*)?"))
{
if (m.Index > 0)
paragraph.Inlines.Add(tweetText.Substring(lastIndex, m.Index));
var hyperlink = new Hyperlink()
{
NavigateUri = new System.Uri(m.Value, System.UriKind.RelativeOrAbsolute),
TargetName = "_blank"
};
hyperlink.Inlines.Add(m.Value);
paragraph.Inlines.Add(hyperlink);
lastIndex = m.Index + m.Length;
}
if (lastIndex < tweetText.Length)
paragraph.Inlines.Add(tweetText.Substring(lastIndex));
return paragraph;
}
You should call this function like so:
var tweetText = #"Testing: http://twitter.com -> link for twitter";
MyRichTextBox.Blocks.Add(ParseTweet(tweetText));
I think it's not possible but but you can parse your text to find URL (with regex) and display a hyperlink below the text.
1) you search for URLs in the text with a regex
2) if a URL is found, you create a HyperlinkButton with this URL

HtmlAgilityPack: How to interpret non-tag-ed text in HTML

Know that the title is kind of vague, here is an example,
<DIV>
<DIV>title1</DIV>
line1<br/>
line2<br/>
<DIV>title2</DIV>
line2.1<br/>
line2.2<br/>
</DIV>
How can I fetch line1<br/>line2<br/> for title1, and line2.1<br/>line2.2<br/> for title2?
I'm using HtmlAgilityPack and SharpQuery together.
Thanks.
Possible Resolution
After researched and tried more, I managed to fetch these by using LinePosition and "//div/text()"
public static HtmlNodeCollection getNodes(string html, string xpath)
{
if (html.Length <= 0) { return null; }
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
return doc.DocumentNode.SelectNodes(xpath);
}
foreach (HtmlNode node in getNodes(html, "//div"){
foreach (HtmlNode plain_node in getNodes(html, "//div/text()")
{
if (plain_node.LinePosition <= node.LinePosition)
{
currentHtml += plain_n.InnerHtml + "<br/>";
}
}
}
Any other better way?
There is rarely one unique solution to an html matching problem. Although your solution works fine now and with your sample, the //div expression will search all div elements under root, recursively.
It means if the original Html evolves somehow, you may catch too many things or analyze too many nodes (performance may be an issue with things like // for big documents).
I would suggest something like this, which is more discriminant:
HtmlDocument doc = new HtmlDocument();
doc.Load(yourHtmlFile);
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("/div/div[starts-with(text(), 'title')]/following-sibling::text()[normalize-space(.) != '']"))
{
Console.WriteLine(node.InnerText.Trim());
}
It means
Search div elements from the root
Then search div elements underneath
Filter (using [...]) these elements and only select those with an inner text that start with 'title'
Then search all following sibling elements of type text
Filter these elements and only select those which are not empty or whitespace only
See this link for some help on XPATH Axes.
Assuming the structure is always the same you could get the divs and then get both of their NextSiblings

How to inject CSS located on /skin?

I want to inject a css file located on the skin folder in a browser page.
It is located on chrome://orkutmanager/skin/om.css, accessing manually show the file contents correctly.
I've tried this, but it's not working... What am I missing, or is it impossible?
You can also use the nsIStyleSheetService:
loadCSS: function() {
var sss = Components.classes["#mozilla.org/content/style-sheet-service;1"]
.getService(Components.interfaces.nsIStyleSheetService);
var ios = Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var uri = ios.newURI("chrome://addon/skin/style.css", null, null);
if(!sss.sheetRegistered(uri, sss.USER_SHEET))
sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
}
If you use USER_SHEET, the website's own CSS rules have higher priority than yours. Using AGENT_SHEET, your CSS should have higher priority.
In any way I needed to enforce some rules by using hte !important keyword.
I found this workaround. Read the file then inject it's contents...
function Read(file)
{
var ioService=Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var scriptableStream=Components
.classes["#mozilla.org/scriptableinputstream;1"]
.getService(Components.interfaces.nsIScriptableInputStream);
var channel=ioService.newChannel(file,null,null);
var input=channel.open();
scriptableStream.init(input);
var str=scriptableStream.read(input.available());
scriptableStream.close();
input.close();
return str;
}
var style = $("<style type='text/css' />");
style.html(Read("chrome://orkutmanager/skin/om.css"));
$("head").append(style);
I found that the link you referred to works if you reference the page document. In my case, using gBrowser.contentDocument worked.
var fileref = gBrowser.contentDocument.createElement("link");
fileref.setAttribute("rel", "stylesheet");
fileref.setAttribute("type", "text/css");
fileref.setAttribute("href", "resource://extensionid/content/skin/style.css");
gBrowser.contentDocument.getElementsByTagName("head")[0].appendChild(fileref);
Obviously make sure that you can access your css via the resource:// protocol.

Resources