converted SgmlReader & NReadability to PCL; start reader project

This commit is contained in:
2013-10-01 23:26:28 +02:00
parent c2b6eab709
commit 266f494db6
35 changed files with 11433 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
using NReadability;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace PocketSharp
{
/// <summary>
/// PocketReader
/// </summary>
public partial class PocketReader
{
/// <summary>
/// REST client used for HTML retrieval
/// </summary>
protected readonly HttpClient _restClient;
/// <summary>
/// Initializes a new instance of the <see cref="PocketReader"/> class.
/// </summary>
public PocketReader()
{
// initialize REST client
_restClient = new HttpClient(new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
});
_restClient.DefaultRequestHeaders.Add("Accept", "text/html");
}
public async Task<string> Read(Uri uri)
{
NReadabilityTranscoder transcoder = new NReadabilityTranscoder();
bool success;
string htmlResponse = await Request(uri);
string transcodedContent = transcoder.Transcode(htmlResponse, out success);
// //new TranscodingInput(
// //transcoder.Transcode(
// //string transcodedContent =
// // transcoder.Transcode("https://github.com/marek-stoj/NReadability", out success);
return transcodedContent;
}
/// <summary>
/// Fetches a typed resource
/// </summary>
protected async Task<string> Request(Uri uri)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri);
// make async request
HttpResponseMessage response = await _restClient.SendAsync(request);
// validate HTTP response
ValidateResponse(response);
// read response
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
/// <summary>
/// Validates the response.
/// </summary>
/// <param name="response">The response.</param>
/// <returns></returns>
/// <exception cref="PocketException">
/// Error retrieving response
/// </exception>
protected void ValidateResponse(HttpResponseMessage response)
{
// no error found
if (response.StatusCode == HttpStatusCode.OK)
{
return;
}
string exceptionString = String.Format("Request error: {0} ({1})", response.ReasonPhrase, (int)response.StatusCode);
throw new PocketException(exceptionString);
}
}
}
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DB574D27-C040-453A-A459-4D6DCCBF2E2E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PocketSharp.Reader</RootNamespace>
<AssemblyName>PocketSharp.Reader</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Profile96</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="PocketReader.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.16\lib\portable-net40+sl4+win8+wp71\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.16\lib\portable-net40+sl4+win8+wp71\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.5.0.6\lib\portable-net40+sl4+wp7+win8\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System.IO">
<HintPath>..\packages\Microsoft.Bcl.1.1.3\lib\portable-net40+sl4+win8+wp71\System.IO.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http">
<HintPath>..\packages\Microsoft.Net.Http.2.2.15\lib\portable-net40+sl4+win8+wp71\System.Net.Http.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Extensions">
<HintPath>..\packages\Microsoft.Net.Http.2.2.15\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Primitives">
<HintPath>..\packages\Microsoft.Net.Http.2.2.15\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Runtime">
<HintPath>..\packages\Microsoft.Bcl.1.1.3\lib\portable-net40+sl4+win8+wp71\System.Runtime.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.1.1.3\lib\portable-net40+sl4+win8+wp71\System.Threading.Tasks.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PocketSharp\PocketSharp.csproj">
<Project>{817200c3-a327-4e35-9b5f-63a51c088577}</Project>
<Name>PocketSharp</Name>
</ProjectReference>
<ProjectReference Include="..\PortablePorts\NReadability\NReadability.csproj">
<Project>{14c3ee6a-54a4-4a37-8b56-d52a3802f1c2}</Project>
<Name>NReadability</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,30 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("PocketSharp.Reader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PocketSharp.Reader")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl" version="1.1.3" targetFramework="portable-net45+sl40+wp71+win" />
<package id="Microsoft.Bcl.Async" version="1.0.16" targetFramework="portable-net45+sl40+wp71+win" />
<package id="Microsoft.Bcl.Build" version="1.0.10" targetFramework="portable-net45+sl40+wp71+win" />
<package id="Microsoft.Net.Http" version="2.2.15" targetFramework="portable-net403+sl40+wp71+win" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="portable-net403+sl40+wp71+win" />
</packages>
@@ -72,9 +72,14 @@
<Compile Include="ModifyTests.cs" />
<Compile Include="GetTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReadTests.cs" />
<Compile Include="TestsBase.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PocketSharp.Reader\PocketSharp.Reader.csproj">
<Project>{db574d27-c040-453a-a459-4d6dccbf2e2e}</Project>
<Name>PocketSharp.Reader</Name>
</ProjectReference>
<ProjectReference Include="..\PocketSharp\PocketSharp.csproj">
<Project>{817200c3-a327-4e35-9b5f-63a51c088577}</Project>
<Name>PocketSharp</Name>
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Xunit;
using PocketSharp.Models;
namespace PocketSharp.Tests
{
public class ReadTests : TestsBase
{
public ReadTests() : base() { }
[Fact]
public async Task TemporaryReadTest()
{
PocketReader reader = new PocketReader();
await reader.Read(new Uri("https://github.com/ceee/PocketSharp"));
Assert.True(true);
}
}
}
+34
View File
@@ -22,6 +22,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{19DD45
.nuget\NuGet.targets = .nuget\NuGet.targets
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NReadability", "PortablePorts\NReadability\NReadability.csproj", "{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SgmlReader", "PortablePorts\SgmlReader\SgmlReader.csproj", "{9112414C-E2D1-43BA-A298-A89F77D94332}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PortablePorts", "PortablePorts", "{82F79DE3-76D7-4FCB-AA7E-16BA7E363E0D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PocketSharp.Reader", "PocketSharp.Reader\PocketSharp.Reader.csproj", "{DB574D27-C040-453A-A459-4D6DCCBF2E2E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -82,6 +90,30 @@ Global
{CA3C491B-A8CA-426C-A0BB-E91636767467}.Release|Any CPU.Build.0 = Release|Any CPU
{CA3C491B-A8CA-426C-A0BB-E91636767467}.Release|ARM.ActiveCfg = Release|Any CPU
{CA3C491B-A8CA-426C-A0BB-E91636767467}.Release|x86.ActiveCfg = Release|Any CPU
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}.Debug|ARM.ActiveCfg = Debug|Any CPU
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}.Debug|x86.ActiveCfg = Debug|Any CPU
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}.Release|Any CPU.Build.0 = Release|Any CPU
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}.Release|ARM.ActiveCfg = Release|Any CPU
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}.Release|x86.ActiveCfg = Release|Any CPU
{9112414C-E2D1-43BA-A298-A89F77D94332}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9112414C-E2D1-43BA-A298-A89F77D94332}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9112414C-E2D1-43BA-A298-A89F77D94332}.Debug|ARM.ActiveCfg = Debug|Any CPU
{9112414C-E2D1-43BA-A298-A89F77D94332}.Debug|x86.ActiveCfg = Debug|Any CPU
{9112414C-E2D1-43BA-A298-A89F77D94332}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9112414C-E2D1-43BA-A298-A89F77D94332}.Release|Any CPU.Build.0 = Release|Any CPU
{9112414C-E2D1-43BA-A298-A89F77D94332}.Release|ARM.ActiveCfg = Release|Any CPU
{9112414C-E2D1-43BA-A298-A89F77D94332}.Release|x86.ActiveCfg = Release|Any CPU
{DB574D27-C040-453A-A459-4D6DCCBF2E2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DB574D27-C040-453A-A459-4D6DCCBF2E2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB574D27-C040-453A-A459-4D6DCCBF2E2E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{DB574D27-C040-453A-A459-4D6DCCBF2E2E}.Debug|x86.ActiveCfg = Debug|Any CPU
{DB574D27-C040-453A-A459-4D6DCCBF2E2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB574D27-C040-453A-A459-4D6DCCBF2E2E}.Release|Any CPU.Build.0 = Release|Any CPU
{DB574D27-C040-453A-A459-4D6DCCBF2E2E}.Release|ARM.ActiveCfg = Release|Any CPU
{DB574D27-C040-453A-A459-4D6DCCBF2E2E}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -90,5 +122,7 @@ Global
{775569C2-987F-4AC4-8BA5-A315A21ED1B7} = {AFD4E3F1-B23C-4924-BA5B-D917FBADB8FA}
{656345D2-AF4C-4F88-9C9F-EBF54D7691DB} = {AFD4E3F1-B23C-4924-BA5B-D917FBADB8FA}
{47721A56-2128-4C5A-8B92-995FFC353304} = {AFD4E3F1-B23C-4924-BA5B-D917FBADB8FA}
{9112414C-E2D1-43BA-A298-A89F77D94332} = {82F79DE3-76D7-4FCB-AA7E-16BA7E363E0D}
{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2} = {82F79DE3-76D7-4FCB-AA7E-16BA7E363E0D}
EndGlobalSection
EndGlobal
@@ -0,0 +1,11 @@
using System.Xml.Linq;
namespace NReadability
{
public class AttributeTransformationInput
{
public string AttributeValue { get; set; }
public XElement Element { get; set; }
}
}
@@ -0,0 +1,15 @@
namespace NReadability
{
public class AttributeTransformationResult
{
/// <summary>
/// Result of the transformation.
/// </summary>
public string TransformedValue { get; set; }
/// <summary>
/// Name of the attribute that will be used to store the original value. Can be null.
/// </summary>
public string OriginalValueAttributeName { get; set; }
}
}
@@ -0,0 +1,67 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Xml.Linq;
namespace NReadability
{
internal class ChildNodesTraverser
{
private readonly Action<XNode> _childNodeVisitor;
#region Constructor(s)
public ChildNodesTraverser(Action<XNode> childNodeVisitor)
{
if (childNodeVisitor == null)
{
throw new ArgumentNullException("childNodeVisitor");
}
_childNodeVisitor = childNodeVisitor;
}
#endregion
#region Public methods
public void Traverse(XNode node)
{
if (!(node is XContainer))
{
throw new ArgumentException("The node must be an XContainer in order to traverse its children.");
}
var childNode = ((XContainer)node).FirstNode;
while (childNode != null)
{
var nextChildNode = childNode.NextNode;
_childNodeVisitor(childNode);
childNode = nextChildNode;
}
}
#endregion
}
}
+28
View File
@@ -0,0 +1,28 @@
using System.Reflection;
namespace NReadability
{
public static class Consts
{
private static readonly string _nReadabilityFullName;
#region Constructor(s)
static Consts()
{
_nReadabilityFullName = string.Format("NReadability {0}", Assembly.GetExecutingAssembly().FullName);
}
#endregion
#region Properties
public static string NReadabilityFullName
{
get { return _nReadabilityFullName; }
}
#endregion
}
}
+303
View File
@@ -0,0 +1,303 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace NReadability
{
public static class DomExtensions
{
#region XDocument extensions
public static XElement GetBody(this XDocument document)
{
if (document == null)
{
throw new ArgumentNullException("document");
}
var documentRoot = document.Root;
if (documentRoot == null)
{
return null;
}
return documentRoot.GetElementsByTagName("body").FirstOrDefault();
}
public static string GetTitle(this XDocument document)
{
if (document == null)
{
throw new ArgumentNullException("document");
}
var documentRoot = document.Root;
if (documentRoot == null)
{
return null;
}
var headElement = documentRoot.GetElementsByTagName("head").FirstOrDefault();
if (headElement == null)
{
return "";
}
var titleElement = headElement.GetChildrenByTagName("title").FirstOrDefault();
if (titleElement == null)
{
return "";
}
return (titleElement.Value ?? "").Trim();
}
public static XElement GetElementById(this XDocument document, string id)
{
if (document == null)
{
throw new ArgumentNullException("document");
}
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException("id");
}
return
(from element in document.Descendants()
let idAttribute = element.Attribute("id")
where idAttribute != null && idAttribute.Value == id
select element).SingleOrDefault();
}
#endregion
#region XElement extensions
public static string GetId(this XElement element)
{
return element.GetAttributeValue("id", "");
}
public static void SetId(this XElement element, string id)
{
element.SetAttributeValue("id", id);
}
public static string GetClass(this XElement element)
{
return element.GetAttributeValue("class", "");
}
public static void SetClass(this XElement element, string @class)
{
element.SetAttributeValue("class", @class);
}
public static string GetStyle(this XElement element)
{
return element.GetAttributeValue("style", "");
}
public static void SetStyle(this XElement element, string style)
{
element.SetAttributeValue("style", style);
}
public static string GetAttributeValue(this XElement element, string attributeName, string defaultValue)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (string.IsNullOrEmpty(attributeName))
{
throw new ArgumentNullException("attributeName");
}
var attribute = element.Attribute(attributeName);
return attribute != null
? (attribute.Value ?? defaultValue)
: defaultValue;
}
public static void SetAttributeValue(this XElement element, string attributeName, string value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (string.IsNullOrEmpty(attributeName))
{
throw new ArgumentNullException("attributeName");
}
if (value == null)
{
var attribute = element.Attribute(attributeName);
if (attribute != null)
{
attribute.Remove();
}
}
else
{
element.SetAttributeValue(attributeName, value);
}
}
public static string GetAttributesString(this XElement element, string separator)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (separator == null)
{
throw new ArgumentNullException("separator");
}
var resultSb = new StringBuilder();
bool isFirst = true;
element.Attributes().Aggregate(
resultSb,
(sb, attribute) =>
{
string attributeValue = attribute.Value;
if (string.IsNullOrEmpty(attributeValue))
{
return sb;
}
if (!isFirst)
{
resultSb.Append(separator);
}
isFirst = false;
sb.Append(attribute.Value);
return sb;
});
return resultSb.ToString();
}
public static string GetInnerHtml(this XContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
var resultSb = new StringBuilder();
foreach (var childNode in container.Nodes())
{
resultSb.Append(childNode.ToString(SaveOptions.DisableFormatting));
}
return resultSb.ToString();
}
public static void SetInnerHtml(this XElement element, string html)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (html == null)
{
throw new ArgumentNullException("html");
}
element.RemoveAll();
var tmpElement = new SgmlDomBuilder().BuildDocument(html);
if (tmpElement.Root == null)
{
return;
}
foreach (var node in tmpElement.Root.Nodes())
{
element.Add(node);
}
}
#endregion
#region XContainer extensions
public static IEnumerable<XElement> GetElementsByTagName(this XContainer container, string tagName)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
if (string.IsNullOrEmpty(tagName))
{
throw new ArgumentNullException("tagName");
}
return container.Descendants()
.Where(e => tagName.Equals(e.Name.LocalName, StringComparison.OrdinalIgnoreCase));
}
public static IEnumerable<XElement> GetChildrenByTagName(this XContainer container, string tagName)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
if (string.IsNullOrEmpty(tagName))
{
throw new ArgumentNullException("tagName");
}
return container.Elements()
.Where(e => e.Name != null && tagName.Equals(e.Name.LocalName, StringComparison.OrdinalIgnoreCase));
}
#endregion
}
}
@@ -0,0 +1,48 @@
using System;
namespace NReadability
{
public class DomSerializationParams
{
#region Factory methods
/// <summary>
/// Creates an instance of DomSerializationParams with parameters set to their defaults.
/// </summary>
public static DomSerializationParams CreateDefault()
{
return new DomSerializationParams();
}
#endregion
#region Properties
/// <summary>
/// Determines whether the output will be formatted.
/// </summary>
public bool PrettyPrint { get; set; }
/// <summary>
/// Determines whether DOCTYPE will be included at the beginning of the output.
/// </summary>
public bool DontIncludeContentTypeMetaElement { get; set; }
/// <summary>
/// Determines whether mobile-specific elements (such as eg. meta HandheldFriendly) will be added/replaced in the output.
/// </summary>
public bool DontIncludeMobileSpecificMetaElements { get; set; }
/// <summary>
/// Determines whether a meta tag with a content-type specification will be added/replaced in the output.
/// </summary>
public bool DontIncludeDocTypeMetaElement { get; set; }
/// <summary>
/// Determines whether a meta tag with a generator specification will be added/replaced in the output.
/// </summary>
public bool DontIncludeGeneratorMetaElement { get; set; }
#endregion
}
}
@@ -0,0 +1,67 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Xml.Linq;
namespace NReadability
{
public class ElementsTraverser
{
private readonly Action<XElement> _elementVisitor;
#region Constructor(s)
public ElementsTraverser(Action<XElement> elementVisitor)
{
if (elementVisitor == null)
{
throw new ArgumentNullException("elementVisitor");
}
_elementVisitor = elementVisitor;
}
#endregion
#region Public methods
public void Traverse(XElement element)
{
_elementVisitor(element);
var childNode = element.FirstNode;
while (childNode != null)
{
var nextChildNode = childNode.NextNode;
if (childNode is XElement)
{
Traverse((XElement)childNode);
}
childNode = nextChildNode;
}
}
#endregion
}
}
@@ -0,0 +1,62 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.Text;
namespace NReadability
{
internal class EncodedStringWriter : StringWriter
{
private static readonly Encoding _DefaultEncoding = Encoding.UTF8;
private readonly Encoding _encoding;
#region Constructor(s)
public EncodedStringWriter(StringBuilder sb, Encoding encoding)
: base(sb)
{
if (encoding == null)
{
throw new ArgumentNullException("encoding");
}
_encoding = encoding;
}
public EncodedStringWriter(StringBuilder sb)
: this(sb, _DefaultEncoding)
{
}
#endregion
#region Properties
public override Encoding Encoding
{
get { return _encoding; }
}
#endregion
}
}
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace NReadability
{
public static class EnumerableExtensions
{
/// <summary>
/// Returns the only one element in the sequence or default(T) if either the sequence doesn't contain any elements or it contains more than one element.
/// </summary>
public static T SingleOrNone<T>(this IEnumerable<T> enumerable)
where T : class
{
// ReSharper disable PossibleMultipleEnumeration
if (enumerable == null)
{
throw new ArgumentNullException("enumerable");
}
T firstElement = enumerable.FirstOrDefault();
if (firstElement == null)
{
// no elements
return null;
}
T secondElement = enumerable.Skip(1).FirstOrDefault();
if (secondElement != null)
{
// more than one element
return null;
}
return firstElement;
// ReSharper restore PossibleMultipleEnumeration
}
}
}
+110
View File
@@ -0,0 +1,110 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace NReadability
{
/// <summary>
/// Determines how the extracted article will be styled.
/// </summary>
public enum ReadingStyle
{
/// <summary>
/// Newspaper style.
/// </summary>
Newspaper,
/// <summary>
/// Novel style.
/// </summary>
Novel,
/// <summary>
/// Ebook style.
/// </summary>
Ebook,
/// <summary>
/// Terminal style.
/// </summary>
Terminal,
}
/// <summary>
/// Determines how wide the margin of the extracted article will be.
/// </summary>
public enum ReadingMargin
{
/// <summary>
/// Extra-narrow margin.
/// </summary>
XNarrow,
/// <summary>
/// Narrow margin.
/// </summary>
Narrow,
/// <summary>
/// Medium margin.
/// </summary>
Medium,
/// <summary>
/// Wide margin.
/// </summary>
Wide,
/// <summary>
/// Extra-wide margin.
/// </summary>
XWide,
}
/// <summary>
/// Determines how large the font of the extracted article will be.
/// </summary>
public enum ReadingSize
{
/// <summary>
/// Extra-small font.
/// </summary>
XSmall,
/// <summary>
/// Small font.
/// </summary>
Small,
/// <summary>
/// Medium font.
/// </summary>
Medium,
/// <summary>
/// Large font.
/// </summary>
Large,
/// <summary>
/// Extra-large font.
/// </summary>
XLarge,
}
}
+40
View File
@@ -0,0 +1,40 @@
using System;
namespace NReadability
{
public static class HtmlUtils
{
public static string RemoveScriptTags(string htmlContent)
{
if (htmlContent == null)
{
throw new ArgumentNullException("htmlContent");
}
if (htmlContent.Length == 0)
{
return "";
}
int indexOfScriptTagStart = htmlContent.IndexOf("<script", StringComparison.OrdinalIgnoreCase);
if (indexOfScriptTagStart == -1)
{
return htmlContent;
}
int indexOfScriptTagEnd = htmlContent.IndexOf("</script>", indexOfScriptTagStart, StringComparison.OrdinalIgnoreCase);
if (indexOfScriptTagEnd == -1)
{
return htmlContent.Substring(0, indexOfScriptTagStart);
}
string strippedHtmlContent =
htmlContent.Substring(0, indexOfScriptTagStart) +
htmlContent.Substring(indexOfScriptTagEnd + "</script>".Length);
return RemoveScriptTags(strippedHtmlContent);
}
}
}
@@ -0,0 +1,62 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Runtime.Serialization;
namespace NReadability
{
/// <summary>
/// An exception that is thrown when an internal error occurrs in the application.
/// Internal error in the application means that there is a bug in the application.
/// </summary>
public class InternalErrorException : Exception
{
#region Constructor(s)
/// <summary>
/// Initializes a new instance of the InternalErrorException class with a specified error message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
public InternalErrorException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the InternalErrorException class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public InternalErrorException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the InternalErrorException class.
/// </summary>
public InternalErrorException()
{
}
#endregion
}
}
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{14C3EE6A-54A4-4A37-8B56-D52A3802F1C2}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NReadability</RootNamespace>
<AssemblyName>NReadability</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Profile96</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<Content Include="Resources\readability.css" />
</ItemGroup>
<ItemGroup>
<Compile Include="AttributeTransformationInput.cs" />
<Compile Include="AttributeTransformationResult.cs" />
<Compile Include="ChildNodesTraverser.cs" />
<Compile Include="Consts.cs" />
<Compile Include="DomExtensions.cs" />
<Compile Include="DomSerializationParams.cs" />
<Compile Include="ElementsTraverser.cs" />
<Compile Include="EncodedStringWriter.cs" />
<Compile Include="EnumerableExtensions.cs" />
<Compile Include="Enums.cs" />
<Compile Include="HtmlUtils.cs" />
<Compile Include="InternalErrorException.cs" />
<Compile Include="NReadabilityTranscoder.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SgmlDomBuilder.cs" />
<Compile Include="SgmlDomSerializer.cs" />
<Compile Include="TranscodingInput.cs" />
<Compile Include="TranscodingResult.cs" />
<Compile Include="UtilityExtensions.cs" />
<Compile Include="WebTranscodingInput.cs" />
<Compile Include="WebTranscodingResult.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SgmlReader\SgmlReader.csproj">
<Project>{9112414c-e2d1-43ba-a298-a89f77d94332}</Project>
<Name>SgmlReader</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NReadabilityPCL")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NReadabilityPCL")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,82 @@
/* Document */
body {font-size: 100%;}
#readability-logo,#arc90-logo,.footer-twitterLink,#readTools a,a.rdbTK-powered span{background-color:transparent!important;background-image:url(http://lab.arc90.com/experiments/readability/images/sprite-readability.png)!important;background-repeat:no-repeat!important;}
#readOverlay {display:block;position:absolute;top:0;left:0;width:100%;}
#readInner {line-height:1.4em;max-width:800px;margin:1em auto;}
#readInner a {color:#039;text-decoration:none;}
#readInner a:hover {text-decoration:underline;}
#readInner img {float:left;clear:both;margin: 0 12px 12px 0;}
#readInner h1 {display:block;width:100%;border-bottom:1px solid #333;font-size:1.2em;padding-bottom:.5em;margin-top:0;margin-bottom:.75em;}
#readInner sup{line-height:.8em;}
#readInner .page-separator{clear:both;display:block;font-size:.85em;filter:alpha(opacity=20);opacity:.20;text-align:center;}
.style-apertura #readInner h1 {border-bottom-color:#ededed;}
#readInner blockquote {margin-left:3em;margin-right:3em;}
#readability-inner * {margin-bottom:16px;border:none;background:none;}
/* Footer */
#readFooter {display:block;border-top:1px solid #333;text-align:center;clear:both;overflow:hidden;}
.style-apertura #readFooter {border-top-color:#ededed;}
#rdb-footer-left {display:inline;float:left;margin-top:15px;width:285px;background-position:0 -36px;}
.rdbTypekit #rdb-footer-left {width:475px;}
#rdb-footer-left a,#rdb-footer-left a:link {float:left;}
#readability-logo {display:inline;background-position:0 -36px;height:29px;width:189px;text-indent:-9000px;}
#arc90-logo {display:inline;background-position:right -36px;height:29px;width:96px;text-indent:-9000px;}
#readability-url {display:none;}
.style-apertura #readability-logo {background-position:0 -67px;}
.style-apertura #arc90-logo {background-position:right -67px;}
#rdb-footer-right {display:inline;float:right;text-align:right;font-size:.75em;margin-top:18px;}
#rdb-footer-right a {display:inline-block;float:left;overflow:visible;line-height:16px;vertical-align:baseline;}
.footer-twitterLink {height:20px;margin-left:20px;padding:4px 0 0 28px;background-position:0 -123px;font-size:12px;}
#rdb-footer-left .footer-twitterLink {display:none;margin-top:1px;padding-top:2px;}
.rdbTypekit #rdb-footer-right .footer-twitterLink {display:none;}
.rdbTypekit #rdb-footer-left .footer-twitterLink {display:inline-block!important;}
a.rdbTK-powered,a.rdbTK-powered:link,a.rdbTK-powered:hover {font-size:16px;color:#858789!important;text-decoration:none!important;}
a.rdbTK-powered span {display:inline-block;height:22px;margin-left:2px;padding:4px 0 0 26px;background-position:0 -146px!important;}
.style-apertura #rdb-inverse,.style-athelas #rdb-athelas {display:block;}
span.version {display:none;}
/* Tools */
#readTools {width:34px;height:150px;position:fixed;z-index:100;top:10px;left:10px;}
#readTools a {overflow:hidden;margin-bottom:8px;display:block;opacity:.4;text-indent:-99999px;height:34px;width:34px;text-decoration:none;filter:alpha(opacity=40);}
#reload-page {background-position:0 0;}
#print-page {background-position:-36px 0;}
#email-page {background-position:-72px 0;}
#kindle-page {background-position:-108px 0;}
#readTools a:hover {opacity:1;filter:alpha(opacity=100);}
/* -- USER-CONFIGURABLE STYLING -- */
/* Size */
.size-x-small {font-size:.75em;}
.size-small {font-size:.938em;}
.size-medium {font-size:1.125em;}
.size-large {font-size:1.375em;}
.size-x-large {font-size:1.75em;}
/* Style */
.style-newspaper {font-family:"Times New Roman", Times, serif;background:#fbfbfb;color:#080000;}
.style-newspaper h1 {text-transform:capitalize;font-family:Georgia, "Times New Roman", Times, serif;}
.style-newspaper #readInner a {color:#0924e1;}
.style-novel {font-family:"Palatino Linotype", "Book Antiqua", Palatino, serif;background:#f4eed9;color:#1d1916;}
.style-novel #readInner a {color:#1856ba;}
.style-ebook {font-family:Arial, Helvetica, sans-serif;background:#edebe8;color:#2c2d32;}
.style-ebook #readInner a {color:#187dc9;}
.style-ebook h1 {font-family:"Arial Black", Gadget, sans-serif;font-weight:400;}
.style-terminal {font-family:"Lucida Console", Monaco, monospace;background:#1d4e2c;color:#c6ffc6;}
.style-terminal #readInner a {color:#093;}
/* Typekit */
.style-apertura {font-family:"apertura-1", "apertura-2", sans-serif;background-color:#2d2828;color:#eae8e9;}
.style-apertura #readInner a {color:#58b0ff;}
.style-athelas {font-family:"athelas-1", "athelas-2", "Palatino Linotype", "Book Antiqua", Palatino, serif;background-color:#f7f7f7;color:#2b373d;}
.style-athelas #readInner a {color:#1e83cb;}
/* Margin */
.margin-x-narrow {width:95%;}
.margin-narrow {width:85%;}
.margin-medium {width:75%;}
.margin-wide {width:55%;}
.margin-x-wide {width:35%;}
/* -- USER-CONFIGURABLE STYLING -- */
/* -- DEBUG -- */
.bug-green {background:#bbf9b0;border:4px solid green;}
.bug-red {background:red;}
.bug-yellow {background:#ffff8e;}
.bug-blue {background:#bfdfff;}
/* -- EMAIL / KINDLE POP UP -- */
#kindle-container, #email-container {position:fixed;top:60px;left:50%;width:500px;height:490px;border:solid 3px #666;background-color:#fff;z-index:100!important;overflow:hidden;margin:0 0 0 -240px;padding:0;}
/* Override html styling attributes */
table, tr, td { background-color: transparent !important; }
@@ -0,0 +1,114 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using Sgml;
using System.IO;
namespace NReadability
{
/// <summary>
/// A class for constructing a DOM from HTML markup.
/// </summary>
public class SgmlDomBuilder
{
#region Public methods
/// <summary>
/// Constructs a DOM (System.Xml.Linq.XDocument) from HTML markup.
/// </summary>
/// <param name="htmlContent">HTML markup from which the DOM is to be constructed.</param>
/// <returns>System.Linq.Xml.XDocument instance which is a DOM of the provided HTML markup.</returns>
public XDocument BuildDocument(string htmlContent)
{
if (htmlContent == null)
{
throw new ArgumentNullException("htmlContent");
}
if (htmlContent.Trim().Length == 0)
{
return new XDocument();
}
// "trim end" htmlContent to ...</html>$ (codinghorror.com puts some scripts after the </html> - sic!)
const string htmlEnd = "</html";
int indexOfHtmlEnd = htmlContent.LastIndexOf(htmlEnd);
if (indexOfHtmlEnd != -1)
{
int indexOfHtmlEndBracket = htmlContent.IndexOf('>', indexOfHtmlEnd);
if (indexOfHtmlEndBracket != -1)
{
htmlContent = htmlContent.Substring(0, indexOfHtmlEndBracket + 1);
}
}
XDocument document;
try
{
document = LoadDocument(htmlContent);
}
catch (InvalidOperationException exc)
{
// sometimes SgmlReader doesn't handle <script> tags well and XDocument.Load() throws,
// so we can retry with the html content with <script> tags stripped off
if (!exc.Message.Contains("EndOfFile"))
{
throw;
}
htmlContent = HtmlUtils.RemoveScriptTags(htmlContent);
document = LoadDocument(htmlContent);
}
return document;
}
private static XDocument LoadDocument(string htmlContent)
{
using (var sgmlReader = new SgmlReader())
{
sgmlReader.CaseFolding = CaseFolding.ToLower;
sgmlReader.DocType = "HTML";
sgmlReader.WhitespaceHandling = WhitespaceHandling.None;
using (var sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(htmlContent))))
{
sgmlReader.InputStream = sr;
var document = XDocument.Load(sgmlReader);
return document;
}
}
}
#endregion
}
}
@@ -0,0 +1,190 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using System.Xml.Linq;
namespace NReadability
{
/// <summary>
/// A class for serializing a DOM to string.
/// </summary>
public class SgmlDomSerializer
{
#region Public methods
/// <summary>
/// Serializes given DOM (System.Xml.Linq.XDocument object) to a string.
/// </summary>
/// <param name="document">System.Xml.Linq.XDocument instance containing the DOM to be serialized.</param>
/// <param name="domSerializationParams">Contains parameters that modify the behaviour of the output serialization.</param>
/// <returns>Serialized representation of the DOM.</returns>
public string SerializeDocument(XDocument document, DomSerializationParams domSerializationParams)
{
if (!domSerializationParams.DontIncludeContentTypeMetaElement
|| !domSerializationParams.DontIncludeMobileSpecificMetaElements
|| !domSerializationParams.DontIncludeGeneratorMetaElement)
{
var documentRoot = document.Root;
if (documentRoot == null)
{
throw new ArgumentException("The document must have a root.");
}
if (documentRoot.Name == null || !"html".Equals(documentRoot.Name.LocalName, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("The document's root must be an html element.");
}
// add <head> element if not present
var headElement = documentRoot.GetChildrenByTagName("head").FirstOrDefault();
if (headElement == null)
{
headElement = new XElement("head");
documentRoot.AddFirst(headElement);
}
ProcessMetaElements(headElement, domSerializationParams);
}
string result = document.ToString(domSerializationParams.PrettyPrint ? SaveOptions.None : SaveOptions.DisableFormatting);
if (!domSerializationParams.DontIncludeDocTypeMetaElement)
{
result = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\r\n\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n" + result;
}
return result;
}
/// <summary>
/// Serializes given DOM (System.Xml.Linq.XDocument object) to a string.
/// </summary>
/// <param name="document">System.Xml.Linq.XDocument instance containing the DOM to be serialized.</param>
/// <returns>Serialized representation of the DOM.</returns>
public string SerializeDocument(XDocument document)
{
return SerializeDocument(document, DomSerializationParams.CreateDefault());
}
#endregion
#region Private helper methods
private static void ProcessMetaElements(XElement headElement, DomSerializationParams domSerializationParams)
{
ProcessMetaContentTypeElement(headElement, domSerializationParams);
ProcessMobileSpecificMetaElements(headElement, domSerializationParams);
ProcessMetaGeneratorElement(headElement, domSerializationParams);
}
private static void ProcessMetaContentTypeElement(XElement headElement, DomSerializationParams domSerializationParams)
{
if (!domSerializationParams.DontIncludeContentTypeMetaElement)
{
XElement metaContentTypeElement =
(from metaElement in headElement.GetChildrenByTagName("meta")
where "content-type".Equals(metaElement.GetAttributeValue("http-equiv", ""), StringComparison.OrdinalIgnoreCase)
select metaElement).FirstOrDefault();
// remove meta 'http-equiv' element if present
if (metaContentTypeElement != null)
{
metaContentTypeElement.Remove();
}
// add <meta name="http-equiv" ... /> element
metaContentTypeElement =
new XElement(
XName.Get("meta", headElement.Name != null ? (headElement.Name.NamespaceName ?? "") : ""),
new XAttribute("http-equiv", "Content-Type"),
new XAttribute("content", "text/html; charset=utf-8"));
headElement.AddFirst(metaContentTypeElement);
}
}
private static void ProcessMobileSpecificMetaElements(XElement headElement, DomSerializationParams domSerializationParams)
{
XElement metaViewportElement =
(from metaElement in headElement.GetChildrenByTagName("meta")
where "viewport".Equals(metaElement.GetAttributeValue("name", ""), StringComparison.OrdinalIgnoreCase)
select metaElement).FirstOrDefault();
// remove meta 'viewport' element if present
if (metaViewportElement != null)
{
metaViewportElement.Remove();
}
XElement metaHandheldFriendlyElement =
(from metaElement in headElement.GetChildrenByTagName("meta")
where "HandheldFriendly".Equals(metaElement.GetAttributeValue("name", ""), StringComparison.OrdinalIgnoreCase)
select metaElement).FirstOrDefault();
// remove meta 'HandheldFriendly' element if present
if (metaHandheldFriendlyElement != null)
{
metaHandheldFriendlyElement.Remove();
}
if (!domSerializationParams.DontIncludeMobileSpecificMetaElements)
{
// add <meta name="HandheldFriendly" ... /> element
metaHandheldFriendlyElement = new XElement(
XName.Get("meta", headElement.Name != null ? (headElement.Name.NamespaceName ?? "") : ""),
new XAttribute("name", "HandheldFriendly"),
new XAttribute("content", "true"));
headElement.AddFirst(metaHandheldFriendlyElement);
}
}
private static void ProcessMetaGeneratorElement(XElement headElement, DomSerializationParams domSerializationParams)
{
if (!domSerializationParams.DontIncludeGeneratorMetaElement)
{
XElement metaGeneratorElement =
(from metaElement in headElement.GetChildrenByTagName("meta")
where "Generator".Equals(metaElement.GetAttributeValue("name", ""), StringComparison.OrdinalIgnoreCase)
select metaElement).FirstOrDefault();
// remove meta 'generator' element if present
if (metaGeneratorElement != null)
{
metaGeneratorElement.Remove();
}
// add <meta name="Generator" ... /> element
metaGeneratorElement = new XElement(
XName.Get("meta", headElement.Name != null ? (headElement.Name.NamespaceName ?? "") : ""),
new XAttribute("name", "Generator"),
new XAttribute("content", Consts.NReadabilityFullName));
headElement.AddFirst(metaGeneratorElement);
}
}
#endregion
}
}
@@ -0,0 +1,29 @@
using System;
namespace NReadability
{
public class TranscodingInput
{
private DomSerializationParams _domSerializationParams;
public TranscodingInput(string htmlContent)
{
if (string.IsNullOrEmpty(htmlContent))
{
throw new ArgumentException("Argument can't be null nor empty.", "htmlContent");
}
HtmlContent = htmlContent;
}
public string HtmlContent { get; private set; }
public string Url { get; set; }
public DomSerializationParams DomSerializationParams
{
get { return _domSerializationParams ?? (_domSerializationParams = DomSerializationParams.CreateDefault()); }
set { _domSerializationParams = value; }
}
}
}
@@ -0,0 +1,21 @@
namespace NReadability
{
public class TranscodingResult
{
public TranscodingResult(bool contentExtracted, bool titleExtracted)
{
ContentExtracted = contentExtracted;
TitleExtracted = titleExtracted;
}
public bool ContentExtracted { get; private set; }
public bool TitleExtracted { get; private set; }
public string ExtractedContent { get; set; }
public string ExtractedTitle { get; set; }
public string NextPageUrl { get; set; }
}
}
@@ -0,0 +1,45 @@
/*
* NReadability
* http://code.google.com/p/nreadability/
*
* Copyright 2010 Marek Stój
* http://immortal.pl/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace NReadability
{
internal static class UtilityExtensions
{
#region Public methods
public static bool IsCloseToZero(this float x)
{
return Math.Abs(x) < float.Epsilon;
}
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (var element in enumerable)
{
action(element);
}
}
#endregion
}
}
@@ -0,0 +1,27 @@
using System;
namespace NReadability
{
public class WebTranscodingInput
{
private DomSerializationParams _domSerializationParams;
public WebTranscodingInput(string url)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException("Argument can't be null nor empty.", "url");
}
Url = url;
}
public string Url { get; private set; }
public DomSerializationParams DomSerializationParams
{
get { return _domSerializationParams ?? (_domSerializationParams = DomSerializationParams.CreateDefault()); }
set { _domSerializationParams = value; }
}
}
}
@@ -0,0 +1,19 @@
namespace NReadability
{
public class WebTranscodingResult
{
public WebTranscodingResult(bool contentExtracted, bool titleExtracted)
{
ContentExtracted = contentExtracted;
TitleExtracted = titleExtracted;
}
public bool ContentExtracted { get; private set; }
public bool TitleExtracted { get; private set; }
public string ExtractedContent { get; set; }
public string ExtractedTitle { get; set; }
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SgmlReader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SgmlReader")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9112414C-E2D1-43BA-A298-A89F77D94332}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SgmlReader</RootNamespace>
<AssemblyName>SgmlReader</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Profile96</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<None Include="Html.dtd" />
</ItemGroup>
<ItemGroup>
<Compile Include="SgmlParser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SgmlReader.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>