Add Parser module.
This commit is contained in:
parent
f8c01f8d18
commit
6cee293a7e
8
Parser/.gitignore
vendored
Normal file
8
Parser/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/configuration/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1610"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Parser"
|
||||
BuildableName = "Parser"
|
||||
BlueprintName = "Parser"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Parser"
|
||||
BuildableName = "Parser"
|
||||
BlueprintName = "Parser"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
79
Parser/.swiftpm/xcode/xcshareddata/xcschemes/Parser.xcscheme
Normal file
79
Parser/.swiftpm/xcode/xcshareddata/xcschemes/Parser.xcscheme
Normal file
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1610"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Parser"
|
||||
BuildableName = "Parser"
|
||||
BlueprintName = "Parser"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ParserTests"
|
||||
BuildableName = "ParserTests"
|
||||
BlueprintName = "ParserTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Parser"
|
||||
BuildableName = "Parser"
|
||||
BlueprintName = "Parser"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1610"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ParserTests"
|
||||
BuildableName = "ParserTests"
|
||||
BlueprintName = "ParserTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
32
Parser/Package.swift
Normal file
32
Parser/Package.swift
Normal file
@ -0,0 +1,32 @@
|
||||
// swift-tools-version: 6.0
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Parser",
|
||||
platforms: [.macOS(.v14), .iOS(.v17)],
|
||||
products: [
|
||||
.library(
|
||||
name: "Parser",
|
||||
targets: ["Parser"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(path: "../RSCore")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "Parser",
|
||||
dependencies: [
|
||||
"RSCore"
|
||||
],
|
||||
swiftSettings: [.unsafeFlags(["-warnings-as-errors"])]
|
||||
),
|
||||
.testTarget(
|
||||
name: "ParserTests",
|
||||
dependencies: [
|
||||
"Parser"
|
||||
],
|
||||
resources: [.copy("Resources")]
|
||||
),
|
||||
]
|
||||
)
|
595
Parser/Sources/Parser/DateParser/DateParser.swift
Normal file
595
Parser/Sources/Parser/DateParser/DateParser.swift
Normal file
@ -0,0 +1,595 @@
|
||||
//
|
||||
// DateParser.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/28/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class DateParser {
|
||||
|
||||
// MARK: - Public API
|
||||
|
||||
/// Parse W3C and pubDate dates — used for feed parsing.
|
||||
/// This is a fast alternative to system APIs
|
||||
/// for parsing dates.
|
||||
public static func date(data: Data) -> Date? {
|
||||
|
||||
let numberOfBytes = data.count
|
||||
|
||||
// Make sure it’s in reasonable range for a date string.
|
||||
if numberOfBytes < 6 || numberOfBytes > 150 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return data.withUnsafeBytes { bytes in
|
||||
let buffer = bytes.bindMemory(to: UInt8.self)
|
||||
|
||||
if dateIsW3CDate(buffer, numberOfBytes) {
|
||||
return parseW3CDate(buffer, numberOfBytes)
|
||||
}
|
||||
else if dateIsPubDate(buffer, numberOfBytes) {
|
||||
return parsePubDate(buffer, numberOfBytes)
|
||||
}
|
||||
|
||||
// Fallback, in case our detection fails.
|
||||
return parseW3CDate(buffer, numberOfBytes)
|
||||
}
|
||||
}
|
||||
|
||||
public static func date(string: String) -> Date? {
|
||||
|
||||
guard let data = string.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
return date(data: data)
|
||||
}
|
||||
|
||||
private typealias DateBuffer = UnsafeBufferPointer<UInt8>
|
||||
|
||||
// See http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations for list
|
||||
private static let timeZoneTable: [String: Int] = [
|
||||
|
||||
"GMT": timeZoneOffset(0, 0),
|
||||
"UTC": timeZoneOffset(0, 0),
|
||||
"PDT": timeZoneOffset(-7, 0),
|
||||
"PST": timeZoneOffset(-8, 0),
|
||||
"EST": timeZoneOffset(-5, 0),
|
||||
"EDT": timeZoneOffset(-4, 0),
|
||||
"MDT": timeZoneOffset(-6, 0),
|
||||
"MST": timeZoneOffset(-7, 0),
|
||||
"CST": timeZoneOffset(-6, 0),
|
||||
"CDT": timeZoneOffset(-5, 0),
|
||||
"ACT": timeZoneOffset(-8, 0),
|
||||
"AFT": timeZoneOffset(4, 30),
|
||||
"AMT": timeZoneOffset(4, 0),
|
||||
"ART": timeZoneOffset(-3, 0),
|
||||
"AST": timeZoneOffset(3, 0),
|
||||
"AZT": timeZoneOffset(4, 0),
|
||||
"BIT": timeZoneOffset(-12, 0),
|
||||
"BDT": timeZoneOffset(8, 0),
|
||||
"ACST": timeZoneOffset(9, 30),
|
||||
"AEST": timeZoneOffset(10, 0),
|
||||
"AKST": timeZoneOffset(-9, 0),
|
||||
"AMST": timeZoneOffset(5, 0),
|
||||
"AWST": timeZoneOffset(8, 0),
|
||||
"AZOST": timeZoneOffset(-1, 0),
|
||||
"BIOT": timeZoneOffset(6, 0),
|
||||
"BRT": timeZoneOffset(-3, 0),
|
||||
"BST": timeZoneOffset(6, 0),
|
||||
"BTT": timeZoneOffset(6, 0),
|
||||
"CAT": timeZoneOffset(2, 0),
|
||||
"CCT": timeZoneOffset(6, 30),
|
||||
"CET": timeZoneOffset(1, 0),
|
||||
"CEST": timeZoneOffset(2, 0),
|
||||
"CHAST": timeZoneOffset(12, 45),
|
||||
"ChST": timeZoneOffset(10, 0),
|
||||
"CIST": timeZoneOffset(-8, 0),
|
||||
"CKT": timeZoneOffset(-10, 0),
|
||||
"CLT": timeZoneOffset(-4, 0),
|
||||
"CLST": timeZoneOffset(-3, 0),
|
||||
"COT": timeZoneOffset(-5, 0),
|
||||
"COST": timeZoneOffset(-4, 0),
|
||||
"CVT": timeZoneOffset(-1, 0),
|
||||
"CXT": timeZoneOffset(7, 0),
|
||||
"EAST": timeZoneOffset(-6, 0),
|
||||
"EAT": timeZoneOffset(3, 0),
|
||||
"ECT": timeZoneOffset(-4, 0),
|
||||
"EEST": timeZoneOffset(3, 0),
|
||||
"EET": timeZoneOffset(2, 0),
|
||||
"FJT": timeZoneOffset(12, 0),
|
||||
"FKST": timeZoneOffset(-4, 0),
|
||||
"GALT": timeZoneOffset(-6, 0),
|
||||
"GET": timeZoneOffset(4, 0),
|
||||
"GFT": timeZoneOffset(-3, 0),
|
||||
"GILT": timeZoneOffset(7, 0),
|
||||
"GIT": timeZoneOffset(-9, 0),
|
||||
"GST": timeZoneOffset(-2, 0),
|
||||
"GYT": timeZoneOffset(-4, 0),
|
||||
"HAST": timeZoneOffset(-10, 0),
|
||||
"HKT": timeZoneOffset(8, 0),
|
||||
"HMT": timeZoneOffset(5, 0),
|
||||
"IRKT": timeZoneOffset(8, 0),
|
||||
"IRST": timeZoneOffset(3, 30),
|
||||
"IST": timeZoneOffset(2, 0),
|
||||
"JST": timeZoneOffset(9, 0),
|
||||
"KRAT": timeZoneOffset(7, 0),
|
||||
"KST": timeZoneOffset(9, 0),
|
||||
"LHST": timeZoneOffset(10, 30),
|
||||
"LINT": timeZoneOffset(14, 0),
|
||||
"MAGT": timeZoneOffset(11, 0),
|
||||
"MIT": timeZoneOffset(-9, 30),
|
||||
"MSK": timeZoneOffset(3, 0),
|
||||
"MUT": timeZoneOffset(4, 0),
|
||||
"NDT": timeZoneOffset(-2, 30),
|
||||
"NFT": timeZoneOffset(11, 30),
|
||||
"NPT": timeZoneOffset(5, 45),
|
||||
"NT": timeZoneOffset(-3, 30),
|
||||
"OMST": timeZoneOffset(6, 0),
|
||||
"PETT": timeZoneOffset(12, 0),
|
||||
"PHOT": timeZoneOffset(13, 0),
|
||||
"PKT": timeZoneOffset(5, 0),
|
||||
"RET": timeZoneOffset(4, 0),
|
||||
"SAMT": timeZoneOffset(4, 0),
|
||||
"SAST": timeZoneOffset(2, 0),
|
||||
"SBT": timeZoneOffset(11, 0),
|
||||
"SCT": timeZoneOffset(4, 0),
|
||||
"SLT": timeZoneOffset(5, 30),
|
||||
"SST": timeZoneOffset(8, 0),
|
||||
"TAHT": timeZoneOffset(-10, 0),
|
||||
"THA": timeZoneOffset(7, 0),
|
||||
"UYT": timeZoneOffset(-3, 0),
|
||||
"UYST": timeZoneOffset(-2, 0),
|
||||
"VET": timeZoneOffset(-4, 30),
|
||||
"VLAT": timeZoneOffset(10, 0),
|
||||
"WAT": timeZoneOffset(1, 0),
|
||||
"WET": timeZoneOffset(0, 0),
|
||||
"WEST": timeZoneOffset(1, 0),
|
||||
"YAKT": timeZoneOffset(9, 0),
|
||||
"YEKT": timeZoneOffset(5, 0)
|
||||
]
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private extension DateParser {
|
||||
|
||||
struct DateCharacter {
|
||||
|
||||
static let space = Character(" ").asciiValue!
|
||||
static let `return` = Character("\r").asciiValue!
|
||||
static let newline = Character("\n").asciiValue!
|
||||
static let tab = Character("\t").asciiValue!
|
||||
static let hyphen = Character("-").asciiValue!
|
||||
static let comma = Character(",").asciiValue!
|
||||
static let dot = Character(".").asciiValue!
|
||||
static let colon = Character(":").asciiValue!
|
||||
static let plus = Character("+").asciiValue!
|
||||
static let minus = Character("-").asciiValue!
|
||||
static let A = Character("A").asciiValue!
|
||||
static let a = Character("a").asciiValue!
|
||||
static let D = Character("D").asciiValue!
|
||||
static let d = Character("d").asciiValue!
|
||||
static let F = Character("F").asciiValue!
|
||||
static let f = Character("f").asciiValue!
|
||||
static let J = Character("J").asciiValue!
|
||||
static let j = Character("j").asciiValue!
|
||||
static let M = Character("M").asciiValue!
|
||||
static let m = Character("m").asciiValue!
|
||||
static let N = Character("N").asciiValue!
|
||||
static let n = Character("n").asciiValue!
|
||||
static let O = Character("O").asciiValue!
|
||||
static let o = Character("o").asciiValue!
|
||||
static let S = Character("S").asciiValue!
|
||||
static let s = Character("s").asciiValue!
|
||||
static let U = Character("U").asciiValue!
|
||||
static let u = Character("u").asciiValue!
|
||||
static let Y = Character("Y").asciiValue!
|
||||
static let y = Character("y").asciiValue!
|
||||
static let Z = Character("Z").asciiValue!
|
||||
static let z = Character("z").asciiValue!
|
||||
}
|
||||
|
||||
enum Month: Int {
|
||||
|
||||
case January = 1,
|
||||
February,
|
||||
March,
|
||||
April,
|
||||
May,
|
||||
June,
|
||||
July,
|
||||
August,
|
||||
September,
|
||||
October,
|
||||
November,
|
||||
December
|
||||
}
|
||||
|
||||
// MARK: - Standard Formats
|
||||
|
||||
private static func dateIsW3CDate(_ bytes: DateBuffer, _ numberOfBytes: Int) -> Bool {
|
||||
|
||||
// Something like 2010-11-17T08:40:07-05:00
|
||||
// But might be missing T character in the middle.
|
||||
// Looks for four digits in a row followed by a -.
|
||||
|
||||
for i in 0..<numberOfBytes - 4 {
|
||||
|
||||
let ch = bytes[i]
|
||||
// Skip whitespace.
|
||||
if ch == DateCharacter.space || ch == DateCharacter.`return` || ch == DateCharacter.newline || ch == DateCharacter.tab {
|
||||
continue
|
||||
}
|
||||
|
||||
assert(i + 4 < numberOfBytes)
|
||||
// First non-whitespace character must be the beginning of the year, as in `2010-`
|
||||
return Bool(isDigit(ch)) && isDigit(bytes[i + 1]) && isDigit(bytes[i + 2]) && isDigit(bytes[i + 3]) && bytes[i + 4] == DateCharacter.hyphen
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private static func dateIsPubDate(_ bytes: DateBuffer, _ numberOfBytes: Int) -> Bool {
|
||||
|
||||
for ch in bytes {
|
||||
if ch == DateCharacter.space || ch == DateCharacter.comma {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private static func parseW3CDate(_ bytes: DateBuffer, _ numberOfBytes: Int) -> Date? {
|
||||
|
||||
/*@"yyyy'-'MM'-'dd'T'HH':'mm':'ss"
|
||||
@"yyyy-MM-dd'T'HH:mm:sszzz"
|
||||
@"yyyy-MM-dd'T'HH:mm:ss'.'SSSzzz"
|
||||
etc.*/
|
||||
|
||||
var finalIndex = 0
|
||||
|
||||
guard let year = nextNumericValue(bytes, numberOfBytes, 0, 4, &finalIndex) else {
|
||||
return nil
|
||||
}
|
||||
guard let month = nextNumericValue(bytes, numberOfBytes, finalIndex + 1, 2, &finalIndex) else {
|
||||
return nil
|
||||
}
|
||||
guard let day = nextNumericValue(bytes, numberOfBytes, finalIndex + 1, 2, &finalIndex) else {
|
||||
return nil
|
||||
}
|
||||
let hour = nextNumericValue(bytes, numberOfBytes, finalIndex + 1, 2, &finalIndex) ?? 0
|
||||
let minute = nextNumericValue(bytes, numberOfBytes, finalIndex + 1, 2, &finalIndex) ?? 0
|
||||
let second = nextNumericValue(bytes, numberOfBytes, finalIndex + 1, 2, &finalIndex) ?? 0
|
||||
|
||||
var currentIndex = finalIndex + 1
|
||||
|
||||
let milliseconds = {
|
||||
var ms = 0
|
||||
let hasMilliseconds = (currentIndex < numberOfBytes) && (bytes[currentIndex] == DateCharacter.dot)
|
||||
if hasMilliseconds {
|
||||
ms = nextNumericValue(bytes, numberOfBytes, currentIndex, 3, &finalIndex) ?? 00
|
||||
currentIndex = finalIndex + 1
|
||||
}
|
||||
|
||||
// Ignore more than 3 digits of precision
|
||||
while currentIndex < numberOfBytes && isDigit(bytes[currentIndex]) {
|
||||
currentIndex += 1
|
||||
}
|
||||
|
||||
return ms
|
||||
}()
|
||||
|
||||
let timeZoneOffset = parsedTimeZoneOffset(bytes, numberOfBytes, currentIndex)
|
||||
|
||||
return dateWithYearMonthDayHourMinuteSecondAndtimeZoneOffset(year, month, day, hour, minute, second, milliseconds, timeZoneOffset)
|
||||
}
|
||||
|
||||
private static func parsePubDate(_ bytes: DateBuffer, _ numberOfBytes: Int) -> Date? {
|
||||
|
||||
var finalIndex = 0
|
||||
|
||||
let day = nextNumericValue(bytes, numberOfBytes, 0, 2, &finalIndex) ?? 1
|
||||
let month = nextMonthValue(bytes, numberOfBytes, finalIndex + 1, &finalIndex) ?? .January
|
||||
|
||||
guard let year = nextNumericValue(bytes, numberOfBytes, finalIndex + 1, 4, &finalIndex) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let hour = nextNumericValue(bytes, numberOfBytes, finalIndex + 1, 2, &finalIndex) ?? 0
|
||||
let minute = nextNumericValue(bytes, numberOfBytes, finalIndex + 1, 2, &finalIndex) ?? 0
|
||||
|
||||
var currentIndex = finalIndex + 1
|
||||
|
||||
let second = {
|
||||
var s = 0
|
||||
let hasSeconds = (currentIndex < numberOfBytes) && (bytes[currentIndex] == DateCharacter.colon)
|
||||
if hasSeconds {
|
||||
s = nextNumericValue(bytes, numberOfBytes, currentIndex, 2, &finalIndex) ?? 0
|
||||
}
|
||||
return s
|
||||
}()
|
||||
|
||||
currentIndex = finalIndex + 1
|
||||
|
||||
let timeZoneOffset = {
|
||||
var offset = 0
|
||||
let hasTimeZone = (currentIndex < numberOfBytes) && (bytes[currentIndex] == DateCharacter.space)
|
||||
if hasTimeZone {
|
||||
offset = parsedTimeZoneOffset(bytes, numberOfBytes, currentIndex)
|
||||
}
|
||||
return offset
|
||||
}()
|
||||
|
||||
return dateWithYearMonthDayHourMinuteSecondAndtimeZoneOffset(year, month.rawValue, day, hour, minute, second, 0, timeZoneOffset)
|
||||
}
|
||||
|
||||
// MARK: - Date Creation
|
||||
|
||||
static func dateWithYearMonthDayHourMinuteSecondAndtimeZoneOffset(_ year: Int, _ month: Int, _ day: Int, _ hour: Int, _ minute: Int, _ second: Int, _ milliseconds: Int, _ timeZoneOffset: Int) -> Date? {
|
||||
|
||||
var timeInfo = tm()
|
||||
timeInfo.tm_sec = CInt(second)
|
||||
timeInfo.tm_min = CInt(minute)
|
||||
timeInfo.tm_hour = CInt(hour)
|
||||
timeInfo.tm_mday = CInt(day)
|
||||
timeInfo.tm_mon = CInt(month - 1) //It's 1-based coming in
|
||||
timeInfo.tm_year = CInt(year - 1900) //see time.h -- it's years since 1900
|
||||
timeInfo.tm_wday = -1
|
||||
timeInfo.tm_yday = -1
|
||||
timeInfo.tm_isdst = -1
|
||||
timeInfo.tm_gmtoff = 0;
|
||||
timeInfo.tm_zone = nil;
|
||||
|
||||
let rawTime = timegm(&timeInfo) - timeZoneOffset
|
||||
if rawTime == time_t(UInt32.max) {
|
||||
|
||||
// NSCalendar is super-amazingly slow (which is partly why this parser exists),
|
||||
// so this is used only when the date is far enough in the future
|
||||
// (19 January 2038 03:14:08Z on 32-bit systems) that timegm fails.
|
||||
// Hopefully by the time we consistently need dates that far in the future
|
||||
// the performance of NSCalendar won’t be an issue.
|
||||
|
||||
var dateComponents = DateComponents()
|
||||
|
||||
dateComponents.timeZone = TimeZone(secondsFromGMT: timeZoneOffset)
|
||||
dateComponents.year = year
|
||||
dateComponents.month = month
|
||||
dateComponents.day = day
|
||||
dateComponents.hour = hour
|
||||
dateComponents.minute = minute
|
||||
dateComponents.second = second
|
||||
dateComponents.nanosecond = milliseconds * 1000000
|
||||
|
||||
return Calendar.autoupdatingCurrent.date(from: dateComponents)
|
||||
}
|
||||
|
||||
var timeInterval = TimeInterval(rawTime)
|
||||
if milliseconds > 0 {
|
||||
timeInterval += TimeInterval(TimeInterval(milliseconds) / 1000.0)
|
||||
}
|
||||
|
||||
return Date(timeIntervalSince1970: timeInterval)
|
||||
}
|
||||
|
||||
// MARK: - Time Zones and Offsets
|
||||
|
||||
private static func parsedTimeZoneOffset(_ bytes: DateBuffer, _ numberOfBytes: Int, _ startingIndex: Int) -> Int {
|
||||
|
||||
var timeZoneCharacters: [UInt8] = [0, 0, 0, 0, 0, 0] // nil-terminated last character
|
||||
var numberOfCharactersFound = 0
|
||||
var hasAtLeastOneAlphaCharacter = false
|
||||
|
||||
for i in startingIndex..<numberOfBytes {
|
||||
let ch = bytes[i]
|
||||
if ch == DateCharacter.colon || ch == DateCharacter.space {
|
||||
continue
|
||||
}
|
||||
let isAlphaCharacter = isAlpha(ch)
|
||||
if isAlphaCharacter {
|
||||
hasAtLeastOneAlphaCharacter = true
|
||||
}
|
||||
if isAlphaCharacter || isDigit(ch) || ch == DateCharacter.plus || ch == DateCharacter.minus {
|
||||
numberOfCharactersFound += 1
|
||||
timeZoneCharacters[numberOfCharactersFound - 1] = ch
|
||||
}
|
||||
if numberOfCharactersFound >= 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if numberOfCharactersFound < 1 || timeZoneCharacters[0] == DateCharacter.Z || timeZoneCharacters[0] == DateCharacter.z {
|
||||
return 0
|
||||
}
|
||||
|
||||
if hasAtLeastOneAlphaCharacter {
|
||||
return offsetInSecondsForTimeZoneAbbreviation(timeZoneCharacters) ?? 0
|
||||
}
|
||||
return offsetInSecondsForOffsetCharacters(timeZoneCharacters)
|
||||
}
|
||||
|
||||
private static func offsetInSecondsForOffsetCharacters(_ timeZoneCharacters: [UInt8]) -> Int {
|
||||
|
||||
let isPlus = timeZoneCharacters[0] == DateCharacter.plus
|
||||
var finalIndex = 0
|
||||
let numberOfCharacters = strlen(timeZoneCharacters)
|
||||
|
||||
return timeZoneCharacters.withUnsafeBufferPointer { bytes in
|
||||
let hours = nextNumericValue(bytes, numberOfCharacters, 0, 2, &finalIndex) ?? 0
|
||||
let minutes = nextNumericValue(bytes, numberOfCharacters, finalIndex + 1, 2, &finalIndex) ?? 0
|
||||
|
||||
if hours == 0 && minutes == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var seconds = (hours * 60 * 60) + (minutes * 60)
|
||||
if !isPlus {
|
||||
seconds = 0 - seconds
|
||||
}
|
||||
|
||||
return seconds
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns offset in seconds.
|
||||
static func timeZoneOffset(_ hours: Int, _ minutes: Int) -> Int {
|
||||
|
||||
if hours < 0 {
|
||||
return (hours * 60 * 60) - (minutes * 60)
|
||||
}
|
||||
return (hours * 60 * 60) + (minutes * 60)
|
||||
}
|
||||
|
||||
private static func offsetInSecondsForTimeZoneAbbreviation(_ abbreviation: [UInt8]) -> Int? {
|
||||
|
||||
var characters = [UInt8]()
|
||||
for character in abbreviation {
|
||||
if character == 0 {
|
||||
break
|
||||
}
|
||||
characters.append(character)
|
||||
}
|
||||
|
||||
let name = String(decoding: characters, as: UTF8.self)
|
||||
return timeZoneTable[name]
|
||||
}
|
||||
|
||||
// MARK: - Parser
|
||||
|
||||
private static func nextMonthValue(_ bytes: DateBuffer, _ numberOfBytes: Int, _ startingIndex: Int, _ finalIndex: inout Int) -> DateParser.Month? {
|
||||
|
||||
// Lots of short-circuits here. Not strict.
|
||||
|
||||
var numberOfAlphaCharactersFound = 0
|
||||
var monthCharacters: [CChar] = [0, 0, 0]
|
||||
|
||||
for i in startingIndex..<numberOfBytes {
|
||||
|
||||
finalIndex = i
|
||||
let ch = bytes[i]
|
||||
|
||||
let isAlphaCharacter = isAlpha(ch)
|
||||
if !isAlphaCharacter {
|
||||
if numberOfAlphaCharactersFound < 1 {
|
||||
continue
|
||||
}
|
||||
if numberOfAlphaCharactersFound > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
numberOfAlphaCharactersFound+=1
|
||||
if numberOfAlphaCharactersFound == 1 {
|
||||
if ch == DateCharacter.F || ch == DateCharacter.f {
|
||||
return .February
|
||||
}
|
||||
if ch == DateCharacter.S || ch == DateCharacter.s {
|
||||
return .September
|
||||
}
|
||||
if ch == DateCharacter.O || ch == DateCharacter.o {
|
||||
return .October
|
||||
}
|
||||
if ch == DateCharacter.N || ch == DateCharacter.n {
|
||||
return .November
|
||||
}
|
||||
if ch == DateCharacter.D || ch == DateCharacter.d {
|
||||
return .December
|
||||
}
|
||||
}
|
||||
|
||||
monthCharacters[numberOfAlphaCharactersFound - 1] = CChar(ch)
|
||||
if numberOfAlphaCharactersFound >= 3 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if numberOfAlphaCharactersFound < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if monthCharacters[0] == DateCharacter.J || monthCharacters[0] == DateCharacter.j { // Jan, Jun, Jul
|
||||
if monthCharacters[1] == DateCharacter.A || monthCharacters[1] == DateCharacter.a {
|
||||
return .January
|
||||
}
|
||||
if monthCharacters[1] == DateCharacter.U || monthCharacters[1] == DateCharacter.u {
|
||||
if monthCharacters[2] == DateCharacter.N || monthCharacters[2] == DateCharacter.n {
|
||||
return .June
|
||||
}
|
||||
return .July
|
||||
}
|
||||
return .January
|
||||
}
|
||||
|
||||
if monthCharacters[0] == DateCharacter.M || monthCharacters[0] == DateCharacter.m { // March, May
|
||||
if monthCharacters[2] == DateCharacter.Y || monthCharacters[2] == DateCharacter.y {
|
||||
return .May
|
||||
}
|
||||
return .March
|
||||
}
|
||||
|
||||
if monthCharacters[0] == DateCharacter.A || monthCharacters[0] == DateCharacter.a { // April, August
|
||||
if monthCharacters[1] == DateCharacter.U || monthCharacters[1] == DateCharacter.u {
|
||||
return .August
|
||||
}
|
||||
return .April
|
||||
}
|
||||
|
||||
return .January // Should never get here (but possibly do)
|
||||
}
|
||||
|
||||
private static func nextNumericValue(_ bytes: DateBuffer, _ numberOfBytes: Int, _ startingIndex: Int, _ maximumNumberOfDigits: Int, _ finalIndex: inout Int) -> Int? {
|
||||
|
||||
// Maximum for the maximum is 4 (for time zone offsets and years)
|
||||
assert(maximumNumberOfDigits > 0 && maximumNumberOfDigits <= 4)
|
||||
|
||||
var numberOfDigitsFound = 0
|
||||
var digits = [0, 0, 0, 0]
|
||||
|
||||
for i in startingIndex..<numberOfBytes {
|
||||
|
||||
finalIndex = i
|
||||
let ch = Int(bytes[i])
|
||||
|
||||
let isDigit = isDigit(ch)
|
||||
if !isDigit && numberOfDigitsFound < 1 {
|
||||
continue
|
||||
}
|
||||
if !isDigit && numberOfDigitsFound > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
digits[numberOfDigitsFound] = ch - 48; // '0' is 48
|
||||
numberOfDigitsFound+=1
|
||||
if numberOfDigitsFound >= maximumNumberOfDigits {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if numberOfDigitsFound < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if numberOfDigitsFound == 1 {
|
||||
return digits[0]
|
||||
}
|
||||
if numberOfDigitsFound == 2 {
|
||||
return (digits[0] * 10) + digits[1]
|
||||
}
|
||||
if numberOfDigitsFound == 3 {
|
||||
return (digits[0] * 100) + (digits[1] * 10) + digits[2]
|
||||
}
|
||||
return (digits[0] * 1000) + (digits[1] * 100) + (digits[2] * 10) + digits[3]
|
||||
}
|
||||
|
||||
static func isDigit<T: BinaryInteger>(_ ch: T) -> Bool {
|
||||
|
||||
return isdigit(Int32(ch)) != 0
|
||||
}
|
||||
|
||||
static func isAlpha<T: BinaryInteger>(_ ch: T) -> Bool {
|
||||
|
||||
return isalpha(Int32(ch)) != 0
|
||||
}
|
||||
}
|
57
Parser/Sources/Parser/FeedParser/Feeds/FeedParser.swift
Normal file
57
Parser/Sources/Parser/FeedParser/Feeds/FeedParser.swift
Normal file
@ -0,0 +1,57 @@
|
||||
//
|
||||
// FeedParser.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/20/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// FeedParser handles RSS, Atom, JSON Feed, and RSS-in-JSON.
|
||||
// You don’t need to know the type of feed.
|
||||
|
||||
public struct FeedParser {
|
||||
|
||||
public static func canParse(_ data: Data) -> Bool {
|
||||
|
||||
let type = FeedType.feedType(data)
|
||||
|
||||
switch type {
|
||||
case .jsonFeed, .rssInJSON, .rss, .atom:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse(urlString: String, data: Data) throws -> ParsedFeed? {
|
||||
|
||||
let type = FeedType.feedType(data)
|
||||
|
||||
switch type {
|
||||
|
||||
case .jsonFeed:
|
||||
return try JSONFeedParser.parse(urlString: urlString, data: data)
|
||||
|
||||
case .rssInJSON:
|
||||
return try RSSInJSONParser.parse(urlString: urlString, data: data)
|
||||
|
||||
case .rss:
|
||||
let feed = RSSParser.parsedFeed(urlString: urlString, data: data)
|
||||
return RSSFeedTransformer.parsedFeed(with: feed, feedType: .rss)
|
||||
|
||||
case .atom:
|
||||
let feed = AtomParser.parsedFeed(urlString: urlString, data: data)
|
||||
return RSSFeedTransformer.parsedFeed(with: feed, feedType: .atom)
|
||||
|
||||
case .unknown, .notAFeed:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public static func parseAsync(urlString: String, data: Data) async throws -> ParsedFeed? {
|
||||
|
||||
try parse(urlString: urlString, data: data)
|
||||
}
|
||||
}
|
29
Parser/Sources/Parser/FeedParser/Feeds/FeedParserError.swift
Normal file
29
Parser/Sources/Parser/FeedParser/Feeds/FeedParserError.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// FeedParserError.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/24/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct FeedParserError: Error, Sendable {
|
||||
|
||||
public enum FeedParserErrorType: Sendable {
|
||||
|
||||
case rssChannelNotFound
|
||||
case rssItemsNotFound
|
||||
case jsonFeedVersionNotFound
|
||||
case jsonFeedItemsNotFound
|
||||
case jsonFeedTitleNotFound
|
||||
case invalidJSON
|
||||
}
|
||||
|
||||
public let errorType: FeedParserErrorType
|
||||
|
||||
public init(_ errorType: FeedParserErrorType) {
|
||||
|
||||
self.errorType = errorType
|
||||
}
|
||||
}
|
148
Parser/Sources/Parser/FeedParser/Feeds/FeedType.swift
Normal file
148
Parser/Sources/Parser/FeedParser/Feeds/FeedType.swift
Normal file
@ -0,0 +1,148 @@
|
||||
//
|
||||
// FeedType.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/20/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum FeedType: Sendable {
|
||||
|
||||
case rss
|
||||
case atom
|
||||
case jsonFeed
|
||||
case rssInJSON
|
||||
case unknown
|
||||
case notAFeed
|
||||
|
||||
private static let minNumberOfBytesRequired = 128
|
||||
|
||||
static func feedType(_ data: Data, isPartialData: Bool = false) -> FeedType {
|
||||
|
||||
// Can call with partial data — while still downloading, for instance.
|
||||
// If there’s not enough data, return .unknown. Ask again when there’s more data.
|
||||
// If it’s definitely not a feed, return .notAFeed.
|
||||
|
||||
let count = data.count
|
||||
if count < minNumberOfBytesRequired {
|
||||
return .unknown
|
||||
}
|
||||
|
||||
return data.withUnsafeBytes { (pointer: UnsafeRawBufferPointer) in
|
||||
|
||||
guard let baseAddress = pointer.baseAddress else {
|
||||
return .unknown
|
||||
}
|
||||
let cCharPointer = baseAddress.assumingMemoryBound(to: CChar.self)
|
||||
|
||||
if isProbablyJSON(cCharPointer, count) {
|
||||
|
||||
if isPartialData {
|
||||
// Might not be able to detect a JSON Feed without all data.
|
||||
// Dr. Drang’s JSON Feed (see althis.json and allthis-partial.json in tests)
|
||||
// has, at this writing, the JSON version element at the end of the feed,
|
||||
// which is totally legal — but it means not being able to detect
|
||||
// that it’s a JSON Feed without all the data.
|
||||
// So this returns .unknown instead of .notAFeed.
|
||||
return .unknown
|
||||
}
|
||||
|
||||
if isProbablyJSONFeed(cCharPointer, count) {
|
||||
return .jsonFeed
|
||||
}
|
||||
if isProbablyRSSInJSON(cCharPointer, count) {
|
||||
return .rssInJSON
|
||||
}
|
||||
}
|
||||
|
||||
if isProbablyRSS(cCharPointer, count) {
|
||||
return .rss
|
||||
}
|
||||
if isProbablyAtom(cCharPointer, count) {
|
||||
return .atom
|
||||
}
|
||||
|
||||
return .notAFeed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension FeedType {
|
||||
|
||||
static func isProbablyRSS(_ bytes: UnsafePointer<CChar>, _ count: Int) -> Bool {
|
||||
|
||||
if didFindString("<rss", bytes, count) || didFindString("<rdf:RDF", bytes, count) {
|
||||
return true
|
||||
}
|
||||
|
||||
return didFindString("<channel>", bytes, count) && didFindString("<pubDate>", bytes, count)
|
||||
}
|
||||
|
||||
static func isProbablyAtom(_ bytes: UnsafePointer<CChar>, _ count: Int) -> Bool {
|
||||
|
||||
didFindString("<feed", bytes, count)
|
||||
}
|
||||
|
||||
static func isProbablyJSON(_ bytes: UnsafePointer<CChar>, _ count: Int) -> Bool {
|
||||
|
||||
bytesStartWithStringIgnoringWhitespace("{", bytes, count)
|
||||
}
|
||||
|
||||
static func isProbablyJSONFeed(_ bytes: UnsafePointer<CChar>, _ count: Int) -> Bool {
|
||||
|
||||
// Assumes already called `isProbablyJSON` and it returned true.
|
||||
didFindString("://jsonfeed.org/version/", bytes, count) || didFindString(":\\/\\/jsonfeed.org\\/version\\/", bytes, count)
|
||||
}
|
||||
|
||||
static func isProbablyRSSInJSON(_ bytes: UnsafePointer<CChar>, _ count: Int) -> Bool {
|
||||
|
||||
// Assumes already called `isProbablyJSON` and it returned true.
|
||||
didFindString("rss", bytes, count) && didFindString("channel", bytes, count) && didFindString("item", bytes, count)
|
||||
}
|
||||
|
||||
static func didFindString(_ string: UnsafePointer<CChar>, _ bytes: UnsafePointer<CChar>, _ numberOfBytes: Int) -> Bool {
|
||||
|
||||
let foundString = strnstr(bytes, string, numberOfBytes)
|
||||
return foundString != nil
|
||||
}
|
||||
|
||||
struct Whitespace {
|
||||
static let space = Character(" ").asciiValue!
|
||||
static let `return` = Character("\r").asciiValue!
|
||||
static let newline = Character("\n").asciiValue!
|
||||
static let tab = Character("\t").asciiValue!
|
||||
}
|
||||
|
||||
static func bytesStartWithStringIgnoringWhitespace(_ string: UnsafePointer<CChar>, _ bytes: UnsafePointer<CChar>, _ numberOfBytes: Int) -> Bool {
|
||||
|
||||
var i = 0
|
||||
|
||||
while i < numberOfBytes {
|
||||
|
||||
let ch = bytes[i]
|
||||
|
||||
if ch == Whitespace.space || ch == Whitespace.return || ch == Whitespace.newline || ch == Whitespace.tab {
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if ch == string[0] {
|
||||
if let found = strnstr(bytes, string, numberOfBytes) {
|
||||
return found == bytes + i
|
||||
}
|
||||
}
|
||||
|
||||
// Allow for a BOM of up to four bytes (assuming BOM is only at the start)
|
||||
if i < 4 {
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
247
Parser/Sources/Parser/FeedParser/Feeds/JSON/JSONFeedParser.swift
Normal file
247
Parser/Sources/Parser/FeedParser/Feeds/JSON/JSONFeedParser.swift
Normal file
@ -0,0 +1,247 @@
|
||||
//
|
||||
// JSONFeedParser.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/25/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// See https://jsonfeed.org/version/1.1
|
||||
|
||||
public struct JSONFeedParser {
|
||||
|
||||
struct Key {
|
||||
static let version = "version"
|
||||
static let items = "items"
|
||||
static let title = "title"
|
||||
static let homePageURL = "home_page_url"
|
||||
static let feedURL = "feed_url"
|
||||
static let feedDescription = "description"
|
||||
static let nextURL = "next_url"
|
||||
static let icon = "icon"
|
||||
static let favicon = "favicon"
|
||||
static let expired = "expired"
|
||||
static let author = "author"
|
||||
static let authors = "authors"
|
||||
static let name = "name"
|
||||
static let url = "url"
|
||||
static let avatar = "avatar"
|
||||
static let hubs = "hubs"
|
||||
static let type = "type"
|
||||
static let contentHTML = "content_html"
|
||||
static let contentText = "content_text"
|
||||
static let externalURL = "external_url"
|
||||
static let summary = "summary"
|
||||
static let image = "image"
|
||||
static let bannerImage = "banner_image"
|
||||
static let datePublished = "date_published"
|
||||
static let dateModified = "date_modified"
|
||||
static let tags = "tags"
|
||||
static let uniqueID = "id"
|
||||
static let attachments = "attachments"
|
||||
static let mimeType = "mime_type"
|
||||
static let sizeInBytes = "size_in_bytes"
|
||||
static let durationInSeconds = "duration_in_seconds"
|
||||
static let language = "language"
|
||||
}
|
||||
|
||||
static let jsonFeedVersionMarker = "://jsonfeed.org/version/" // Allow for the mistake of not getting the scheme exactly correct.
|
||||
|
||||
public static func parse(urlString: String, data: Data) throws -> ParsedFeed? {
|
||||
|
||||
guard let d = JSONUtilities.dictionary(with: data) else {
|
||||
throw FeedParserError(.invalidJSON)
|
||||
}
|
||||
|
||||
guard let version = d[Key.version] as? String, let _ = version.range(of: JSONFeedParser.jsonFeedVersionMarker) else {
|
||||
throw FeedParserError(.jsonFeedVersionNotFound)
|
||||
}
|
||||
guard let itemsArray = d[Key.items] as? JSONArray else {
|
||||
throw FeedParserError(.jsonFeedItemsNotFound)
|
||||
}
|
||||
guard let title = d[Key.title] as? String else {
|
||||
throw FeedParserError(.jsonFeedTitleNotFound)
|
||||
}
|
||||
|
||||
let authors = parseAuthors(d)
|
||||
let homePageURL = d[Key.homePageURL] as? String
|
||||
let feedURL = d[Key.feedURL] as? String ?? urlString
|
||||
let feedDescription = d[Key.feedDescription] as? String
|
||||
let nextURL = d[Key.nextURL] as? String
|
||||
let iconURL = d[Key.icon] as? String
|
||||
let faviconURL = d[Key.favicon] as? String
|
||||
let expired = d[Key.expired] as? Bool ?? false
|
||||
let hubs = parseHubs(d)
|
||||
let language = d[Key.language] as? String
|
||||
|
||||
let items = parseItems(itemsArray, urlString)
|
||||
|
||||
return ParsedFeed(type: .jsonFeed, title: title, homePageURL: homePageURL, feedURL: feedURL, language: language, feedDescription: feedDescription, nextURL: nextURL, iconURL: iconURL, faviconURL: faviconURL, authors: authors, expired: expired, hubs: hubs, items: items)
|
||||
}
|
||||
}
|
||||
|
||||
private extension JSONFeedParser {
|
||||
|
||||
static func parseAuthors(_ dictionary: JSONDictionary) -> Set<ParsedAuthor>? {
|
||||
|
||||
if let authorsArray = dictionary[Key.authors] as? JSONArray {
|
||||
var authors = Set<ParsedAuthor>()
|
||||
for author in authorsArray {
|
||||
if let parsedAuthor = parseAuthor(author) {
|
||||
authors.insert(parsedAuthor)
|
||||
}
|
||||
}
|
||||
return authors
|
||||
}
|
||||
|
||||
guard let authorDictionary = dictionary[Key.author] as? JSONDictionary,
|
||||
let parsedAuthor = parseAuthor(authorDictionary) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Set([parsedAuthor])
|
||||
}
|
||||
|
||||
static func parseAuthor(_ dictionary: JSONDictionary) -> ParsedAuthor? {
|
||||
let name = dictionary[Key.name] as? String
|
||||
let url = dictionary[Key.url] as? String
|
||||
let avatar = dictionary[Key.avatar] as? String
|
||||
if name == nil && url == nil && avatar == nil {
|
||||
return nil
|
||||
}
|
||||
return ParsedAuthor(name: name, url: url, avatarURL: avatar, emailAddress: nil)
|
||||
}
|
||||
|
||||
static func parseHubs(_ dictionary: JSONDictionary) -> Set<ParsedHub>? {
|
||||
|
||||
guard let hubsArray = dictionary[Key.hubs] as? JSONArray else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let hubs = hubsArray.compactMap { (hubDictionary) -> ParsedHub? in
|
||||
guard let hubURL = hubDictionary[Key.url] as? String, let hubType = hubDictionary[Key.type] as? String else {
|
||||
return nil
|
||||
}
|
||||
return ParsedHub(type: hubType, url: hubURL)
|
||||
}
|
||||
return hubs.isEmpty ? nil : Set(hubs)
|
||||
}
|
||||
|
||||
static func parseItems(_ itemsArray: JSONArray, _ feedURL: String) -> Set<ParsedItem> {
|
||||
|
||||
return Set(itemsArray.compactMap { (oneItemDictionary) -> ParsedItem? in
|
||||
return parseItem(oneItemDictionary, feedURL)
|
||||
})
|
||||
}
|
||||
|
||||
static func parseItem(_ itemDictionary: JSONDictionary, _ feedURL: String) -> ParsedItem? {
|
||||
|
||||
guard let uniqueID = parseUniqueID(itemDictionary) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let contentHTML = itemDictionary[Key.contentHTML] as? String
|
||||
let contentText = itemDictionary[Key.contentText] as? String
|
||||
if contentHTML == nil && contentText == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
let url = itemDictionary[Key.url] as? String
|
||||
let externalURL = itemDictionary[Key.externalURL] as? String
|
||||
let title = parseTitle(itemDictionary, feedURL)
|
||||
let language = itemDictionary[Key.language] as? String
|
||||
let summary = itemDictionary[Key.summary] as? String
|
||||
let imageURL = itemDictionary[Key.image] as? String
|
||||
let bannerImageURL = itemDictionary[Key.bannerImage] as? String
|
||||
|
||||
let datePublished = parseDate(itemDictionary[Key.datePublished] as? String)
|
||||
let dateModified = parseDate(itemDictionary[Key.dateModified] as? String)
|
||||
|
||||
let authors = parseAuthors(itemDictionary)
|
||||
var tags: Set<String>? = nil
|
||||
if let tagsArray = itemDictionary[Key.tags] as? [String] {
|
||||
tags = Set(tagsArray)
|
||||
}
|
||||
let attachments = parseAttachments(itemDictionary)
|
||||
|
||||
return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: feedURL, url: url, externalURL: externalURL, title: title, language: language, contentHTML: contentHTML, contentText: contentText, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments)
|
||||
}
|
||||
|
||||
static func parseTitle(_ itemDictionary: JSONDictionary, _ feedURL: String) -> String? {
|
||||
|
||||
guard let title = itemDictionary[Key.title] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isSpecialCaseTitleWithEntitiesFeed(feedURL) {
|
||||
return HTMLEntityDecoder.decodedString(title)
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
static func isSpecialCaseTitleWithEntitiesFeed(_ feedURL: String) -> Bool {
|
||||
|
||||
// As of 16 Feb. 2018, Kottke’s and Heer’s feeds includes HTML entities in the title elements.
|
||||
// If we find more feeds like this, we’ll add them here. If these feeds get fixed, we’ll remove them.
|
||||
|
||||
let lowerFeedURL = feedURL.lowercased()
|
||||
let matchStrings = ["kottke.org", "pxlnv.com", "macstories.net", "macobserver.com"]
|
||||
for matchString in matchStrings {
|
||||
if lowerFeedURL.contains(matchString) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
static func parseUniqueID(_ itemDictionary: JSONDictionary) -> String? {
|
||||
|
||||
if let uniqueID = itemDictionary[Key.uniqueID] as? String {
|
||||
return uniqueID // Spec says it must be a string
|
||||
}
|
||||
// Version 1 spec also says that if it’s a number, even though that’s incorrect, it should be coerced to a string.
|
||||
if let uniqueID = itemDictionary[Key.uniqueID] as? Int {
|
||||
return "\(uniqueID)"
|
||||
}
|
||||
if let uniqueID = itemDictionary[Key.uniqueID] as? Double {
|
||||
return "\(uniqueID)"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static func parseDate(_ dateString: String?) -> Date? {
|
||||
|
||||
guard let dateString = dateString, !dateString.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return DateParser.date(string: dateString)
|
||||
}
|
||||
|
||||
static func parseAttachments(_ itemDictionary: JSONDictionary) -> Set<ParsedAttachment>? {
|
||||
|
||||
guard let attachmentsArray = itemDictionary[Key.attachments] as? JSONArray else {
|
||||
return nil
|
||||
}
|
||||
return Set(attachmentsArray.compactMap { parseAttachment($0) })
|
||||
}
|
||||
|
||||
static func parseAttachment(_ attachmentObject: JSONDictionary) -> ParsedAttachment? {
|
||||
|
||||
guard let url = attachmentObject[Key.url] as? String else {
|
||||
return nil
|
||||
}
|
||||
guard let mimeType = attachmentObject[Key.mimeType] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let title = attachmentObject[Key.title] as? String
|
||||
let sizeInBytes = attachmentObject[Key.sizeInBytes] as? Int
|
||||
let durationInSeconds = attachmentObject[Key.durationInSeconds] as? Int
|
||||
|
||||
return ParsedAttachment(url: url, mimeType: mimeType, title: title, sizeInBytes: sizeInBytes, durationInSeconds: durationInSeconds)
|
||||
}
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
//
|
||||
// RSSInJSONParser.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/24/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
|
||||
// See https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md
|
||||
// Also: http://cyber.harvard.edu/rss/rss.html
|
||||
|
||||
public struct RSSInJSONParser {
|
||||
|
||||
public static func parse(urlString: String, data: Data) throws -> ParsedFeed? {
|
||||
|
||||
do {
|
||||
guard let parsedObject = try JSONSerialization.jsonObject(with: data) as? JSONDictionary else {
|
||||
throw FeedParserError(.invalidJSON)
|
||||
}
|
||||
guard let rssObject = parsedObject["rss"] as? JSONDictionary else {
|
||||
throw FeedParserError(.rssChannelNotFound)
|
||||
}
|
||||
guard let channelObject = rssObject["channel"] as? JSONDictionary else {
|
||||
throw FeedParserError(.rssChannelNotFound)
|
||||
}
|
||||
|
||||
// I’d bet money that in practice the items array won’t always appear correctly inside the channel object.
|
||||
// I’d also bet that sometimes it gets called "items" instead of "item".
|
||||
var itemsObject = channelObject["item"] as? JSONArray
|
||||
if itemsObject == nil {
|
||||
itemsObject = parsedObject["item"] as? JSONArray
|
||||
}
|
||||
if itemsObject == nil {
|
||||
itemsObject = channelObject["items"] as? JSONArray
|
||||
}
|
||||
if itemsObject == nil {
|
||||
itemsObject = parsedObject["items"] as? JSONArray
|
||||
}
|
||||
if itemsObject == nil {
|
||||
throw FeedParserError(.rssItemsNotFound)
|
||||
}
|
||||
|
||||
let title = channelObject["title"] as? String
|
||||
let homePageURL = channelObject["link"] as? String
|
||||
let feedURL = urlString
|
||||
let feedDescription = channelObject["description"] as? String
|
||||
let feedLanguage = channelObject["language"] as? String
|
||||
|
||||
let items = parseItems(itemsObject!, urlString)
|
||||
|
||||
return ParsedFeed(type: .rssInJSON, title: title, homePageURL: homePageURL, feedURL: feedURL, language: feedLanguage, feedDescription: feedDescription, nextURL: nil, iconURL: nil, faviconURL: nil, authors: nil, expired: false, hubs: nil, items: items)
|
||||
|
||||
}
|
||||
catch { throw error }
|
||||
}
|
||||
}
|
||||
|
||||
private extension RSSInJSONParser {
|
||||
|
||||
static func parseItems(_ itemsObject: JSONArray, _ feedURL: String) -> Set<ParsedItem> {
|
||||
|
||||
return Set(itemsObject.compactMap{ (oneItemDictionary) -> ParsedItem? in
|
||||
|
||||
return parsedItemWithDictionary(oneItemDictionary, feedURL)
|
||||
})
|
||||
}
|
||||
|
||||
static func parsedItemWithDictionary(_ itemDictionary: JSONDictionary, _ feedURL: String) -> ParsedItem? {
|
||||
|
||||
let externalURL = itemDictionary["link"] as? String
|
||||
let title = itemDictionary["title"] as? String
|
||||
|
||||
var contentHTML = itemDictionary["description"] as? String
|
||||
var contentText: String? = nil
|
||||
if contentHTML != nil && !(contentHTML!.contains("<")) {
|
||||
contentText = contentHTML
|
||||
contentHTML = nil
|
||||
}
|
||||
if contentHTML == nil && contentText == nil && title == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var datePublished: Date? = nil
|
||||
if let datePublishedString = itemDictionary["pubDate"] as? String {
|
||||
datePublished = DateParser.date(string: datePublishedString)
|
||||
}
|
||||
|
||||
let authors = parseAuthors(itemDictionary)
|
||||
let tags = parseTags(itemDictionary)
|
||||
let attachments = parseAttachments(itemDictionary)
|
||||
|
||||
var uniqueID: String? = itemDictionary["guid"] as? String
|
||||
if uniqueID == nil {
|
||||
|
||||
// Calculate a uniqueID based on a combination of non-empty elements. Then hash the result.
|
||||
// Items should have guids. When they don't, re-runs are very likely
|
||||
// because there's no other 100% reliable way to determine identity.
|
||||
// This calculated uniqueID is valid only for this particular feed. (Just like ids in JSON Feed.)
|
||||
|
||||
var s = ""
|
||||
if let datePublished = datePublished {
|
||||
s += "\(datePublished.timeIntervalSince1970)"
|
||||
}
|
||||
if let title = title {
|
||||
s += title
|
||||
}
|
||||
if let externalURL = externalURL {
|
||||
s += externalURL
|
||||
}
|
||||
if let authorEmailAddress = authors?.first?.emailAddress {
|
||||
s += authorEmailAddress
|
||||
}
|
||||
if let oneAttachmentURL = attachments?.first?.url {
|
||||
s += oneAttachmentURL
|
||||
}
|
||||
if s.isEmpty {
|
||||
// Sheesh. Tough case.
|
||||
if let _ = contentHTML {
|
||||
s = contentHTML!
|
||||
}
|
||||
if let _ = contentText {
|
||||
s = contentText!
|
||||
}
|
||||
}
|
||||
uniqueID = s.md5String
|
||||
}
|
||||
|
||||
if let uniqueID = uniqueID {
|
||||
return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: feedURL, url: nil, externalURL: externalURL, title: title, language: nil, contentHTML: contentHTML, contentText: contentText, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: nil, authors: authors, tags: tags, attachments: attachments)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static func parseAuthors(_ itemDictionary: JSONDictionary) -> Set<ParsedAuthor>? {
|
||||
|
||||
guard let authorEmailAddress = itemDictionary["author"] as? String else {
|
||||
return nil
|
||||
}
|
||||
let parsedAuthor = ParsedAuthor(name: nil, url: nil, avatarURL: nil, emailAddress: authorEmailAddress)
|
||||
return Set([parsedAuthor])
|
||||
}
|
||||
|
||||
static func parseTags(_ itemDictionary: JSONDictionary) -> Set<String>? {
|
||||
|
||||
if let categoryObject = itemDictionary["category"] as? JSONDictionary {
|
||||
if let oneTag = categoryObject["#value"] as? String {
|
||||
return Set([oneTag])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
else if let categoryArray = itemDictionary["category"] as? JSONArray {
|
||||
return Set(categoryArray.compactMap{ $0["#value"] as? String })
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static func parseAttachments(_ itemDictionary: JSONDictionary) -> Set<ParsedAttachment>? {
|
||||
|
||||
guard let enclosureObject = itemDictionary["enclosure"] as? JSONDictionary else {
|
||||
return nil
|
||||
}
|
||||
guard let attachmentURL = enclosureObject["url"] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var attachmentSize = enclosureObject["length"] as? Int
|
||||
if attachmentSize == nil {
|
||||
if let attachmentSizeString = enclosureObject["length"] as? String {
|
||||
attachmentSize = (attachmentSizeString as NSString).integerValue
|
||||
}
|
||||
}
|
||||
|
||||
let type = enclosureObject["type"] as? String
|
||||
if let attachment = ParsedAttachment(url: attachmentURL, mimeType: type, title: nil, sizeInBytes: attachmentSize, durationInSeconds: nil) {
|
||||
return Set([attachment])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
//
|
||||
// ParsedAttachment.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/20/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class ParsedAttachment: Hashable, Sendable {
|
||||
|
||||
public let url: String
|
||||
public let mimeType: String?
|
||||
public let title: String?
|
||||
public let sizeInBytes: Int?
|
||||
public let durationInSeconds: Int?
|
||||
|
||||
public init?(url: String, mimeType: String?, title: String?, sizeInBytes: Int?, durationInSeconds: Int?) {
|
||||
if url.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.url = url
|
||||
self.mimeType = mimeType
|
||||
self.title = title
|
||||
self.sizeInBytes = sizeInBytes
|
||||
self.durationInSeconds = durationInSeconds
|
||||
}
|
||||
|
||||
// MARK: - Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(url)
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
|
||||
public static func ==(lhs: ParsedAttachment, rhs: ParsedAttachment) -> Bool {
|
||||
lhs.url == rhs.url && lhs.mimeType == rhs.mimeType && lhs.title == rhs.title && lhs.sizeInBytes == rhs.sizeInBytes && lhs.durationInSeconds == rhs.durationInSeconds
|
||||
}
|
||||
}
|
63
Parser/Sources/Parser/FeedParser/Feeds/ParsedAuthor.swift
Normal file
63
Parser/Sources/Parser/FeedParser/Feeds/ParsedAuthor.swift
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// ParsedAuthor.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/20/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class ParsedAuthor: Hashable, Codable, Sendable {
|
||||
|
||||
public let name: String?
|
||||
public let url: String?
|
||||
public let avatarURL: String?
|
||||
public let emailAddress: String?
|
||||
|
||||
public init(name: String?, url: String?, avatarURL: String?, emailAddress: String?) {
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.avatarURL = avatarURL
|
||||
self.emailAddress = emailAddress
|
||||
}
|
||||
|
||||
/// Use when the actual property is unknown. Guess based on contents of the string. (This is common with RSS.)
|
||||
convenience init(singleString: String) {
|
||||
|
||||
if singleString.contains("@") {
|
||||
self.init(name: nil, url: nil, avatarURL: nil, emailAddress: singleString)
|
||||
} else if singleString.lowercased().hasPrefix("http") {
|
||||
self.init(name: nil, url: singleString, avatarURL: nil, emailAddress: nil)
|
||||
} else {
|
||||
self.init(name: singleString, url: nil, avatarURL: nil, emailAddress: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
if let name {
|
||||
hasher.combine(name)
|
||||
}
|
||||
else if let url {
|
||||
hasher.combine(url)
|
||||
}
|
||||
else if let emailAddress {
|
||||
hasher.combine(emailAddress)
|
||||
}
|
||||
else if let avatarURL{
|
||||
hasher.combine(avatarURL)
|
||||
}
|
||||
else {
|
||||
hasher.combine("")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
|
||||
public static func ==(lhs: ParsedAuthor, rhs: ParsedAuthor) -> Bool {
|
||||
|
||||
lhs.name == rhs.name && lhs.url == rhs.url && lhs.avatarURL == rhs.avatarURL && lhs.emailAddress == rhs.emailAddress
|
||||
}
|
||||
}
|
42
Parser/Sources/Parser/FeedParser/Feeds/ParsedFeed.swift
Normal file
42
Parser/Sources/Parser/FeedParser/Feeds/ParsedFeed.swift
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// ParsedFeed.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/20/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class ParsedFeed: Sendable {
|
||||
|
||||
public let type: FeedType
|
||||
public let title: String?
|
||||
public let homePageURL: String?
|
||||
public let feedURL: String?
|
||||
public let language: String?
|
||||
public let feedDescription: String?
|
||||
public let nextURL: String?
|
||||
public let iconURL: String?
|
||||
public let faviconURL: String?
|
||||
public let authors: Set<ParsedAuthor>?
|
||||
public let expired: Bool
|
||||
public let hubs: Set<ParsedHub>?
|
||||
public let items: Set<ParsedItem>
|
||||
|
||||
public init(type: FeedType, title: String?, homePageURL: String?, feedURL: String?, language: String?, feedDescription: String?, nextURL: String?, iconURL: String?, faviconURL: String?, authors: Set<ParsedAuthor>?, expired: Bool, hubs: Set<ParsedHub>?, items: Set<ParsedItem>) {
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.homePageURL = homePageURL?.nilIfEmptyOrWhitespace
|
||||
self.feedURL = feedURL
|
||||
self.language = language
|
||||
self.feedDescription = feedDescription
|
||||
self.nextURL = nextURL
|
||||
self.iconURL = iconURL
|
||||
self.faviconURL = faviconURL
|
||||
self.authors = authors
|
||||
self.expired = expired
|
||||
self.hubs = hubs
|
||||
self.items = items
|
||||
}
|
||||
}
|
33
Parser/Sources/Parser/FeedParser/Feeds/ParsedHub.swift
Normal file
33
Parser/Sources/Parser/FeedParser/Feeds/ParsedHub.swift
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// ParsedHub.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/20/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class ParsedHub: Hashable, Sendable {
|
||||
|
||||
public let type: String
|
||||
public let url: String
|
||||
|
||||
init(type: String, url: String) {
|
||||
self.type = type
|
||||
self.url = url
|
||||
}
|
||||
|
||||
// MARK: - Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(type)
|
||||
hasher.combine(url)
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
|
||||
public static func ==(lhs: ParsedHub, rhs: ParsedHub) -> Bool {
|
||||
lhs.type == rhs.type && lhs.url == rhs.url
|
||||
}
|
||||
}
|
72
Parser/Sources/Parser/FeedParser/Feeds/ParsedItem.swift
Normal file
72
Parser/Sources/Parser/FeedParser/Feeds/ParsedItem.swift
Normal file
@ -0,0 +1,72 @@
|
||||
//
|
||||
// ParsedItem.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/20/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class ParsedItem: Hashable, Sendable {
|
||||
|
||||
public let syncServiceID: String? //Nil when not syncing
|
||||
public let uniqueID: String //RSS guid, for instance; may be calculated
|
||||
public let feedURL: String
|
||||
public let url: String?
|
||||
public let externalURL: String?
|
||||
public let title: String?
|
||||
public let language: String?
|
||||
public let contentHTML: String?
|
||||
public let contentText: String?
|
||||
public let summary: String?
|
||||
public let imageURL: String?
|
||||
public let bannerImageURL: String?
|
||||
public let datePublished: Date?
|
||||
public let dateModified: Date?
|
||||
public let authors: Set<ParsedAuthor>?
|
||||
public let tags: Set<String>?
|
||||
public let attachments: Set<ParsedAttachment>?
|
||||
|
||||
public init(syncServiceID: String?, uniqueID: String, feedURL: String, url: String?, externalURL: String?, title: String?,
|
||||
language: String?, contentHTML: String?, contentText: String?, summary: String?, imageURL: String?,
|
||||
bannerImageURL: String?,datePublished: Date?, dateModified: Date?, authors: Set<ParsedAuthor>?,
|
||||
tags: Set<String>?, attachments: Set<ParsedAttachment>?) {
|
||||
|
||||
self.syncServiceID = syncServiceID
|
||||
self.uniqueID = uniqueID
|
||||
self.feedURL = feedURL
|
||||
self.url = url
|
||||
self.externalURL = externalURL
|
||||
self.title = title
|
||||
self.language = language
|
||||
self.contentHTML = contentHTML
|
||||
self.contentText = contentText
|
||||
self.summary = summary
|
||||
self.imageURL = imageURL
|
||||
self.bannerImageURL = bannerImageURL
|
||||
self.datePublished = datePublished
|
||||
self.dateModified = dateModified
|
||||
self.authors = authors
|
||||
self.tags = tags
|
||||
self.attachments = attachments
|
||||
}
|
||||
|
||||
// MARK: - Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
if let syncServiceID = syncServiceID {
|
||||
hasher.combine(syncServiceID)
|
||||
}
|
||||
else {
|
||||
hasher.combine(uniqueID)
|
||||
hasher.combine(feedURL)
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: ParsedItem, rhs: ParsedItem) -> Bool {
|
||||
|
||||
lhs.syncServiceID == rhs.syncServiceID && lhs.uniqueID == rhs.uniqueID && lhs.feedURL == rhs.feedURL && lhs.url == rhs.url && lhs.externalURL == rhs.externalURL && lhs.title == rhs.title && lhs.language == rhs.language && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.summary == rhs.summary && lhs.imageURL == rhs.imageURL && lhs.bannerImageURL == rhs.bannerImageURL && lhs.datePublished == rhs.datePublished && lhs.dateModified == rhs.dateModified && lhs.authors == rhs.authors && lhs.tags == rhs.tags && lhs.attachments == rhs.attachments
|
||||
}
|
||||
}
|
||||
|
454
Parser/Sources/Parser/FeedParser/Feeds/XML/AtomParser.swift
Normal file
454
Parser/Sources/Parser/FeedParser/Feeds/XML/AtomParser.swift
Normal file
@ -0,0 +1,454 @@
|
||||
//
|
||||
// AtomParser.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/25/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
|
||||
final class AtomParser {
|
||||
|
||||
private var feedURL: String
|
||||
private let data: Data
|
||||
private let feed: RSSFeed
|
||||
|
||||
private var articles = [RSSArticle]()
|
||||
private var currentArticle: RSSArticle? {
|
||||
articles.last
|
||||
}
|
||||
|
||||
private var attributesStack = [StringDictionary]()
|
||||
private var currentAttributes: StringDictionary? {
|
||||
attributesStack.last
|
||||
}
|
||||
|
||||
private var parsingXHTML = false
|
||||
private var xhtmlString: String?
|
||||
|
||||
private var currentAuthor: RSSAuthor?
|
||||
private var parsingAuthor = false
|
||||
|
||||
private var parsingArticle = false
|
||||
private var parsingSource = false
|
||||
private var endFeedFound = false
|
||||
|
||||
static func parsedFeed(urlString: String, data: Data) -> RSSFeed {
|
||||
|
||||
let parser = AtomParser(urlString: urlString, data: data)
|
||||
parser.parse()
|
||||
return parser.feed
|
||||
}
|
||||
|
||||
init(urlString: String, data: Data) {
|
||||
self.feedURL = urlString
|
||||
self.data = data
|
||||
self.feed = RSSFeed(urlString: urlString)
|
||||
}
|
||||
}
|
||||
|
||||
private extension AtomParser {
|
||||
|
||||
func parse() {
|
||||
|
||||
let saxParser = SAXParser(delegate: self, data: data)
|
||||
saxParser.parse()
|
||||
feed.articles = articles
|
||||
}
|
||||
|
||||
private struct XMLName {
|
||||
static let entry = "entry".utf8CString
|
||||
static let content = "content".utf8CString
|
||||
static let summary = "summary".utf8CString
|
||||
static let link = "link".utf8CString
|
||||
static let feed = "feed".utf8CString
|
||||
static let source = "source".utf8CString
|
||||
static let author = "author".utf8CString
|
||||
static let name = "name".utf8CString
|
||||
static let email = "email".utf8CString
|
||||
static let uri = "uri".utf8CString
|
||||
static let title = "title".utf8CString
|
||||
static let id = "id".utf8CString
|
||||
static let published = "published".utf8CString
|
||||
static let updated = "updated".utf8CString
|
||||
static let issued = "issued".utf8CString
|
||||
static let modified = "modified".utf8CString
|
||||
}
|
||||
|
||||
private struct XMLString {
|
||||
static let rel = "rel"
|
||||
static let alternate = "alternate"
|
||||
static let related = "related"
|
||||
static let enclosure = "enclosure"
|
||||
static let href = "href"
|
||||
static let title = "title"
|
||||
static let type = "type"
|
||||
static let length = "length"
|
||||
static let xmlLang = "xml:lang"
|
||||
}
|
||||
|
||||
func currentString(_ saxParser: SAXParser) -> String? {
|
||||
|
||||
saxParser.currentStringWithTrimmedWhitespace
|
||||
}
|
||||
|
||||
func currentDate(_ saxParser: SAXParser) -> Date? {
|
||||
|
||||
guard let data = saxParser.currentCharacters else {
|
||||
assertionFailure("Unexpected nil saxParser.currentCharacters in AtomParser.currentDate")
|
||||
return nil
|
||||
}
|
||||
|
||||
return DateParser.date(data: data)
|
||||
}
|
||||
|
||||
func addFeedTitle(_ saxParser: SAXParser) {
|
||||
|
||||
guard feed.title == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
if let title = currentString(saxParser), !title.isEmpty {
|
||||
feed.title = title
|
||||
}
|
||||
}
|
||||
|
||||
func addFeedLink() {
|
||||
|
||||
guard feed.link == nil, let currentAttributes else {
|
||||
return
|
||||
}
|
||||
guard let link = currentAttributes[XMLString.href] else {
|
||||
return
|
||||
}
|
||||
|
||||
let isRelated: Bool = {
|
||||
if let related = currentAttributes[XMLString.rel], related == XMLString.alternate { // rel="alternate"
|
||||
return true
|
||||
}
|
||||
return currentAttributes.count == 1 // Example: <link href="https://www.allenpike.com/"/> — no rel or anything
|
||||
}()
|
||||
|
||||
if isRelated {
|
||||
feed.link = link
|
||||
}
|
||||
}
|
||||
|
||||
func addFeedLanguage() {
|
||||
|
||||
guard feed.language == nil, let currentAttributes else {
|
||||
return
|
||||
}
|
||||
|
||||
feed.language = currentAttributes[XMLString.xmlLang]
|
||||
}
|
||||
|
||||
func addArticle() {
|
||||
let article = RSSArticle(feedURL)
|
||||
articles.append(article)
|
||||
}
|
||||
|
||||
func addArticleElement(_ saxParser: SAXParser, _ localName: XMLPointer, _ prefix: XMLPointer?) {
|
||||
|
||||
guard prefix == nil else {
|
||||
return
|
||||
}
|
||||
guard let currentArticle else {
|
||||
assertionFailure("currentArticle must not be nil in AtomParser.addArticleElement")
|
||||
return
|
||||
}
|
||||
|
||||
if SAXEqualTags(localName, XMLName.id) {
|
||||
currentArticle.guid = currentString(saxParser)
|
||||
}
|
||||
|
||||
else if SAXEqualTags(localName, XMLName.title) {
|
||||
currentArticle.title = currentString(saxParser)
|
||||
}
|
||||
|
||||
else if SAXEqualTags(localName, XMLName.content) {
|
||||
addContent(saxParser, currentArticle)
|
||||
}
|
||||
|
||||
else if SAXEqualTags(localName, XMLName.summary) {
|
||||
addSummary(saxParser, currentArticle)
|
||||
}
|
||||
|
||||
else if SAXEqualTags(localName, XMLName.link) {
|
||||
addLink(currentArticle)
|
||||
}
|
||||
|
||||
else if SAXEqualTags(localName, XMLName.published) {
|
||||
currentArticle.datePublished = currentDate(saxParser)
|
||||
}
|
||||
|
||||
else if SAXEqualTags(localName, XMLName.updated) {
|
||||
currentArticle.dateModified = currentDate(saxParser)
|
||||
}
|
||||
|
||||
// Atom 0.3 dates
|
||||
else if SAXEqualTags(localName, XMLName.issued) {
|
||||
if currentArticle.datePublished == nil {
|
||||
currentArticle.datePublished = currentDate(saxParser)
|
||||
}
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.modified) {
|
||||
if currentArticle.dateModified == nil {
|
||||
currentArticle.dateModified = currentDate(saxParser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addContent(_ saxParser: SAXParser, _ article: RSSArticle) {
|
||||
|
||||
article.body = currentString(saxParser)
|
||||
}
|
||||
|
||||
func addSummary(_ saxParser: SAXParser, _ article: RSSArticle) {
|
||||
|
||||
guard article.body == nil else {
|
||||
return
|
||||
}
|
||||
article.body = currentString(saxParser)
|
||||
}
|
||||
|
||||
func addLink(_ article: RSSArticle) {
|
||||
|
||||
guard let attributes = currentAttributes else {
|
||||
return
|
||||
}
|
||||
guard let urlString = attributes[XMLString.href], !urlString.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
var rel = attributes[XMLString.rel]
|
||||
if rel?.isEmpty ?? true {
|
||||
rel = XMLString.alternate
|
||||
}
|
||||
|
||||
if rel == XMLString.related {
|
||||
if article.link == nil {
|
||||
article.link = urlString
|
||||
}
|
||||
}
|
||||
else if rel == XMLString.alternate {
|
||||
if article.permalink == nil {
|
||||
article.permalink = urlString
|
||||
}
|
||||
}
|
||||
else if rel == XMLString.enclosure {
|
||||
if let enclosure = enclosure(urlString, attributes) {
|
||||
article.addEnclosure(enclosure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enclosure(_ urlString: String, _ attributes: StringDictionary) -> RSSEnclosure? {
|
||||
|
||||
let enclosure = RSSEnclosure(url: urlString)
|
||||
enclosure.title = attributes[XMLString.title]
|
||||
enclosure.mimeType = attributes[XMLString.type]
|
||||
|
||||
if let lengthString = attributes[XMLString.length] {
|
||||
enclosure.length = Int(lengthString)
|
||||
}
|
||||
|
||||
return enclosure
|
||||
}
|
||||
|
||||
func addXHTMLTag(_ localName: XMLPointer) {
|
||||
|
||||
guard var xhtmlString else {
|
||||
assertionFailure("xhtmlString must not be nil when in addXHTMLTag.")
|
||||
return
|
||||
}
|
||||
|
||||
guard let name = String(xmlPointer: localName) else {
|
||||
assertionFailure("Unexpected failure converting XMLPointer to String in addXHTMLTag.")
|
||||
return
|
||||
}
|
||||
|
||||
xhtmlString.append("<")
|
||||
xhtmlString.append(name)
|
||||
|
||||
if let currentAttributes, currentAttributes.count > 0 {
|
||||
for (key, value) in currentAttributes {
|
||||
xhtmlString.append(" ")
|
||||
xhtmlString.append(key)
|
||||
xhtmlString.append("=\"")
|
||||
|
||||
let encodedValue = value.replacingOccurrences(of: "\"", with: """)
|
||||
xhtmlString.append(encodedValue)
|
||||
xhtmlString.append("\"")
|
||||
}
|
||||
}
|
||||
|
||||
xhtmlString.append(">")
|
||||
}
|
||||
}
|
||||
|
||||
extension AtomParser: SAXParserDelegate {
|
||||
|
||||
public func saxParser(_ saxParser: SAXParser, xmlStartElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafePointer<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafePointer<XMLPointer?>?) {
|
||||
|
||||
if endFeedFound {
|
||||
return
|
||||
}
|
||||
|
||||
let xmlAttributes = saxParser.attributesDictionary(attributes, attributeCount: attributeCount) ?? StringDictionary()
|
||||
attributesStack.append(xmlAttributes)
|
||||
|
||||
if parsingXHTML {
|
||||
addXHTMLTag(localName)
|
||||
return
|
||||
}
|
||||
|
||||
if SAXEqualTags(localName, XMLName.entry) {
|
||||
parsingArticle = true
|
||||
addArticle()
|
||||
return
|
||||
}
|
||||
|
||||
if SAXEqualTags(localName, XMLName.author) {
|
||||
parsingAuthor = true
|
||||
currentAuthor = RSSAuthor()
|
||||
return
|
||||
}
|
||||
|
||||
if SAXEqualTags(localName, XMLName.source) {
|
||||
parsingSource = true
|
||||
return
|
||||
}
|
||||
|
||||
let isContentTag = SAXEqualTags(localName, XMLName.content)
|
||||
let isSummaryTag = SAXEqualTags(localName, XMLName.summary)
|
||||
|
||||
if parsingArticle && (isContentTag || isSummaryTag) {
|
||||
|
||||
if isContentTag {
|
||||
currentArticle?.language = xmlAttributes["xml:lang"]
|
||||
}
|
||||
|
||||
let contentType = xmlAttributes["type"];
|
||||
if contentType == "xhtml" {
|
||||
parsingXHTML = true
|
||||
xhtmlString = ""
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !parsingArticle && SAXEqualTags(localName, XMLName.link) {
|
||||
addFeedLink()
|
||||
return
|
||||
}
|
||||
|
||||
if SAXEqualTags(localName, XMLName.feed) {
|
||||
addFeedLanguage()
|
||||
}
|
||||
|
||||
saxParser.beginStoringCharacters()
|
||||
}
|
||||
|
||||
public func saxParser(_ saxParser: SAXParser, xmlEndElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?) {
|
||||
|
||||
if SAXEqualTags(localName, XMLName.feed) {
|
||||
endFeedFound = true
|
||||
return
|
||||
}
|
||||
|
||||
if endFeedFound {
|
||||
return
|
||||
}
|
||||
|
||||
if parsingXHTML {
|
||||
|
||||
let isContentTag = SAXEqualTags(localName, XMLName.content)
|
||||
let isSummaryTag = SAXEqualTags(localName, XMLName.summary)
|
||||
|
||||
if parsingArticle && (isContentTag || isSummaryTag) {
|
||||
|
||||
if isContentTag {
|
||||
currentArticle?.body = xhtmlString
|
||||
}
|
||||
|
||||
else if isSummaryTag {
|
||||
if (currentArticle?.body?.count ?? 0) < 1 {
|
||||
currentArticle?.body = xhtmlString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isContentTag || isSummaryTag {
|
||||
parsingXHTML = false
|
||||
}
|
||||
|
||||
if var xhtmlString {
|
||||
if let localNameString = String(xmlPointer: localName) {
|
||||
xhtmlString.append("</")
|
||||
xhtmlString.append(localNameString)
|
||||
xhtmlString.append(">")
|
||||
}
|
||||
} else {
|
||||
assertionFailure("xhtmlString must not be nil when parsingXHTML in xmlEndElement.")
|
||||
}
|
||||
}
|
||||
|
||||
else if parsingAuthor {
|
||||
|
||||
if SAXEqualTags(localName, XMLName.author) {
|
||||
parsingAuthor = false
|
||||
if let currentAuthor, !currentAuthor.isEmpty() {
|
||||
currentArticle?.addAuthor(currentAuthor)
|
||||
}
|
||||
currentAuthor = nil
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.name) {
|
||||
currentAuthor?.name = saxParser.currentStringWithTrimmedWhitespace
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.email) {
|
||||
currentAuthor?.emailAddress = saxParser.currentStringWithTrimmedWhitespace
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.uri) {
|
||||
currentAuthor?.url = saxParser.currentStringWithTrimmedWhitespace
|
||||
}
|
||||
}
|
||||
|
||||
else if SAXEqualTags(localName, XMLName.entry) {
|
||||
parsingArticle = false
|
||||
}
|
||||
|
||||
else if parsingArticle && !parsingSource {
|
||||
addArticleElement(saxParser, localName, prefix)
|
||||
}
|
||||
|
||||
else if SAXEqualTags(localName, XMLName.source) {
|
||||
parsingSource = false
|
||||
}
|
||||
|
||||
else if !parsingArticle && !parsingSource && SAXEqualTags(localName, XMLName.title) {
|
||||
addFeedTitle(saxParser)
|
||||
}
|
||||
|
||||
_ = attributesStack.popLast()
|
||||
}
|
||||
|
||||
public func saxParser(_ saxParser: SAXParser, xmlCharactersFound: XMLPointer, count: Int) {
|
||||
|
||||
guard parsingXHTML else {
|
||||
return
|
||||
}
|
||||
guard var s = String(xmlPointer: xmlCharactersFound, count: count) else {
|
||||
return
|
||||
}
|
||||
|
||||
// libxml decodes all entities; we need to re-encode certain characters
|
||||
// (<, >, and &) when inside XHTML text content.
|
||||
s = s.replacingOccurrences(of: "<", with: "&;lt;")
|
||||
s = s.replacingOccurrences(of: ">", with: "&;gt;")
|
||||
s = s.replacingOccurrences(of: "&", with: "&")
|
||||
|
||||
xhtmlString = s
|
||||
}
|
||||
}
|
111
Parser/Sources/Parser/FeedParser/Feeds/XML/RSSArticle.swift
Normal file
111
Parser/Sources/Parser/FeedParser/Feeds/XML/RSSArticle.swift
Normal file
@ -0,0 +1,111 @@
|
||||
//
|
||||
// RSSArticle.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/27/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
//import FoundationExtras
|
||||
|
||||
final class RSSArticle {
|
||||
|
||||
var feedURL: String
|
||||
|
||||
/// An RSS guid, if present, or calculated from other attributes.
|
||||
/// Should be unique to the feed, but not necessarily unique
|
||||
/// across different feeds. (Not suitable for a database ID.)
|
||||
lazy var articleID: String = {
|
||||
if let guid {
|
||||
return guid
|
||||
}
|
||||
return calculatedArticleID()
|
||||
}()
|
||||
|
||||
var guid: String?
|
||||
var title: String?
|
||||
var body: String?
|
||||
var link: String?
|
||||
var permalink: String?
|
||||
var authors: [RSSAuthor]?
|
||||
var enclosures: [RSSEnclosure]?
|
||||
var datePublished: Date?
|
||||
var dateModified: Date?
|
||||
var dateParsed: Date
|
||||
var language: String?
|
||||
|
||||
init(_ feedURL: String) {
|
||||
self.feedURL = feedURL
|
||||
self.dateParsed = Date()
|
||||
}
|
||||
|
||||
func addEnclosure(_ enclosure: RSSEnclosure) {
|
||||
|
||||
if enclosures == nil {
|
||||
enclosures = [RSSEnclosure]()
|
||||
}
|
||||
enclosures!.append(enclosure)
|
||||
}
|
||||
|
||||
func addAuthor(_ author: RSSAuthor) {
|
||||
|
||||
if authors == nil {
|
||||
authors = [RSSAuthor]()
|
||||
}
|
||||
authors!.append(author)
|
||||
}
|
||||
}
|
||||
|
||||
private extension RSSArticle {
|
||||
|
||||
func calculatedArticleID() -> String {
|
||||
|
||||
// Concatenate a combination of properties when no guid. Then hash the result.
|
||||
// In general, feeds should have guids. When they don't, re-runs are very likely,
|
||||
// because there's no other 100% reliable way to determine identity.
|
||||
// This is intended to create an ID unique inside a feed, but not globally unique.
|
||||
// Not suitable for a database ID, in other words.
|
||||
|
||||
var s = ""
|
||||
|
||||
let datePublishedTimeStampString: String? = {
|
||||
guard let datePublished else {
|
||||
return nil
|
||||
}
|
||||
return String(format: "%.0f", datePublished.timeIntervalSince1970)
|
||||
}()
|
||||
|
||||
// Ideally we have a permalink and a pubDate.
|
||||
// Either one would probably be a good guid, but together they should be rock-solid.
|
||||
// (In theory. Feeds are buggy, though.)
|
||||
if let permalink, !permalink.isEmpty, let datePublishedTimeStampString {
|
||||
s.append(permalink)
|
||||
s.append(datePublishedTimeStampString)
|
||||
}
|
||||
else if let link, !link.isEmpty, let datePublishedTimeStampString {
|
||||
s.append(link)
|
||||
s.append(datePublishedTimeStampString)
|
||||
}
|
||||
else if let title, !title.isEmpty, let datePublishedTimeStampString {
|
||||
s.append(title)
|
||||
s.append(datePublishedTimeStampString)
|
||||
}
|
||||
else if let datePublishedTimeStampString {
|
||||
s.append(datePublishedTimeStampString)
|
||||
}
|
||||
else if let permalink, !permalink.isEmpty {
|
||||
s.append(permalink)
|
||||
}
|
||||
else if let link, !link.isEmpty {
|
||||
s.append(link)
|
||||
}
|
||||
else if let title, !title.isEmpty {
|
||||
s.append(title)
|
||||
}
|
||||
else if let body, !body.isEmpty {
|
||||
s.append(body)
|
||||
}
|
||||
|
||||
return s.md5String
|
||||
}
|
||||
}
|
40
Parser/Sources/Parser/FeedParser/Feeds/XML/RSSAuthor.swift
Normal file
40
Parser/Sources/Parser/FeedParser/Feeds/XML/RSSAuthor.swift
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// RSSAuthor.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/27/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class RSSAuthor {
|
||||
|
||||
var name: String?
|
||||
var url: String?
|
||||
var avatarURL: String?
|
||||
var emailAddress: String?
|
||||
|
||||
init(name: String? = nil, url: String? = nil, avatarURL: String? = nil, emailAddress: String? = nil) {
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.avatarURL = avatarURL
|
||||
self.emailAddress = emailAddress
|
||||
}
|
||||
|
||||
/// Use when the actual property is unknown. Guess based on contents of the string. (This is common with RSS.)
|
||||
convenience init(singleString: String) {
|
||||
|
||||
if singleString.contains("@") {
|
||||
self.init(emailAddress: singleString)
|
||||
} else if singleString.lowercased().hasPrefix("http") {
|
||||
self.init(url: singleString)
|
||||
} else {
|
||||
self.init(name: singleString)
|
||||
}
|
||||
}
|
||||
|
||||
func isEmpty() -> Bool {
|
||||
|
||||
name == nil && url == nil && avatarURL == nil && emailAddress == nil
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
//
|
||||
// RSSEnclosure.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/27/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class RSSEnclosure {
|
||||
|
||||
var url: String
|
||||
var length: Int?
|
||||
var mimeType: String?
|
||||
var title: String?
|
||||
|
||||
init(url: String) {
|
||||
self.url = url
|
||||
}
|
||||
}
|
22
Parser/Sources/Parser/FeedParser/Feeds/XML/RSSFeed.swift
Normal file
22
Parser/Sources/Parser/FeedParser/Feeds/XML/RSSFeed.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// RSSFeed.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/27/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class RSSFeed {
|
||||
|
||||
var urlString: String
|
||||
var title: String?
|
||||
var link: String?
|
||||
var language: String?
|
||||
|
||||
var articles: [RSSArticle]?
|
||||
|
||||
init(urlString: String) {
|
||||
self.urlString = urlString
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
//
|
||||
// RSSFeedTransformer.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/25/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct RSSFeedTransformer {
|
||||
|
||||
/// Turn an internal RSSFeed into a public ParsedFeed.
|
||||
static func parsedFeed(with feed: RSSFeed, feedType: FeedType) -> ParsedFeed {
|
||||
|
||||
let items = parsedItems(feed.articles)
|
||||
return ParsedFeed(type: feedType, title: feed.title, homePageURL: feed.link, feedURL: feed.urlString, language: feed.language, feedDescription: nil, nextURL: nil, iconURL: nil, faviconURL: nil, authors: nil, expired: false, hubs: nil, items: items)
|
||||
}
|
||||
}
|
||||
|
||||
private extension RSSFeedTransformer {
|
||||
|
||||
static func parsedItems(_ articles: [RSSArticle]?) -> Set<ParsedItem> {
|
||||
|
||||
guard let articles else {
|
||||
return Set<ParsedItem>()
|
||||
}
|
||||
|
||||
return Set(articles.map(parsedItem))
|
||||
}
|
||||
|
||||
static func parsedItem(_ article: RSSArticle) -> ParsedItem {
|
||||
|
||||
let uniqueID = article.articleID
|
||||
let url = article.permalink
|
||||
let externalURL = article.link
|
||||
let title = article.title
|
||||
let language = article.language
|
||||
let contentHTML = article.body
|
||||
let datePublished = article.datePublished
|
||||
let dateModified = article.dateModified
|
||||
let authors = parsedAuthors(article.authors)
|
||||
let attachments = parsedAttachments(article.enclosures)
|
||||
|
||||
return ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: article.feedURL, url: url, externalURL: externalURL, title: title, language: language, contentHTML: contentHTML, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: nil, attachments: attachments)
|
||||
}
|
||||
|
||||
static func parsedAuthors(_ authors: [RSSAuthor]?) -> Set<ParsedAuthor>? {
|
||||
|
||||
guard let authors = authors, !authors.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let transformedAuthors = authors.compactMap { (author) -> ParsedAuthor? in
|
||||
return ParsedAuthor(name: author.name, url: author.url, avatarURL: nil, emailAddress: author.emailAddress)
|
||||
}
|
||||
|
||||
return transformedAuthors.isEmpty ? nil : Set(transformedAuthors)
|
||||
}
|
||||
|
||||
static func parsedAttachments(_ enclosures: [RSSEnclosure]?) -> Set<ParsedAttachment>? {
|
||||
|
||||
guard let enclosures = enclosures, !enclosures.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let attachments = enclosures.compactMap { (enclosure) -> ParsedAttachment? in
|
||||
|
||||
let sizeInBytes = (enclosure.length ?? 0) > 0 ? enclosure.length : nil
|
||||
return ParsedAttachment(url: enclosure.url, mimeType: enclosure.mimeType, title: nil, sizeInBytes: sizeInBytes, durationInSeconds: nil)
|
||||
}
|
||||
|
||||
return attachments.isEmpty ? nil : Set(attachments)
|
||||
}
|
||||
}
|
366
Parser/Sources/Parser/FeedParser/Feeds/XML/RSSParser.swift
Normal file
366
Parser/Sources/Parser/FeedParser/Feeds/XML/RSSParser.swift
Normal file
@ -0,0 +1,366 @@
|
||||
//
|
||||
// RSSParser.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/25/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
|
||||
public final class RSSParser {
|
||||
|
||||
private let feedURL: String
|
||||
private let data: Data
|
||||
private let feed: RSSFeed
|
||||
private var articles = [RSSArticle]()
|
||||
private var currentArticle: RSSArticle? {
|
||||
articles.last
|
||||
}
|
||||
|
||||
private var endRSSFound = false
|
||||
private var isRDF = false
|
||||
private var parsingArticle = false
|
||||
private var parsingChannelImage = false
|
||||
private var parsingAuthor = false
|
||||
private var currentAttributes: StringDictionary?
|
||||
|
||||
static func parsedFeed(urlString: String, data: Data) -> RSSFeed {
|
||||
|
||||
let parser = RSSParser(urlString: urlString, data: data)
|
||||
parser.parse()
|
||||
return parser.feed
|
||||
}
|
||||
|
||||
init(urlString: String, data: Data) {
|
||||
self.feedURL = urlString
|
||||
self.data = data
|
||||
self.feed = RSSFeed(urlString: urlString)
|
||||
}
|
||||
}
|
||||
|
||||
private extension RSSParser {
|
||||
|
||||
func parse() {
|
||||
|
||||
let saxParser = SAXParser(delegate: self, data: data)
|
||||
saxParser.parse()
|
||||
feed.articles = articles
|
||||
}
|
||||
|
||||
private struct XMLName {
|
||||
static let uppercaseRDF = "RDF".utf8CString
|
||||
static let item = "item".utf8CString
|
||||
static let guid = "guid".utf8CString
|
||||
static let enclosure = "enclosure".utf8CString
|
||||
static let image = "image".utf8CString
|
||||
static let author = "author".utf8CString
|
||||
static let rss = "rss".utf8CString
|
||||
static let link = "link".utf8CString
|
||||
static let title = "title".utf8CString
|
||||
static let language = "language".utf8CString
|
||||
static let dc = "dc".utf8CString
|
||||
static let content = "content".utf8CString
|
||||
static let encoded = "encoded".utf8CString
|
||||
static let creator = "creator".utf8CString
|
||||
static let date = "date".utf8CString
|
||||
static let pubDate = "pubDate".utf8CString
|
||||
static let description = "description".utf8CString
|
||||
}
|
||||
|
||||
func addFeedElement(_ saxParser: SAXParser, _ localName: XMLPointer, _ prefix: XMLPointer?) {
|
||||
|
||||
guard prefix == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
if SAXEqualTags(localName, XMLName.link) {
|
||||
if feed.link == nil {
|
||||
feed.link = saxParser.currentString
|
||||
}
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.title) {
|
||||
feed.title = saxParser.currentString
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.language) {
|
||||
feed.language = saxParser.currentString
|
||||
}
|
||||
}
|
||||
|
||||
func addArticle() {
|
||||
let article = RSSArticle(feedURL)
|
||||
articles.append(article)
|
||||
}
|
||||
|
||||
func addArticleElement(_ saxParser: SAXParser, _ localName: XMLPointer, _ prefix: XMLPointer?) {
|
||||
|
||||
guard let currentArticle else {
|
||||
return
|
||||
}
|
||||
|
||||
if let prefix, SAXEqualTags(prefix, XMLName.dc) {
|
||||
addDCElement(saxParser, localName, currentArticle)
|
||||
return
|
||||
}
|
||||
|
||||
if let prefix, SAXEqualTags(prefix, XMLName.content) && SAXEqualTags(localName, XMLName.encoded) {
|
||||
if let currentString = saxParser.currentString, !currentString.isEmpty {
|
||||
currentArticle.body = currentString
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard prefix == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
if let currentString = saxParser.currentString {
|
||||
if SAXEqualTags(localName, XMLName.guid) {
|
||||
addGuid(currentString, currentArticle)
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.author) {
|
||||
addAuthorWithString(currentString, currentArticle)
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.link) {
|
||||
currentArticle.link = urlString(currentString)
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.description) {
|
||||
if currentArticle.body == nil {
|
||||
currentArticle.body = currentString
|
||||
}
|
||||
}
|
||||
else if !parsingAuthor && SAXEqualTags(localName, XMLName.title) {
|
||||
currentArticle.title = currentString
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.pubDate) {
|
||||
currentArticle.datePublished = currentDate(saxParser)
|
||||
}
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.enclosure), let currentAttributes {
|
||||
addEnclosure(currentAttributes, currentArticle)
|
||||
}
|
||||
}
|
||||
|
||||
func addDCElement(_ saxParser: SAXParser, _ localName: XMLPointer, _ currentArticle: RSSArticle) {
|
||||
|
||||
if SAXEqualTags(localName, XMLName.creator) {
|
||||
if let currentString = saxParser.currentString {
|
||||
addAuthorWithString(currentString, currentArticle)
|
||||
}
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.date) {
|
||||
currentArticle.datePublished = currentDate(saxParser)
|
||||
}
|
||||
}
|
||||
|
||||
static let isPermalinkKey = "isPermaLink"
|
||||
static let isPermalinkLowercaseKey = "ispermalink"
|
||||
static let falseValue = "false"
|
||||
|
||||
func addGuid(_ guid: String, _ currentArticle: RSSArticle) {
|
||||
|
||||
currentArticle.guid = guid
|
||||
|
||||
guard let currentAttributes else {
|
||||
return
|
||||
}
|
||||
|
||||
let isPermaLinkValue: String? = {
|
||||
|
||||
if let value = currentAttributes[Self.isPermalinkKey] {
|
||||
return value
|
||||
}
|
||||
// Allow for `ispermalink`, `isPermalink`, etc.
|
||||
for (key, value) in currentAttributes {
|
||||
if key.lowercased() == Self.isPermalinkLowercaseKey {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
// Spec: `isPermaLink is optional, its default value is true.`
|
||||
// https://cyber.harvard.edu/rss/rss.html#ltguidgtSubelementOfLtitemgt
|
||||
// Return only if non-nil and equal to false — otherwise it’s a permalink.
|
||||
if let isPermaLinkValue, isPermaLinkValue == Self.falseValue {
|
||||
return
|
||||
}
|
||||
|
||||
// Feed bug found in the wild: using a guid that’s not really a permalink
|
||||
// and not realizing that `isPermaLink` is true by default.
|
||||
if stringIsProbablyAURLOrRelativePath(guid) {
|
||||
currentArticle.permalink = urlString(guid)
|
||||
}
|
||||
}
|
||||
|
||||
func stringIsProbablyAURLOrRelativePath(_ s: String) -> Bool {
|
||||
|
||||
// The RSS guid is defined as a permalink, except when it appears like this:
|
||||
// `<guid isPermaLink="false">some—identifier</guid>`
|
||||
// However, people often seem to think it’s *not* a permalink by default, even
|
||||
// though it is. So we try to detect the situation where the value is not a URL string,
|
||||
// and not even a relative path. This may need to evolve over time.
|
||||
|
||||
if !s.contains("/") {
|
||||
// This seems to be just about the best possible check.
|
||||
// Bad guids are often just integers, for instance.
|
||||
return false
|
||||
}
|
||||
|
||||
if s.lowercased().hasPrefix("tag:") {
|
||||
// A common non-URL guid form starts with `tag:`.
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/// Do best attempt at turning a string into a URL string.
|
||||
///
|
||||
/// If it already appears to be a URL, return it.
|
||||
/// Otherwise, treat it like a relative URL and resolve using
|
||||
/// the URL of the home page of the feed (if available)
|
||||
/// or the URL of the feed.
|
||||
///
|
||||
/// The returned value is not guaranteed to be a valid URL string.
|
||||
/// It’s a best attempt without going to heroic lengths.
|
||||
func urlString(_ s: String) -> String {
|
||||
|
||||
if s.lowercased().hasPrefix("http") {
|
||||
return s
|
||||
}
|
||||
|
||||
let baseURLString = feed.link ?? feedURL
|
||||
guard let baseURL = URL(string: baseURLString) else {
|
||||
return s
|
||||
}
|
||||
guard let resolvedURL = URL(string: s, relativeTo: baseURL) else {
|
||||
return s
|
||||
}
|
||||
|
||||
return resolvedURL.absoluteString
|
||||
}
|
||||
|
||||
func addAuthorWithString(_ authorString: String, _ currentArticle: RSSArticle) {
|
||||
|
||||
if authorString.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
let author = RSSAuthor(singleString: authorString)
|
||||
currentArticle.addAuthor(author)
|
||||
}
|
||||
|
||||
private struct EnclosureKey {
|
||||
static let url = "url"
|
||||
static let length = "length"
|
||||
static let type = "type"
|
||||
}
|
||||
|
||||
func addEnclosure(_ attributes: StringDictionary, _ currentArticle: RSSArticle) {
|
||||
|
||||
guard let url = attributes[EnclosureKey.url], !url.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let enclosure = RSSEnclosure(url: url)
|
||||
if let lengthValue = attributes[EnclosureKey.length], let length = Int(lengthValue) {
|
||||
enclosure.length = length
|
||||
}
|
||||
enclosure.mimeType = attributes[EnclosureKey.type]
|
||||
|
||||
currentArticle.addEnclosure(enclosure)
|
||||
}
|
||||
|
||||
func currentDate(_ saxParser: SAXParser) -> Date? {
|
||||
|
||||
guard let data = saxParser.currentCharacters else {
|
||||
return nil
|
||||
}
|
||||
return DateParser.date(data: data)
|
||||
}
|
||||
}
|
||||
|
||||
extension RSSParser: SAXParserDelegate {
|
||||
|
||||
static let rdfAbout = "rdf:about"
|
||||
|
||||
public func saxParser(_ saxParser: SAXParser, xmlStartElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafePointer<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafePointer<XMLPointer?>?) {
|
||||
|
||||
if endRSSFound {
|
||||
return
|
||||
}
|
||||
|
||||
if SAXEqualTags(localName, XMLName.uppercaseRDF) {
|
||||
isRDF = true
|
||||
return
|
||||
}
|
||||
|
||||
var xmlAttributes: StringDictionary? = nil
|
||||
if (isRDF && SAXEqualTags(localName, XMLName.item)) || SAXEqualTags(localName, XMLName.guid) || SAXEqualTags(localName, XMLName.enclosure) {
|
||||
xmlAttributes = saxParser.attributesDictionary(attributes, attributeCount: attributeCount)
|
||||
}
|
||||
if currentAttributes != xmlAttributes {
|
||||
currentAttributes = xmlAttributes
|
||||
}
|
||||
|
||||
if prefix == nil && SAXEqualTags(localName, XMLName.item) {
|
||||
addArticle()
|
||||
parsingArticle = true
|
||||
|
||||
if isRDF, let rdfGuid = xmlAttributes?[Self.rdfAbout], let currentArticle { // RSS 1.0 guid
|
||||
currentArticle.guid = rdfGuid
|
||||
currentArticle.permalink = rdfGuid
|
||||
}
|
||||
}
|
||||
else if prefix == nil && SAXEqualTags(localName, XMLName.image) {
|
||||
parsingChannelImage = true
|
||||
}
|
||||
else if prefix == nil && SAXEqualTags(localName, XMLName.author) {
|
||||
if parsingArticle {
|
||||
parsingAuthor = true
|
||||
}
|
||||
}
|
||||
|
||||
if !parsingChannelImage {
|
||||
saxParser.beginStoringCharacters()
|
||||
}
|
||||
}
|
||||
|
||||
public func saxParser(_ saxParser: SAXParser, xmlEndElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?) {
|
||||
|
||||
if endRSSFound {
|
||||
return
|
||||
}
|
||||
|
||||
if isRDF && SAXEqualTags(localName, XMLName.uppercaseRDF) {
|
||||
endRSSFound = true
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.rss) {
|
||||
endRSSFound = true
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.image) {
|
||||
parsingChannelImage = false
|
||||
}
|
||||
else if SAXEqualTags(localName, XMLName.item) {
|
||||
parsingArticle = false
|
||||
}
|
||||
else if parsingArticle {
|
||||
addArticleElement(saxParser, localName, prefix)
|
||||
if SAXEqualTags(localName, XMLName.author) {
|
||||
parsingAuthor = false
|
||||
}
|
||||
}
|
||||
else if !parsingChannelImage {
|
||||
addFeedElement(saxParser, localName, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
public func saxParser(_ saxParser: SAXParser, xmlCharactersFound: XMLPointer, count: Int) {
|
||||
|
||||
// Required method.
|
||||
}
|
||||
}
|
||||
|
12
Parser/Sources/Parser/FeedParser/JSON/JSONTypes.swift
Normal file
12
Parser/Sources/Parser/FeedParser/JSON/JSONTypes.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// JSONDictionary.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/24/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias JSONDictionary = [String: Any]
|
||||
public typealias JSONArray = [JSONDictionary]
|
27
Parser/Sources/Parser/FeedParser/JSON/JSONUtilities.swift
Normal file
27
Parser/Sources/Parser/FeedParser/JSON/JSONUtilities.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// JSONUtilities.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 12/10/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct JSONUtilities {
|
||||
|
||||
public static func object(with data: Data) -> Any? {
|
||||
|
||||
return try? JSONSerialization.jsonObject(with: data)
|
||||
}
|
||||
|
||||
public static func dictionary(with data: Data) -> JSONDictionary? {
|
||||
|
||||
return object(with: data) as? JSONDictionary
|
||||
}
|
||||
|
||||
public static func array(with data: Data) -> JSONArray? {
|
||||
|
||||
return object(with: data) as? JSONArray
|
||||
}
|
||||
}
|
446
Parser/Sources/Parser/HTMLParser/HTMLEntityDecoder.swift
Normal file
446
Parser/Sources/Parser/HTMLParser/HTMLEntityDecoder.swift
Normal file
@ -0,0 +1,446 @@
|
||||
//
|
||||
// HTMLEntityDecoder.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 9/26/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class HTMLEntityDecoder {
|
||||
|
||||
public static func decodedString(_ encodedString: String) -> String {
|
||||
|
||||
var didDecodeAtLeastOneEntity = false
|
||||
|
||||
// If `withContiguousStorageIfAvailable` works, then we can avoid copying memory.
|
||||
var result: String? = encodedString.utf8.withContiguousStorageIfAvailable { buffer in
|
||||
return decodedEntities(buffer, &didDecodeAtLeastOneEntity)
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
let d = Data(encodedString.utf8)
|
||||
result = d.withUnsafeBytes { bytes in
|
||||
let buffer = bytes.bindMemory(to: UInt8.self)
|
||||
return decodedEntities(buffer, &didDecodeAtLeastOneEntity)
|
||||
}
|
||||
}
|
||||
|
||||
if let result {
|
||||
if didDecodeAtLeastOneEntity {
|
||||
return result
|
||||
}
|
||||
return encodedString
|
||||
}
|
||||
|
||||
assertionFailure("Expected result but got nil.")
|
||||
return encodedString
|
||||
}
|
||||
}
|
||||
|
||||
private let ampersandCharacter = Character("&").asciiValue!
|
||||
private let numberSignCharacter = Character("#").asciiValue!
|
||||
private let xCharacter = Character("x").asciiValue!
|
||||
private let XCharacter = Character("X").asciiValue!
|
||||
private let semicolonCharacter = Character(";").asciiValue!
|
||||
|
||||
private let zeroCharacter = Character("0").asciiValue!
|
||||
private let nineCharacter = Character("9").asciiValue!
|
||||
private let aCharacter = Character("a").asciiValue!
|
||||
private let fCharacter = Character("f").asciiValue!
|
||||
private let zCharacter = Character("z").asciiValue!
|
||||
private let ACharacter = Character("A").asciiValue!
|
||||
private let FCharacter = Character("F").asciiValue!
|
||||
private let ZCharacter = Character("Z").asciiValue!
|
||||
|
||||
private let maxUnicodeNumber = 0x10FFFF
|
||||
|
||||
private func decodedEntities(_ sourceBuffer: UnsafeBufferPointer<UInt8>, _ didDecodeAtLeastOneEntity: inout Bool) -> String {
|
||||
|
||||
let byteCount = sourceBuffer.count
|
||||
let resultBufferByteCount = byteCount + 1
|
||||
|
||||
// Allocate a destination buffer for the result string. It can be the same size
|
||||
// as the source string buffer, since decoding HTML entities will only make it smaller.
|
||||
// Same size plus 1, that is, for null-termination.
|
||||
let resultBuffer = UnsafeMutableRawPointer.allocate(byteCount: resultBufferByteCount, alignment: MemoryLayout<UInt8>.alignment)
|
||||
defer {
|
||||
resultBuffer.deallocate()
|
||||
}
|
||||
|
||||
resultBuffer.initializeMemory(as: UInt8.self, repeating: 0, count: resultBufferByteCount)
|
||||
let result = resultBuffer.assumingMemoryBound(to: UInt8.self)
|
||||
|
||||
var sourceLocation = 0
|
||||
var resultLocation = 0
|
||||
|
||||
while sourceLocation < byteCount {
|
||||
|
||||
let ch = sourceBuffer[sourceLocation]
|
||||
|
||||
var decodedEntity: String? = nil
|
||||
|
||||
if ch == ampersandCharacter {
|
||||
decodedEntity = decodedEntityValue(sourceBuffer, byteCount, &sourceLocation)
|
||||
}
|
||||
|
||||
if let decodedEntity {
|
||||
addDecodedEntity(decodedEntity, result, byteCount, &resultLocation)
|
||||
didDecodeAtLeastOneEntity = true
|
||||
sourceLocation += 1
|
||||
continue
|
||||
}
|
||||
|
||||
result[resultLocation] = ch
|
||||
|
||||
resultLocation += 1
|
||||
sourceLocation += 1
|
||||
}
|
||||
|
||||
let cString = resultBuffer.assumingMemoryBound(to: CChar.self)
|
||||
return String(cString: cString)
|
||||
}
|
||||
|
||||
private func addDecodedEntity(_ decodedEntity: String, _ result: UnsafeMutablePointer<UInt8>, _ resultByteCount: Int, _ resultLocation: inout Int) {
|
||||
|
||||
let utf8Bytes = Array(decodedEntity.utf8)
|
||||
precondition(resultLocation + utf8Bytes.count <= resultByteCount)
|
||||
|
||||
for byte in utf8Bytes {
|
||||
result[resultLocation] = byte
|
||||
resultLocation += 1
|
||||
}
|
||||
}
|
||||
|
||||
private func decodedEntityValue(_ buffer: UnsafeBufferPointer<UInt8>, _ byteCount: Int, _ sourceLocation: inout Int) -> /*[UInt8]?*/ String? {
|
||||
|
||||
guard let rawEntity = rawEntityValue(buffer, byteCount, &sourceLocation) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return decodedRawEntityValue(rawEntity)
|
||||
}
|
||||
|
||||
private func decodedRawEntityValue(_ rawEntity: ContiguousArray<UInt8>) -> String? {
|
||||
|
||||
var entityCharacters = [UInt8]()
|
||||
for character in rawEntity {
|
||||
if character == 0 {
|
||||
break
|
||||
}
|
||||
entityCharacters.append(character)
|
||||
}
|
||||
|
||||
let key = String(decoding: entityCharacters, as: UTF8.self)
|
||||
if let entityString = entitiesDictionary[key] {
|
||||
return entityString
|
||||
}
|
||||
|
||||
if rawEntity[0] == numberSignCharacter {
|
||||
if let entityString = decodedNumericEntity(rawEntity) {
|
||||
return entityString
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private func decodedNumericEntity(_ rawEntity: ContiguousArray<UInt8>) -> String? {
|
||||
|
||||
assert(rawEntity[0] == numberSignCharacter)
|
||||
|
||||
var decodedNumber: UInt32?
|
||||
|
||||
if rawEntity[1] == xCharacter || rawEntity[1] == XCharacter { // Hex?
|
||||
decodedNumber = decodedHexEntity(rawEntity)
|
||||
}
|
||||
else {
|
||||
decodedNumber = decodedDecimalEntity(rawEntity)
|
||||
}
|
||||
|
||||
if let decodedNumber {
|
||||
return stringWithValue(decodedNumber)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func decodedHexEntity(_ rawEntity: ContiguousArray<UInt8>) -> UInt32? {
|
||||
|
||||
assert(rawEntity[0] == numberSignCharacter)
|
||||
assert(rawEntity[1] == xCharacter || rawEntity[1] == XCharacter)
|
||||
|
||||
var number: UInt32 = 0
|
||||
var i = 0
|
||||
|
||||
for byte in rawEntity {
|
||||
|
||||
if i < 2 { // Skip first two characters: #x or #X
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if byte == 0 { // rawEntity is null-terminated
|
||||
break
|
||||
}
|
||||
|
||||
var digit: UInt32?
|
||||
|
||||
switch byte {
|
||||
case zeroCharacter...nineCharacter: // 0-9
|
||||
digit = UInt32(byte - zeroCharacter)
|
||||
case aCharacter...fCharacter: // a-f
|
||||
digit = UInt32((byte - aCharacter) + 10)
|
||||
case ACharacter...FCharacter: // a-f
|
||||
digit = UInt32((byte - ACharacter) + 10)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let digit else {
|
||||
return nil // Shouldn’t get here — handled by default case — but we need to bind digit
|
||||
}
|
||||
|
||||
number = (number * 16) + digit
|
||||
if number > maxUnicodeNumber {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if number == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return number
|
||||
}
|
||||
|
||||
private func decodedDecimalEntity(_ rawEntity: ContiguousArray<UInt8>) -> UInt32? {
|
||||
|
||||
assert(rawEntity[0] == numberSignCharacter)
|
||||
assert(rawEntity[1] != xCharacter && rawEntity[1] != XCharacter) // not hex
|
||||
|
||||
var number: UInt32 = 0
|
||||
var isFirstCharacter = true
|
||||
|
||||
// Convert, for instance, [51, 57] to 39
|
||||
for byte in rawEntity {
|
||||
|
||||
if isFirstCharacter { // first character is #
|
||||
isFirstCharacter = false
|
||||
continue
|
||||
}
|
||||
|
||||
if byte == 0 { // rawEntity is null-terminated
|
||||
break
|
||||
}
|
||||
|
||||
// Be sure it’s a digit 0-9
|
||||
if byte < zeroCharacter || byte > nineCharacter {
|
||||
return nil
|
||||
}
|
||||
let digit = UInt32(byte - zeroCharacter)
|
||||
number = (number * 10) + digit
|
||||
if number > maxUnicodeNumber {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if number == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return number
|
||||
}
|
||||
|
||||
private func rawEntityValue(_ buffer: UnsafeBufferPointer<UInt8>, _ byteCount: Int, _ sourceLocation: inout Int) -> ContiguousArray<UInt8>? {
|
||||
|
||||
// sourceLocation points to the & character.
|
||||
let savedSourceLocation = sourceLocation
|
||||
let maxEntityCharacters = 36 // Longest current entity is ∳
|
||||
|
||||
var entityCharacters: ContiguousArray<UInt8> = [0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, // 20 characters
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, // 35 characters
|
||||
0] // nil-terminated last character
|
||||
|
||||
var entityCharactersIndex = 0
|
||||
|
||||
while true {
|
||||
|
||||
sourceLocation += 1
|
||||
if sourceLocation >= byteCount || entityCharactersIndex >= maxEntityCharacters { // did not parse entity
|
||||
sourceLocation = savedSourceLocation
|
||||
return nil
|
||||
}
|
||||
|
||||
let ch = buffer[sourceLocation]
|
||||
if ch == semicolonCharacter { // End of entity?
|
||||
return entityCharacters
|
||||
}
|
||||
|
||||
// Make sure character is in 0-9, A-Z, a-z, #
|
||||
if ch < zeroCharacter && ch != numberSignCharacter {
|
||||
return nil
|
||||
}
|
||||
if ch > nineCharacter && ch < ACharacter {
|
||||
return nil
|
||||
}
|
||||
if ch > ZCharacter && ch < aCharacter {
|
||||
return nil
|
||||
}
|
||||
if ch > zCharacter {
|
||||
return nil
|
||||
}
|
||||
|
||||
entityCharacters[entityCharactersIndex] = ch
|
||||
|
||||
entityCharactersIndex += 1
|
||||
}
|
||||
}
|
||||
|
||||
private func stringWithValue(_ value: UInt32) -> String? {
|
||||
|
||||
// From WebCore's HTMLEntityParser
|
||||
let windowsLatin1ExtensionArray: [UInt32] = [
|
||||
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87
|
||||
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, // 88-8F
|
||||
0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97
|
||||
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178 // 98-9F
|
||||
]
|
||||
|
||||
var modifiedValue = value
|
||||
|
||||
if value >= 128 && value < 160 {
|
||||
modifiedValue = windowsLatin1ExtensionArray[Int(modifiedValue - 0x80)]
|
||||
}
|
||||
|
||||
modifiedValue = CFSwapInt32HostToLittle(modifiedValue)
|
||||
|
||||
let data = Data(bytes: &modifiedValue, count: MemoryLayout.size(ofValue: modifiedValue))
|
||||
|
||||
return String(data: data, encoding: .utf32LittleEndian)
|
||||
}
|
||||
|
||||
private let entitiesDictionary =
|
||||
[
|
||||
"AElig": "Æ",
|
||||
"Aacute": "Á",
|
||||
"Acirc": "Â",
|
||||
"Agrave": "À",
|
||||
"Aring": "Å",
|
||||
"Atilde": "Ã",
|
||||
"Auml": "Ä",
|
||||
"Ccedil": "Ç",
|
||||
"Dstrok": "Ð",
|
||||
"ETH": "Ð",
|
||||
"Eacute": "É",
|
||||
"Ecirc": "Ê",
|
||||
"Egrave": "È",
|
||||
"Euml": "Ë",
|
||||
"Iacute": "Í",
|
||||
"Icirc": "Î",
|
||||
"Igrave": "Ì",
|
||||
"Iuml": "Ï",
|
||||
"Ntilde": "Ñ",
|
||||
"Oacute": "Ó",
|
||||
"Ocirc": "Ô",
|
||||
"Ograve": "Ò",
|
||||
"Oslash": "Ø",
|
||||
"Otilde": "Õ",
|
||||
"Ouml": "Ö",
|
||||
"Pi": "Π",
|
||||
"THORN": "Þ",
|
||||
"Uacute": "Ú",
|
||||
"Ucirc": "Û",
|
||||
"Ugrave": "Ù",
|
||||
"Uuml": "Ü",
|
||||
"Yacute": "Y",
|
||||
"aacute": "á",
|
||||
"acirc": "â",
|
||||
"acute": "´",
|
||||
"aelig": "æ",
|
||||
"agrave": "à",
|
||||
"amp": "&",
|
||||
"apos": "'",
|
||||
"aring": "å",
|
||||
"atilde": "ã",
|
||||
"auml": "ä",
|
||||
"brkbar": "¦",
|
||||
"brvbar": "¦",
|
||||
"ccedil": "ç",
|
||||
"cedil": "¸",
|
||||
"cent": "¢",
|
||||
"copy": "©",
|
||||
"curren": "¤",
|
||||
"deg": "°",
|
||||
"die": "¨",
|
||||
"divide": "÷",
|
||||
"eacute": "é",
|
||||
"ecirc": "ê",
|
||||
"egrave": "è",
|
||||
"eth": "ð",
|
||||
"euml": "ë",
|
||||
"euro": "€",
|
||||
"frac12": "½",
|
||||
"frac14": "¼",
|
||||
"frac34": "¾",
|
||||
"gt": ">",
|
||||
"hearts": "♥",
|
||||
"hellip": "…",
|
||||
"iacute": "í",
|
||||
"icirc": "î",
|
||||
"iexcl": "¡",
|
||||
"igrave": "ì",
|
||||
"iquest": "¿",
|
||||
"iuml": "ï",
|
||||
"laquo": "«",
|
||||
"ldquo": "“",
|
||||
"lsquo": "‘",
|
||||
"lt": "<",
|
||||
"macr": "¯",
|
||||
"mdash": "—",
|
||||
"micro": "µ",
|
||||
"middot": "·",
|
||||
"ndash": "–",
|
||||
"not": "¬",
|
||||
"ntilde": "ñ",
|
||||
"oacute": "ó",
|
||||
"ocirc": "ô",
|
||||
"ograve": "ò",
|
||||
"ordf": "ª",
|
||||
"ordm": "º",
|
||||
"oslash": "ø",
|
||||
"otilde": "õ",
|
||||
"ouml": "ö",
|
||||
"para": "¶",
|
||||
"pi": "π",
|
||||
"plusmn": "±",
|
||||
"pound": "£",
|
||||
"quot": "\"",
|
||||
"raquo": "»",
|
||||
"rdquo": "”",
|
||||
"reg": "®",
|
||||
"rsquo": "’",
|
||||
"sect": "§",
|
||||
"shy": stringWithValue(173),
|
||||
"sup1": "¹",
|
||||
"sup2": "²",
|
||||
"sup3": "³",
|
||||
"szlig": "ß",
|
||||
"thorn": "þ",
|
||||
"times": "×",
|
||||
"trade": "™",
|
||||
"uacute": "ú",
|
||||
"ucirc": "û",
|
||||
"ugrave": "ù",
|
||||
"uml": "¨",
|
||||
"uuml": "ü",
|
||||
"yacute": "y",
|
||||
"yen": "¥",
|
||||
"yuml": "ÿ",
|
||||
"infin": "∞",
|
||||
"nbsp": stringWithValue(160)
|
||||
]
|
22
Parser/Sources/Parser/HTMLParser/HTMLLink.swift
Normal file
22
Parser/Sources/Parser/HTMLParser/HTMLLink.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// HTMLLink.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 9/21/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class HTMLLink {
|
||||
|
||||
public var urlString: String? // Absolute URL string
|
||||
public var text: String?
|
||||
public var title: String? // Title attribute inside anchor tag
|
||||
|
||||
init(urlString: String? = nil, text: String? = nil, title: String? = nil) {
|
||||
|
||||
self.urlString = urlString
|
||||
self.text = text
|
||||
self.title = title
|
||||
}
|
||||
}
|
120
Parser/Sources/Parser/HTMLParser/HTMLLinkParser.swift
Normal file
120
Parser/Sources/Parser/HTMLParser/HTMLLinkParser.swift
Normal file
@ -0,0 +1,120 @@
|
||||
//
|
||||
// HTMLLinkParser.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 9/21/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import os
|
||||
|
||||
public final class HTMLLinkParser {
|
||||
|
||||
public private(set) var links = [HTMLLink]()
|
||||
private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "HTMLLinkParser")
|
||||
|
||||
private let parserData: ParserData
|
||||
private let baseURL: URL?
|
||||
|
||||
public static func htmlLinks(with parserData: ParserData) -> [HTMLLink] {
|
||||
|
||||
let parser = HTMLLinkParser(parserData)
|
||||
parser.parse()
|
||||
return parser.links
|
||||
}
|
||||
|
||||
init(_ parserData: ParserData) {
|
||||
|
||||
self.parserData = parserData
|
||||
self.baseURL = URL(string: parserData.url)
|
||||
}
|
||||
}
|
||||
|
||||
private extension HTMLLinkParser {
|
||||
|
||||
func parse() {
|
||||
|
||||
let htmlParser = SAXHTMLParser(delegate: self, data: parserData.data)
|
||||
htmlParser.parse()
|
||||
}
|
||||
}
|
||||
|
||||
extension HTMLLinkParser: SAXHTMLParserDelegate {
|
||||
|
||||
private var currentLink: HTMLLink? {
|
||||
links.last
|
||||
}
|
||||
|
||||
private struct HTMLAttributeName {
|
||||
static let href = "href"
|
||||
static let title = "title"
|
||||
}
|
||||
|
||||
private func title(with attributesDictionary: StringDictionary) -> String? {
|
||||
|
||||
attributesDictionary.object(forCaseInsensitiveKey: HTMLAttributeName.title)
|
||||
}
|
||||
|
||||
private func urlString(with attributesDictionary: StringDictionary) -> String? {
|
||||
|
||||
guard let href = attributesDictionary.object(forCaseInsensitiveKey: HTMLAttributeName.href), !href.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let baseURL, let absoluteURL = URL(string: href, relativeTo: baseURL) else {
|
||||
Self.logger.info("Expected to create URL but got nil with \(href)")
|
||||
return nil
|
||||
}
|
||||
|
||||
return absoluteURL.absoluteString
|
||||
}
|
||||
|
||||
private func handleLinkAttributes(_ attributesDictionary: StringDictionary) {
|
||||
|
||||
guard let currentLink else {
|
||||
assertionFailure("currentLink must not be nil")
|
||||
return
|
||||
}
|
||||
|
||||
currentLink.urlString = urlString(with: attributesDictionary)
|
||||
currentLink.title = title(with: attributesDictionary)
|
||||
}
|
||||
|
||||
private struct HTMLName {
|
||||
static let a = "a".utf8CString
|
||||
}
|
||||
|
||||
public func saxHTMLParser(_ saxHTMLParser: SAXHTMLParser, startElement name: XMLPointer, attributes: UnsafePointer<XMLPointer?>?) {
|
||||
|
||||
guard SAXEqualTags(name, HTMLName.a) else {
|
||||
return
|
||||
}
|
||||
|
||||
let link = HTMLLink()
|
||||
links.append(link)
|
||||
|
||||
if let attributesDictionary = saxHTMLParser.attributesDictionary(attributes) {
|
||||
handleLinkAttributes(attributesDictionary)
|
||||
}
|
||||
|
||||
saxHTMLParser.beginStoringCharacters()
|
||||
}
|
||||
|
||||
public func saxHTMLParser(_ saxHTMLParser: SAXHTMLParser, endElement name: XMLPointer) {
|
||||
|
||||
guard SAXEqualTags(name, HTMLName.a) else {
|
||||
return
|
||||
}
|
||||
guard let currentLink else {
|
||||
assertionFailure("currentLink must not be nil.")
|
||||
return
|
||||
}
|
||||
|
||||
currentLink.text = saxHTMLParser.currentStringWithTrimmedWhitespace
|
||||
}
|
||||
|
||||
public func saxHTMLParser(_: SAXHTMLParser, charactersFound: XMLPointer, count: Int) {
|
||||
// Nothing needed.
|
||||
}
|
||||
}
|
437
Parser/Sources/Parser/HTMLParser/HTMLMetadata.swift
Normal file
437
Parser/Sources/Parser/HTMLParser/HTMLMetadata.swift
Normal file
@ -0,0 +1,437 @@
|
||||
//
|
||||
// HTMLMetadata.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 9/22/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class HTMLMetadata: Sendable {
|
||||
|
||||
public let baseURLString: String
|
||||
public let tags: [HTMLTag]
|
||||
public let favicons: [HTMLMetadataFavicon]?
|
||||
public let appleTouchIcons: [HTMLMetadataAppleTouchIcon]?
|
||||
public let feedLinks: [HTMLMetadataFeedLink]?
|
||||
public let openGraphProperties: HTMLOpenGraphProperties?
|
||||
public let twitterProperties: HTMLTwitterProperties?
|
||||
|
||||
init(_ urlString: String, _ tags: [HTMLTag]) {
|
||||
|
||||
self.baseURLString = urlString
|
||||
self.tags = tags
|
||||
|
||||
self.favicons = Self.resolvedFaviconLinks(urlString, tags)
|
||||
|
||||
if let appleTouchIconTags = Self.appleTouchIconTags(tags) {
|
||||
self.appleTouchIcons = appleTouchIconTags.map { htmlTag in
|
||||
HTMLMetadataAppleTouchIcon(urlString, htmlTag)
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.appleTouchIcons = nil
|
||||
}
|
||||
|
||||
if let feedLinkTags = Self.feedLinkTags(tags) {
|
||||
self.feedLinks = feedLinkTags.map { htmlTag in
|
||||
HTMLMetadataFeedLink(urlString, htmlTag)
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.feedLinks = nil
|
||||
}
|
||||
|
||||
self.openGraphProperties = HTMLOpenGraphProperties(urlString, tags)
|
||||
self.twitterProperties = HTMLTwitterProperties(urlString, tags)
|
||||
}
|
||||
|
||||
static func resolvedFaviconLinks(_ baseURLString: String, _ tags: [HTMLTag]) -> [HTMLMetadataFavicon]? {
|
||||
|
||||
guard let linkTags = linkTagsWithMatchingRel("icon", tags) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var seenHrefs = [String]()
|
||||
|
||||
let favicons: [HTMLMetadataFavicon] = linkTags.compactMap { htmlTag in
|
||||
|
||||
let favicon = HTMLMetadataFavicon(baseURLString, htmlTag)
|
||||
guard let urlString = favicon.urlString else {
|
||||
return nil
|
||||
}
|
||||
guard !seenHrefs.contains(urlString) else {
|
||||
return nil
|
||||
}
|
||||
seenHrefs.append(urlString)
|
||||
return favicon
|
||||
}
|
||||
|
||||
return favicons.isEmpty ? nil : favicons
|
||||
}
|
||||
|
||||
static func appleTouchIconTags(_ tags: [HTMLTag]) -> [HTMLTag]? {
|
||||
|
||||
guard let linkTags = linkTags(tags) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let appleTouchIconTags = tagsMatchingRelValues(["apple-touch-icon", "apple-touch-icon-precomposed"], linkTags) else {
|
||||
return nil
|
||||
}
|
||||
return appleTouchIconTags.isEmpty ? nil : appleTouchIconTags
|
||||
}
|
||||
|
||||
static func feedLinkTags(_ tags: [HTMLTag]) -> [HTMLTag]? {
|
||||
|
||||
guard let alternateLinkTags = linkTagsWithMatchingRel("alternate", tags) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let feedLinkTags = alternateLinkTags.filter { tag in
|
||||
|
||||
guard let attributes = tag.attributes, let type = attributes.object(forCaseInsensitiveKey: "type"), typeIsFeedType(type) else {
|
||||
return false
|
||||
}
|
||||
guard let urlString = urlString(from: attributes), !urlString.isEmpty else {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return feedLinkTags.isEmpty ? nil : feedLinkTags
|
||||
}
|
||||
|
||||
static func typeIsFeedType(_ type: String) -> Bool {
|
||||
|
||||
let lowerType = type.lowercased()
|
||||
return lowerType.hasSuffix("/rss+xml") || lowerType.hasSuffix("/atom+xml") || lowerType.hasSuffix("/json")
|
||||
}
|
||||
|
||||
static func linkTags(_ tags: [HTMLTag]) -> [HTMLTag]? {
|
||||
|
||||
let linkTags = tags.filter { $0.tagType == .link }
|
||||
return linkTags.isEmpty ? nil : linkTags
|
||||
}
|
||||
|
||||
static func linkTagsWithMatchingRel(_ valueToMatch: String, _ tags: [HTMLTag]) -> [HTMLTag]? {
|
||||
|
||||
// Case-insensitive; matches a whitespace-delimited word
|
||||
|
||||
guard let linkTags = linkTags(tags) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let tagsWithURLString = linkTags.filter { tag in
|
||||
guard let attributes = tag.attributes else {
|
||||
return false
|
||||
}
|
||||
guard let urlString = urlString(from: attributes), !urlString.isEmpty else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if tagsWithURLString.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let matchingTags = tagsMatchingRelValues([valueToMatch], tagsWithURLString) else {
|
||||
return nil
|
||||
}
|
||||
return matchingTags.isEmpty ? nil : matchingTags
|
||||
}
|
||||
|
||||
static func tagsMatchingRelValues(_ valuesToMatch: [String], _ tags: [HTMLTag]) -> [HTMLTag]? {
|
||||
|
||||
let lowerValuesToMatch = valuesToMatch.map { $0.lowercased() }
|
||||
|
||||
let matchingTags: [HTMLTag] = {
|
||||
|
||||
tags.filter { tag in
|
||||
|
||||
guard let attributes = tag.attributes else {
|
||||
return false
|
||||
}
|
||||
guard let relValue = relValue(from: attributes) else {
|
||||
return false
|
||||
}
|
||||
|
||||
let relValues = relValue.components(separatedBy: .whitespacesAndNewlines)
|
||||
for oneRelValue in relValues {
|
||||
let oneLowerRelValue = oneRelValue.lowercased()
|
||||
|
||||
for lowerValueToMatch in lowerValuesToMatch {
|
||||
if lowerValueToMatch == oneLowerRelValue {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}()
|
||||
|
||||
return matchingTags.isEmpty ? nil : matchingTags
|
||||
}
|
||||
}
|
||||
|
||||
public final class HTMLMetadataAppleTouchIcon: Sendable {
|
||||
|
||||
public let rel: String?
|
||||
public let sizes: String?
|
||||
public let size: CGSize?
|
||||
public let urlString: String? // Absolute
|
||||
|
||||
init(_ urlString: String, _ tag: HTMLTag) {
|
||||
|
||||
guard let attributes = tag.attributes else {
|
||||
self.rel = nil
|
||||
self.sizes = nil
|
||||
self.size = nil
|
||||
self.urlString = nil
|
||||
return
|
||||
}
|
||||
|
||||
self.rel = attributes.object(forCaseInsensitiveKey: "rel")
|
||||
self.urlString = absoluteURLString(from: attributes, baseURL: urlString)
|
||||
|
||||
guard let sizes = attributes.object(forCaseInsensitiveKey: "sizes") else {
|
||||
self.sizes = nil
|
||||
self.size = nil
|
||||
return
|
||||
}
|
||||
self.sizes = sizes
|
||||
|
||||
let sizeComponents = sizes.components(separatedBy: CharacterSet(charactersIn: "x"))
|
||||
if sizeComponents.count == 2, let width = Double(sizeComponents[0]), let height = Double(sizeComponents[1]) {
|
||||
self.size = CGSize(width: width, height: height)
|
||||
}
|
||||
else {
|
||||
self.size = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class HTMLMetadataFeedLink: Sendable {
|
||||
|
||||
public let title: String?
|
||||
public let type: String?
|
||||
public let urlString: String? // Absolute
|
||||
|
||||
init(_ urlString: String, _ tag: HTMLTag) {
|
||||
|
||||
guard let attributes = tag.attributes else {
|
||||
self.title = nil
|
||||
self.type = nil
|
||||
self.urlString = nil
|
||||
return
|
||||
}
|
||||
|
||||
self.urlString = absoluteURLString(from: attributes, baseURL: urlString)
|
||||
self.title = attributes.object(forCaseInsensitiveKey: "title")
|
||||
self.type = attributes.object(forCaseInsensitiveKey: "type")
|
||||
}
|
||||
}
|
||||
|
||||
public final class HTMLMetadataFavicon: Sendable {
|
||||
|
||||
public let type: String?
|
||||
public let urlString: String?
|
||||
|
||||
init(_ urlString: String, _ tag: HTMLTag) {
|
||||
|
||||
guard let attributes = tag.attributes else {
|
||||
self.type = nil
|
||||
self.urlString = nil
|
||||
return
|
||||
}
|
||||
|
||||
self.urlString = absoluteURLString(from: attributes, baseURL: urlString)
|
||||
self.type = attributes.object(forCaseInsensitiveKey: "type")
|
||||
}
|
||||
}
|
||||
|
||||
public final class HTMLOpenGraphProperties: Sendable {
|
||||
|
||||
// TODO: the rest. At this writing (Nov. 26, 2017) I just care about og:image.
|
||||
// See http://ogp.me/
|
||||
|
||||
public let image: HTMLOpenGraphImage?
|
||||
|
||||
init(_ urlString: String, _ tags: [HTMLTag]) {
|
||||
|
||||
self.image = Self.parse(tags)
|
||||
}
|
||||
}
|
||||
|
||||
private extension HTMLOpenGraphProperties {
|
||||
|
||||
private static let ogPrefix = "og:"
|
||||
|
||||
struct OGKey {
|
||||
static let property = "property"
|
||||
static let content = "content"
|
||||
}
|
||||
|
||||
struct OGValue {
|
||||
static let ogImage = "og:image"
|
||||
static let ogImageURL = "og:image:url"
|
||||
static let ogImageSecureURL = "og:image:secure_url"
|
||||
static let ogImageType = "og:image:type"
|
||||
static let ogImageAlt = "og:image:alt"
|
||||
static let ogImageWidth = "og:image:width"
|
||||
static let ogImageHeight = "og:image:height"
|
||||
}
|
||||
|
||||
static func parse(_ tags: [HTMLTag]) -> HTMLOpenGraphImage? {
|
||||
|
||||
let metaTags = tags.filter { $0.tagType == .meta }
|
||||
if metaTags.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTMLOpenGraphImage properties to fill in.
|
||||
var url: String?
|
||||
var secureURL: String?
|
||||
var mimeType: String?
|
||||
var width: CGFloat?
|
||||
var height: CGFloat?
|
||||
var altText: String?
|
||||
|
||||
for tag in metaTags {
|
||||
|
||||
guard let attributes = tag.attributes else {
|
||||
continue
|
||||
}
|
||||
guard let propertyName = attributes[OGKey.property], propertyName.hasPrefix(ogPrefix) else {
|
||||
continue
|
||||
}
|
||||
guard let content = attributes[OGKey.content] else {
|
||||
continue
|
||||
}
|
||||
|
||||
if propertyName == OGValue.ogImage {
|
||||
url = content
|
||||
}
|
||||
else if propertyName == OGValue.ogImageURL {
|
||||
url = content
|
||||
}
|
||||
else if propertyName == OGValue.ogImageSecureURL {
|
||||
secureURL = content
|
||||
}
|
||||
else if propertyName == OGValue.ogImageType {
|
||||
mimeType = content
|
||||
}
|
||||
else if propertyName == OGValue.ogImageAlt {
|
||||
altText = content
|
||||
}
|
||||
else if propertyName == OGValue.ogImageWidth {
|
||||
if let value = Double(content) {
|
||||
width = CGFloat(value)
|
||||
}
|
||||
}
|
||||
else if propertyName == OGValue.ogImageHeight {
|
||||
if let value = Double(content) {
|
||||
height = CGFloat(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if url == nil && secureURL == nil && mimeType == nil && width == nil && height == nil && altText == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return HTMLOpenGraphImage(url: url, secureURL: secureURL, mimeType: mimeType, width: width, height: height, altText: altText)
|
||||
}
|
||||
}
|
||||
|
||||
public final class HTMLOpenGraphImage: Sendable {
|
||||
|
||||
public let url : String?
|
||||
public let secureURL: String?
|
||||
public let mimeType: String?
|
||||
public let width: CGFloat?
|
||||
public let height: CGFloat?
|
||||
public let altText: String?
|
||||
|
||||
init(url: String?, secureURL: String?, mimeType: String?, width: CGFloat?, height: CGFloat?, altText: String?) {
|
||||
|
||||
self.url = url
|
||||
self.secureURL = secureURL
|
||||
self.mimeType = mimeType
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.altText = altText
|
||||
}
|
||||
}
|
||||
|
||||
public final class HTMLTwitterProperties: Sendable {
|
||||
|
||||
public let imageURL: String? // twitter:image:src
|
||||
|
||||
private struct TwitterKey {
|
||||
static let name = "name"
|
||||
static let content = "content"
|
||||
}
|
||||
|
||||
private struct TwitterValue {
|
||||
static let imageSrc = "twitter:image:src"
|
||||
}
|
||||
|
||||
init(_ urlString: String, _ tags: [HTMLTag]) {
|
||||
|
||||
let imageURL: String? = {
|
||||
for tag in tags {
|
||||
guard tag.tagType == .meta else {
|
||||
continue
|
||||
}
|
||||
guard let name = tag.attributes?[TwitterKey.name], name == TwitterValue.imageSrc else {
|
||||
continue
|
||||
}
|
||||
guard let content = tag.attributes?[TwitterKey.content], !content.isEmpty else {
|
||||
continue
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
self.imageURL = imageURL
|
||||
}
|
||||
}
|
||||
|
||||
private func urlString(from attributes: HTMLTagAttributes) -> String? {
|
||||
|
||||
if let urlString = attributes.object(forCaseInsensitiveKey: "href") {
|
||||
return urlString
|
||||
}
|
||||
return attributes.object(forCaseInsensitiveKey: "src")
|
||||
}
|
||||
|
||||
private func relValue(from attributes: HTMLTagAttributes) -> String? {
|
||||
|
||||
attributes.object(forCaseInsensitiveKey: "rel")
|
||||
}
|
||||
|
||||
private func absoluteURLString(from attributes: HTMLTagAttributes, baseURL: String) -> String? {
|
||||
|
||||
guard let urlString = urlString(from: attributes), !urlString.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return absoluteURLStringWithRelativeURLString(urlString, baseURLString: baseURL)
|
||||
}
|
||||
|
||||
private func absoluteURLStringWithRelativeURLString(_ relativeURLString: String, baseURLString: String) -> String? {
|
||||
|
||||
guard let baseURL = URL(string: baseURLString) else {
|
||||
return nil
|
||||
}
|
||||
guard let absoluteURL = URL(string: relativeURLString, relativeTo: baseURL) else {
|
||||
return nil
|
||||
}
|
||||
return absoluteURL.absoluteURL.standardized.absoluteString
|
||||
}
|
||||
|
102
Parser/Sources/Parser/HTMLParser/HTMLMetadataParser.swift
Normal file
102
Parser/Sources/Parser/HTMLParser/HTMLMetadataParser.swift
Normal file
@ -0,0 +1,102 @@
|
||||
//
|
||||
// HTMLMetadataParser.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 9/22/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
|
||||
public final class HTMLMetadataParser {
|
||||
|
||||
private var tags = [HTMLTag]()
|
||||
|
||||
public static func metadata(with parserData: ParserData) -> HTMLMetadata {
|
||||
|
||||
HTMLMetadataParser().parse(parserData)
|
||||
}
|
||||
}
|
||||
|
||||
private extension HTMLMetadataParser {
|
||||
|
||||
func parse(_ parserData: ParserData) -> HTMLMetadata {
|
||||
|
||||
tags = [HTMLTag]()
|
||||
|
||||
let htmlParser = SAXHTMLParser(delegate: self, data: parserData.data)
|
||||
htmlParser.parse()
|
||||
|
||||
return HTMLMetadata(parserData.url, tags)
|
||||
}
|
||||
}
|
||||
|
||||
extension HTMLMetadataParser: SAXHTMLParserDelegate {
|
||||
|
||||
private struct HTMLName {
|
||||
|
||||
static let link = "link".utf8CString
|
||||
static let meta = "meta".utf8CString
|
||||
}
|
||||
|
||||
private struct HTMLKey {
|
||||
|
||||
static let href = "href"
|
||||
static let src = "src"
|
||||
static let rel = "rel"
|
||||
}
|
||||
|
||||
private func link(with attributes: StringDictionary) -> String? {
|
||||
|
||||
if let link = attributes.object(forCaseInsensitiveKey: HTMLKey.href) {
|
||||
return link
|
||||
}
|
||||
|
||||
return attributes.object(forCaseInsensitiveKey: HTMLKey.src)
|
||||
}
|
||||
|
||||
private func handleLinkAttributes(_ attributes: StringDictionary) {
|
||||
|
||||
guard let rel = attributes.object(forCaseInsensitiveKey: HTMLKey.rel), !rel.isEmpty else {
|
||||
return
|
||||
}
|
||||
guard let link = link(with: attributes), !link.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let tag = HTMLTag(tagType: .link, attributes: attributes)
|
||||
tags.append(tag)
|
||||
}
|
||||
|
||||
private func handleMetaAttributes(_ attributes: StringDictionary) {
|
||||
|
||||
let tag = HTMLTag(tagType: .meta, attributes: attributes)
|
||||
tags.append(tag)
|
||||
}
|
||||
|
||||
public func saxHTMLParser(_ saxHTMLParser: SAXHTMLParser, startElement name: XMLPointer, attributes: UnsafePointer<XMLPointer?>?) {
|
||||
|
||||
if SAXEqualTags(name, HTMLName.link) {
|
||||
let d = saxHTMLParser.attributesDictionary(attributes)
|
||||
if let d, !d.isEmpty {
|
||||
handleLinkAttributes(d)
|
||||
}
|
||||
}
|
||||
else if SAXEqualTags(name, HTMLName.meta) {
|
||||
let d = saxHTMLParser.attributesDictionary(attributes)
|
||||
if let d, !d.isEmpty {
|
||||
handleMetaAttributes(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func saxHTMLParser(_: SAXHTMLParser, endElement: XMLPointer) {
|
||||
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
public func saxHTMLParser(_: SAXHTMLParser, charactersFound: XMLPointer, count: Int) {
|
||||
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
26
Parser/Sources/Parser/HTMLParser/HTMLTag.swift
Normal file
26
Parser/Sources/Parser/HTMLParser/HTMLTag.swift
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// HTMLTag.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/18/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias HTMLTagAttributes = [String: String]
|
||||
|
||||
public struct HTMLTag: Sendable {
|
||||
|
||||
public enum TagType: Sendable {
|
||||
case link
|
||||
case meta
|
||||
}
|
||||
|
||||
public let tagType: TagType
|
||||
public let attributes: HTMLTagAttributes?
|
||||
|
||||
public init(tagType: TagType, attributes: HTMLTagAttributes?) {
|
||||
self.tagType = tagType
|
||||
self.attributes = attributes
|
||||
}
|
||||
}
|
53
Parser/Sources/Parser/OPMLParser/OPMLAttributes.swift
Normal file
53
Parser/Sources/Parser/OPMLParser/OPMLAttributes.swift
Normal file
@ -0,0 +1,53 @@
|
||||
//
|
||||
// OPMLAttributes.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/18/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// OPML allows for arbitrary attributes.
|
||||
// These are the common attributes in OPML files used as RSS subscription lists.
|
||||
|
||||
private let opmlTextKey = "text"
|
||||
private let opmlTitleKey = "title"
|
||||
private let opmlDescriptionKey = "description"
|
||||
private let opmlTypeKey = "type"
|
||||
private let opmlVersionKey = "version"
|
||||
private let opmlHMTLURLKey = "htmlUrl"
|
||||
private let opmlXMLURLKey = "xmlUrl"
|
||||
|
||||
// A frequent error in OPML files is to mess up the capitalization,
|
||||
// so these do a case-insensitive lookup.
|
||||
|
||||
extension Dictionary where Key == String, Value == String {
|
||||
|
||||
var opml_text: String? {
|
||||
object(forCaseInsensitiveKey: opmlTextKey)
|
||||
}
|
||||
|
||||
var opml_title: String? {
|
||||
object(forCaseInsensitiveKey: opmlTitleKey)
|
||||
}
|
||||
|
||||
var opml_description: String? {
|
||||
object(forCaseInsensitiveKey: opmlDescriptionKey)
|
||||
}
|
||||
|
||||
var opml_type: String? {
|
||||
object(forCaseInsensitiveKey: opmlTypeKey)
|
||||
}
|
||||
|
||||
var opml_version: String? {
|
||||
object(forCaseInsensitiveKey: opmlVersionKey)
|
||||
}
|
||||
|
||||
var opml_htmlUrl: String? {
|
||||
object(forCaseInsensitiveKey: opmlHMTLURLKey)
|
||||
}
|
||||
|
||||
var opml_xmlUrl: String? {
|
||||
object(forCaseInsensitiveKey: opmlXMLURLKey)
|
||||
}
|
||||
}
|
19
Parser/Sources/Parser/OPMLParser/OPMLDocument.swift
Normal file
19
Parser/Sources/Parser/OPMLParser/OPMLDocument.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// OPMLDocument.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/18/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class OPMLDocument: OPMLItem {
|
||||
|
||||
public var title: String? = nil
|
||||
public var url: String? = nil
|
||||
|
||||
init(url: String?) {
|
||||
self.url = url
|
||||
super.init(attributes: nil)
|
||||
}
|
||||
}
|
40
Parser/Sources/Parser/OPMLParser/OPMLFeedSpecifier.swift
Normal file
40
Parser/Sources/Parser/OPMLParser/OPMLFeedSpecifier.swift
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// OPMLFeedSpecifier.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/18/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct OPMLFeedSpecifier: Sendable {
|
||||
|
||||
public let title: String?
|
||||
public let feedDescription: String?
|
||||
public let homePageURL: String?
|
||||
public let feedURL: String
|
||||
|
||||
init(title: String?, feedDescription: String?, homePageURL: String?, feedURL: String) {
|
||||
|
||||
if String.isEmptyOrNil(title) {
|
||||
self.title = nil
|
||||
} else {
|
||||
self.title = title
|
||||
}
|
||||
|
||||
if String.isEmptyOrNil(feedDescription) {
|
||||
self.feedDescription = nil
|
||||
} else {
|
||||
self.feedDescription = feedDescription
|
||||
}
|
||||
|
||||
if String.isEmptyOrNil(homePageURL) {
|
||||
self.homePageURL = nil
|
||||
} else {
|
||||
self.homePageURL = homePageURL
|
||||
}
|
||||
|
||||
self.feedURL = feedURL
|
||||
}
|
||||
}
|
||||
|
42
Parser/Sources/Parser/OPMLParser/OPMLItem.swift
Normal file
42
Parser/Sources/Parser/OPMLParser/OPMLItem.swift
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// OPMLItem.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/18/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
public class OPMLItem {
|
||||
|
||||
public let feedSpecifier: OPMLFeedSpecifier?
|
||||
|
||||
public let attributes: [String: String]?
|
||||
public let titleFromAttributes: String?
|
||||
|
||||
public var items: [OPMLItem]?
|
||||
public var isFolder: Bool {
|
||||
(items?.count ?? 0) > 0
|
||||
}
|
||||
|
||||
init(attributes: [String : String]?) {
|
||||
|
||||
self.titleFromAttributes = attributes?.opml_title ?? attributes?.opml_text
|
||||
self.attributes = attributes
|
||||
|
||||
if let feedURL = attributes?.opml_xmlUrl {
|
||||
self.feedSpecifier = OPMLFeedSpecifier(title: self.titleFromAttributes, feedDescription: attributes?.opml_description, homePageURL: attributes?.opml_htmlUrl, feedURL: feedURL)
|
||||
} else {
|
||||
self.feedSpecifier = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func add(_ item: OPMLItem) {
|
||||
|
||||
if items == nil {
|
||||
items = [OPMLItem]()
|
||||
}
|
||||
items?.append(item)
|
||||
}
|
||||
}
|
117
Parser/Sources/Parser/OPMLParser/OPMLParser.swift
Normal file
117
Parser/Sources/Parser/OPMLParser/OPMLParser.swift
Normal file
@ -0,0 +1,117 @@
|
||||
//
|
||||
// OPMLParser.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/18/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class OPMLParser {
|
||||
|
||||
private let parserData: ParserData
|
||||
private var data: Data {
|
||||
parserData.data
|
||||
}
|
||||
|
||||
private var opmlDocument: OPMLDocument?
|
||||
|
||||
private var itemStack = [OPMLItem]()
|
||||
private var currentItem: OPMLItem? {
|
||||
itemStack.last
|
||||
}
|
||||
|
||||
/// Returns nil if data can’t be parsed (if it’s not OPML).
|
||||
public static func document(with parserData: ParserData) -> OPMLDocument? {
|
||||
|
||||
let opmlParser = OPMLParser(parserData)
|
||||
opmlParser.parse()
|
||||
return opmlParser.opmlDocument
|
||||
}
|
||||
|
||||
init(_ parserData: ParserData) {
|
||||
self.parserData = parserData
|
||||
}
|
||||
}
|
||||
|
||||
private extension OPMLParser {
|
||||
|
||||
func parse() {
|
||||
|
||||
guard canParseData() else {
|
||||
return
|
||||
}
|
||||
|
||||
opmlDocument = OPMLDocument(url: parserData.url)
|
||||
push(opmlDocument!)
|
||||
|
||||
let saxParser = SAXParser(delegate: self, data: data)
|
||||
saxParser.parse()
|
||||
}
|
||||
|
||||
func canParseData() -> Bool {
|
||||
|
||||
data.containsASCIIString("<opml")
|
||||
}
|
||||
|
||||
func push(_ item: OPMLItem) {
|
||||
|
||||
itemStack.append(item)
|
||||
}
|
||||
|
||||
func popItem() {
|
||||
|
||||
guard itemStack.count > 0 else {
|
||||
assertionFailure("itemStack.count must be > 0")
|
||||
return
|
||||
}
|
||||
|
||||
itemStack.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
extension OPMLParser: SAXParserDelegate {
|
||||
|
||||
private struct XMLName {
|
||||
static let title = "title".utf8CString
|
||||
static let outline = "outline".utf8CString
|
||||
}
|
||||
|
||||
public func saxParser(_ saxParser: SAXParser, xmlStartElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafePointer<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafePointer<XMLPointer?>?) {
|
||||
|
||||
if SAXEqualTags(localName, XMLName.title) {
|
||||
saxParser.beginStoringCharacters()
|
||||
return
|
||||
}
|
||||
|
||||
if !SAXEqualTags(localName, XMLName.outline) {
|
||||
return
|
||||
}
|
||||
|
||||
let attributesDictionary = saxParser.attributesDictionary(attributes, attributeCount: attributeCount)
|
||||
let item = OPMLItem(attributes: attributesDictionary)
|
||||
|
||||
currentItem?.add(item)
|
||||
push(item)
|
||||
}
|
||||
|
||||
public func saxParser(_ saxParser: SAXParser, xmlEndElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?) {
|
||||
|
||||
if SAXEqualTags(localName, XMLName.title) {
|
||||
if let item = currentItem as? OPMLDocument {
|
||||
item.title = saxParser.currentStringWithTrimmedWhitespace
|
||||
}
|
||||
saxParser.endStoringCharacters()
|
||||
return
|
||||
}
|
||||
|
||||
if SAXEqualTags(localName, XMLName.outline) {
|
||||
popItem()
|
||||
}
|
||||
}
|
||||
|
||||
public func saxParser(_: SAXParser, xmlCharactersFound: XMLPointer, count: Int) {
|
||||
|
||||
// Nothing to do, but method is required.
|
||||
}
|
||||
}
|
68
Parser/Sources/Parser/SAX/Extensions/Data+SAX.swift
Normal file
68
Parser/Sources/Parser/SAX/Extensions/Data+SAX.swift
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// Data+Parser.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/24/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension Data {
|
||||
|
||||
/// Return true if the data contains a given String.
|
||||
///
|
||||
/// Assumes that the data is UTF-8 or similar encoding —
|
||||
/// if it’s UTF-16 or UTF-32, for instance, this will always return false.
|
||||
/// Luckily these are rare.
|
||||
///
|
||||
/// The String to search for should be something that could be encoded
|
||||
/// in ASCII — like "<opml" or "<rss". (In other words,
|
||||
/// the sequence of characters would always be the same in
|
||||
/// commonly-used encodings.)
|
||||
func containsASCIIString(_ searchFor: String) -> Bool {
|
||||
|
||||
contains(searchFor.utf8)
|
||||
}
|
||||
|
||||
/// Return true if searchFor appears in self.
|
||||
func contains(_ searchFor: Data) -> Bool {
|
||||
|
||||
let searchForCount = searchFor.count
|
||||
let dataCount = self.count
|
||||
|
||||
guard searchForCount > 0, searchForCount <= dataCount else {
|
||||
return false
|
||||
}
|
||||
|
||||
let searchForInitialByte = searchFor[0]
|
||||
var found = false
|
||||
|
||||
self.withUnsafeBytes { bytes in
|
||||
|
||||
let buffer = bytes.bindMemory(to: UInt8.self)
|
||||
|
||||
for i in 0...dataCount - searchForCount {
|
||||
|
||||
if buffer[i] == searchForInitialByte {
|
||||
|
||||
var match = true
|
||||
|
||||
for j in 1..<searchForCount {
|
||||
|
||||
if buffer[i + j] != searchFor[j] {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if match {
|
||||
found = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
}
|
28
Parser/Sources/Parser/SAX/Extensions/Dictionary+SAX.swift
Normal file
28
Parser/Sources/Parser/SAX/Extensions/Dictionary+SAX.swift
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// Dictionary+Parser.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/18/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension Dictionary where Key == String, Value == String {
|
||||
|
||||
func object(forCaseInsensitiveKey key: String) -> String? {
|
||||
|
||||
if let object = self[key] {
|
||||
return object
|
||||
}
|
||||
|
||||
let lowercaseKey = key.lowercased()
|
||||
|
||||
for (oneKey, oneValue) in self {
|
||||
if lowercaseKey.caseInsensitiveCompare(oneKey) == .orderedSame {
|
||||
return oneValue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
23
Parser/Sources/Parser/SAX/Extensions/String+SAX.swift
Normal file
23
Parser/Sources/Parser/SAX/Extensions/String+SAX.swift
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// String+RSParser.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Nate Weaver on 2020-01-19.
|
||||
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension String {
|
||||
|
||||
var nilIfEmptyOrWhitespace: String? {
|
||||
return self.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? nil : self
|
||||
}
|
||||
|
||||
static func isEmptyOrNil(_ s: String?) -> Bool {
|
||||
if let s {
|
||||
return s.isEmpty
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
19
Parser/Sources/Parser/SAX/ParserData.swift
Normal file
19
Parser/Sources/Parser/SAX/ParserData.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// ParserData.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/18/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct ParserData: Sendable {
|
||||
|
||||
public let url: String
|
||||
public let data: Data
|
||||
|
||||
public init(url: String, data: Data) {
|
||||
self.url = url
|
||||
self.data = data
|
||||
}
|
||||
}
|
200
Parser/Sources/Parser/SAX/SAXHTMLParser.swift
Normal file
200
Parser/Sources/Parser/SAX/SAXHTMLParser.swift
Normal file
@ -0,0 +1,200 @@
|
||||
//
|
||||
// SAXHTMLParser.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/26/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import libxml2
|
||||
|
||||
public protocol SAXHTMLParserDelegate: AnyObject {
|
||||
|
||||
func saxHTMLParser(_: SAXHTMLParser, startElement: XMLPointer, attributes: UnsafePointer<XMLPointer?>?)
|
||||
|
||||
func saxHTMLParser(_: SAXHTMLParser, endElement: XMLPointer)
|
||||
|
||||
// Length is guaranteed to be greater than 0.
|
||||
func saxHTMLParser(_: SAXHTMLParser, charactersFound: XMLPointer, count: Int)
|
||||
}
|
||||
|
||||
public final class SAXHTMLParser {
|
||||
|
||||
fileprivate let delegate: SAXHTMLParserDelegate
|
||||
|
||||
public var currentCharacters: Data? { // UTF-8 encoded
|
||||
|
||||
guard storingCharacters else {
|
||||
return nil
|
||||
}
|
||||
return characters
|
||||
}
|
||||
|
||||
// Conveniences to get string version of currentCharacters
|
||||
|
||||
public var currentString: String? {
|
||||
|
||||
guard let d = currentCharacters, !d.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return String(data: d, encoding: .utf8)
|
||||
}
|
||||
|
||||
public var currentStringWithTrimmedWhitespace: String? {
|
||||
|
||||
guard let s = currentString else {
|
||||
return nil
|
||||
}
|
||||
return s.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
}
|
||||
|
||||
private var data: Data
|
||||
private var storingCharacters = false
|
||||
private var characters = Data()
|
||||
|
||||
public init(delegate: SAXHTMLParserDelegate, data: Data) {
|
||||
|
||||
self.delegate = delegate
|
||||
self.data = data
|
||||
}
|
||||
|
||||
public func parse() {
|
||||
|
||||
guard !data.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
data.withUnsafeBytes { bufferPointer in
|
||||
|
||||
guard let bytes = bufferPointer.bindMemory(to: CChar.self).baseAddress else {
|
||||
return
|
||||
}
|
||||
|
||||
let characterEncoding = xmlDetectCharEncoding(bytes, Int32(data.count))
|
||||
let context = htmlCreatePushParserCtxt(&saxHandlerStruct, Unmanaged.passUnretained(self).toOpaque(), nil, 0, nil, characterEncoding)
|
||||
htmlCtxtUseOptions(context, Int32(HTML_PARSE_RECOVER.rawValue | HTML_PARSE_NONET.rawValue | HTML_PARSE_COMPACT.rawValue | HTML_PARSE_NOERROR.rawValue | HTML_PARSE_NOWARNING.rawValue))
|
||||
|
||||
htmlParseChunk(context, bytes, Int32(data.count), 0)
|
||||
|
||||
htmlParseChunk(context, nil, 0, 1)
|
||||
htmlFreeParserCtxt(context)
|
||||
}
|
||||
}
|
||||
|
||||
/// Delegate can call from xmlStartElement. Characters will be available in xmlEndElement as currentCharacters property. Storing characters is stopped after each xmlEndElement.
|
||||
public func beginStoringCharacters() {
|
||||
|
||||
storingCharacters = true
|
||||
characters.count = 0
|
||||
}
|
||||
|
||||
public func endStoringCharacters() {
|
||||
|
||||
storingCharacters = false
|
||||
characters.count = 0
|
||||
}
|
||||
|
||||
public func attributesDictionary(_ attributes: UnsafePointer<XMLPointer?>?) -> StringDictionary? {
|
||||
|
||||
guard let attributes else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var dictionary = [String: String]()
|
||||
var ix = 0
|
||||
var currentKey: String? = nil
|
||||
|
||||
while true {
|
||||
let oneAttribute = attributes[ix]
|
||||
ix += 1
|
||||
|
||||
if currentKey == nil && oneAttribute == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if currentKey == nil {
|
||||
if let oneAttribute {
|
||||
currentKey = String(cString: oneAttribute)
|
||||
}
|
||||
} else {
|
||||
let value: String?
|
||||
if let oneAttribute {
|
||||
value = String(cString: oneAttribute)
|
||||
} else {
|
||||
value = nil
|
||||
}
|
||||
|
||||
dictionary[currentKey!] = value ?? ""
|
||||
currentKey = nil
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary
|
||||
}
|
||||
}
|
||||
|
||||
private extension SAXHTMLParser {
|
||||
|
||||
func charactersFound(_ htmlCharacters: XMLPointer, count: Int) {
|
||||
|
||||
if storingCharacters {
|
||||
characters.append(htmlCharacters, count: count)
|
||||
}
|
||||
|
||||
delegate.saxHTMLParser(self, charactersFound: htmlCharacters, count: count)
|
||||
}
|
||||
|
||||
func startElement(_ name: XMLPointer, attributes: UnsafePointer<XMLPointer?>?) {
|
||||
|
||||
delegate.saxHTMLParser(self, startElement: name, attributes: attributes)
|
||||
}
|
||||
|
||||
func endElement(_ name: XMLPointer) {
|
||||
|
||||
delegate.saxHTMLParser(self, endElement: name)
|
||||
endStoringCharacters()
|
||||
}
|
||||
}
|
||||
|
||||
private func parser(from context: UnsafeMutableRawPointer) -> SAXHTMLParser {
|
||||
|
||||
Unmanaged<SAXHTMLParser>.fromOpaque(context).takeUnretainedValue()
|
||||
}
|
||||
|
||||
nonisolated(unsafe) private var saxHandlerStruct: xmlSAXHandler = {
|
||||
|
||||
var handler = htmlSAXHandler()
|
||||
|
||||
handler.characters = { (context: UnsafeMutableRawPointer?, ch: XMLPointer?, len: CInt) in
|
||||
|
||||
guard let context, let ch, len > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
let parser = parser(from: context)
|
||||
parser.charactersFound(ch, count: Int(len))
|
||||
}
|
||||
|
||||
handler.startElement = { (context: UnsafeMutableRawPointer?, name: XMLPointer?, attributes: UnsafeMutablePointer<XMLPointer?>?) in
|
||||
|
||||
guard let context, let name else {
|
||||
return
|
||||
}
|
||||
|
||||
let parser = parser(from: context)
|
||||
parser.startElement(name, attributes: attributes)
|
||||
}
|
||||
|
||||
handler.endElement = { (context: UnsafeMutableRawPointer?, name: XMLPointer?) in
|
||||
|
||||
guard let context, let name else {
|
||||
return
|
||||
}
|
||||
|
||||
let parser = parser(from: context)
|
||||
parser.endElement(name)
|
||||
}
|
||||
|
||||
return handler
|
||||
}()
|
204
Parser/Sources/Parser/SAX/SAXParser.swift
Normal file
204
Parser/Sources/Parser/SAX/SAXParser.swift
Normal file
@ -0,0 +1,204 @@
|
||||
//
|
||||
// SAXParser.swift.
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/12/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import libxml2
|
||||
|
||||
public typealias XMLPointer = UnsafePointer<xmlChar>
|
||||
|
||||
public protocol SAXParserDelegate {
|
||||
|
||||
func saxParser(_: SAXParser, xmlStartElement: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafePointer<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafePointer<XMLPointer?>?)
|
||||
|
||||
func saxParser(_: SAXParser, xmlEndElement: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?)
|
||||
|
||||
func saxParser(_: SAXParser, xmlCharactersFound: XMLPointer, count: Int)
|
||||
}
|
||||
|
||||
public final class SAXParser {
|
||||
|
||||
fileprivate let delegate: SAXParserDelegate
|
||||
|
||||
public var currentCharacters: Data? { // UTF-8 encoded
|
||||
|
||||
guard storingCharacters else {
|
||||
return nil
|
||||
}
|
||||
return characters
|
||||
}
|
||||
|
||||
// Conveniences to get string version of currentCharacters
|
||||
|
||||
public var currentString: String? {
|
||||
|
||||
guard let d = currentCharacters, !d.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return String(data: d, encoding: .utf8)
|
||||
}
|
||||
|
||||
public var currentStringWithTrimmedWhitespace: String? {
|
||||
|
||||
guard let s = currentString else {
|
||||
return nil
|
||||
}
|
||||
return s.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
}
|
||||
|
||||
private var data: Data
|
||||
private var storingCharacters = false
|
||||
private var characters = Data()
|
||||
|
||||
public init(delegate: SAXParserDelegate, data: Data) {
|
||||
|
||||
self.delegate = delegate
|
||||
self.data = data
|
||||
}
|
||||
|
||||
public func parse() {
|
||||
|
||||
guard !data.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let context = xmlCreatePushParserCtxt(&saxHandlerStruct, Unmanaged.passUnretained(self).toOpaque(), nil, 0, nil)
|
||||
xmlCtxtUseOptions(context, Int32(XML_PARSE_RECOVER.rawValue | XML_PARSE_NOENT.rawValue))
|
||||
|
||||
data.withUnsafeBytes { bufferPointer in
|
||||
if let bytes = bufferPointer.bindMemory(to: CChar.self).baseAddress {
|
||||
xmlParseChunk(context, bytes, Int32(data.count), 0)
|
||||
}
|
||||
}
|
||||
|
||||
xmlParseChunk(context, nil, 0, 1)
|
||||
xmlFreeParserCtxt(context)
|
||||
}
|
||||
|
||||
/// Delegate can call from xmlStartElement. Characters will be available in xmlEndElement as currentCharacters property. Storing characters is stopped after each xmlEndElement.
|
||||
public func beginStoringCharacters() {
|
||||
|
||||
storingCharacters = true
|
||||
characters.count = 0
|
||||
}
|
||||
|
||||
public func endStoringCharacters() {
|
||||
|
||||
storingCharacters = false
|
||||
characters.count = 0
|
||||
}
|
||||
|
||||
public func attributesDictionary(_ attributes: UnsafePointer<XMLPointer?>?, attributeCount: Int) -> StringDictionary? {
|
||||
|
||||
guard attributeCount > 0, let attributes else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var dictionary = [String: String]()
|
||||
|
||||
let fieldCount = 5
|
||||
var i = 0, j = 0
|
||||
while i < attributeCount {
|
||||
|
||||
guard let attribute = attributes[j] else {
|
||||
continue
|
||||
}
|
||||
let prefix = attributes[j + 1]
|
||||
var attributeName = String(cString: attribute)
|
||||
if let prefix {
|
||||
let attributePrefix = String(cString: prefix)
|
||||
attributeName = "\(attributePrefix):\(attributeName)"
|
||||
}
|
||||
|
||||
guard let valueStart = attributes[j + 3], let valueEnd = attributes[j + 4] else {
|
||||
continue
|
||||
}
|
||||
let valueCount = valueEnd - valueStart
|
||||
let value = String(bytes: UnsafeRawBufferPointer(start: valueStart, count: Int(valueCount)), encoding: .utf8)
|
||||
|
||||
if let value {
|
||||
dictionary[attributeName] = value
|
||||
}
|
||||
|
||||
i += 1
|
||||
j += fieldCount
|
||||
}
|
||||
|
||||
return dictionary
|
||||
}
|
||||
}
|
||||
|
||||
private extension SAXParser {
|
||||
|
||||
func charactersFound(_ xmlCharacters: XMLPointer, count: Int) {
|
||||
|
||||
if storingCharacters {
|
||||
characters.append(xmlCharacters, count: count)
|
||||
}
|
||||
|
||||
delegate.saxParser(self, xmlCharactersFound: xmlCharacters, count: count)
|
||||
}
|
||||
|
||||
func startElement(_ name: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafePointer<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafePointer<XMLPointer?>?) {
|
||||
|
||||
delegate.saxParser(self, xmlStartElement: name, prefix: prefix, uri: uri, namespaceCount: namespaceCount, namespaces: namespaces, attributeCount: attributeCount, attributesDefaultedCount: attributesDefaultedCount, attributes: attributes)
|
||||
}
|
||||
|
||||
func endElement(_ name: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?) {
|
||||
|
||||
delegate.saxParser(self, xmlEndElement: name, prefix: prefix, uri: uri)
|
||||
endStoringCharacters()
|
||||
}
|
||||
}
|
||||
|
||||
private func startElement(_ context: UnsafeMutableRawPointer?, name: XMLPointer?, prefix: XMLPointer?, URI: XMLPointer?, nb_namespaces: CInt, namespaces: UnsafeMutablePointer<XMLPointer?>?, nb_attributes: CInt, nb_defaulted: CInt, attributes: UnsafeMutablePointer<XMLPointer?>?) {
|
||||
|
||||
guard let context, let name else {
|
||||
return
|
||||
}
|
||||
|
||||
let parser = parser(from: context)
|
||||
parser.startElement(name, prefix: prefix, uri: URI, namespaceCount: Int(nb_namespaces), namespaces: namespaces, attributeCount: Int(nb_attributes), attributesDefaultedCount: Int(nb_defaulted), attributes: attributes)
|
||||
}
|
||||
|
||||
private func endElement(_ context: UnsafeMutableRawPointer?, name: XMLPointer?, prefix: XMLPointer?, URI: XMLPointer?) {
|
||||
|
||||
guard let context, let name else {
|
||||
return
|
||||
}
|
||||
|
||||
let parser = parser(from: context)
|
||||
parser.endElement(name, prefix: prefix, uri: URI)
|
||||
}
|
||||
|
||||
private func charactersFound(_ context: UnsafeMutableRawPointer?, ch: XMLPointer?, len: CInt) {
|
||||
|
||||
guard let context, let ch, len > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
let parser = parser(from: context)
|
||||
parser.charactersFound(ch, count: Int(len))
|
||||
}
|
||||
|
||||
private func parser(from context: UnsafeMutableRawPointer) -> SAXParser {
|
||||
|
||||
Unmanaged<SAXParser>.fromOpaque(context).takeUnretainedValue()
|
||||
}
|
||||
|
||||
nonisolated(unsafe) private var saxHandlerStruct: xmlSAXHandler = {
|
||||
|
||||
var handler = xmlSAXHandler()
|
||||
|
||||
handler.characters = charactersFound
|
||||
handler.startElementNs = startElement
|
||||
handler.endElementNs = endElement
|
||||
handler.initialized = XML_SAX2_MAGIC
|
||||
|
||||
return handler
|
||||
}()
|
||||
|
41
Parser/Sources/Parser/SAX/SAXUtilities.swift
Normal file
41
Parser/Sources/Parser/SAX/SAXUtilities.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// SAXUtilities.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 8/26/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import libxml2
|
||||
|
||||
public func SAXEqualTags(_ localName: XMLPointer, _ tag: ContiguousArray<Int8>) -> Bool {
|
||||
|
||||
return tag.withUnsafeBufferPointer { bufferPointer in
|
||||
|
||||
let tagCount = tag.count // includes 0 terminator
|
||||
|
||||
for i in 0..<tagCount - 1 {
|
||||
|
||||
let localNameCharacter = localName[i]
|
||||
if localNameCharacter == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
let tagCharacter = UInt8(tag[i])
|
||||
if localNameCharacter != tagCharacter {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// localName might actually be longer — make sure it’s the same length as tag.
|
||||
return localName[tagCount - 1] == 0
|
||||
}
|
||||
}
|
||||
|
||||
public extension String {
|
||||
|
||||
init?(xmlPointer: XMLPointer, count: Int? = nil) {
|
||||
let d = Data(bytes: xmlPointer, count: count ?? strlen(xmlPointer))
|
||||
self.init(data: d, encoding: .utf8)
|
||||
}
|
||||
}
|
107
Parser/Tests/ParserTests/AtomParserTests.swift
Normal file
107
Parser/Tests/ParserTests/AtomParserTests.swift
Normal file
@ -0,0 +1,107 @@
|
||||
//
|
||||
// AtomParserTests.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/26/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Parser
|
||||
|
||||
final class AtomParserTests: XCTestCase {
|
||||
|
||||
func testDaringFireballPerformance() {
|
||||
|
||||
// 0.009 sec on my 2012 iMac.
|
||||
let d = parserData("DaringFireball", "atom", "http://daringfireball.net/") //It’s actually an Atom feed
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testAllThisPerformance() {
|
||||
|
||||
// 0.003 sec on my 2012 iMac.
|
||||
let d = parserData("allthis", "atom", "http://leancrew.com/all-this")
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testGettingHomePageLink() {
|
||||
|
||||
let d = parserData("allthis", "atom", "http://leancrew.com/all-this")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.homePageURL == "http://leancrew.com/all-this")
|
||||
}
|
||||
|
||||
func testDaringFireball() {
|
||||
|
||||
let d = parserData("DaringFireball", "atom", "http://daringfireball.net/") //It’s actually an Atom feed
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
for article in parsedFeed.items {
|
||||
|
||||
XCTAssertNotNil(article.url)
|
||||
|
||||
XCTAssertTrue(article.uniqueID.hasPrefix("tag:daringfireball.net,2017:/"))
|
||||
|
||||
XCTAssertEqual(article.authors!.count, 1) // TODO: parse Atom authors
|
||||
let author = article.authors!.first!
|
||||
if author.name == "Daring Fireball Department of Commerce" {
|
||||
XCTAssertNil(author.url)
|
||||
}
|
||||
else {
|
||||
XCTAssertEqual(author.name, "John Gruber")
|
||||
XCTAssertEqual(author.url, "http://daringfireball.net/")
|
||||
}
|
||||
|
||||
XCTAssertNotNil(article.datePublished)
|
||||
XCTAssert(article.attachments == nil)
|
||||
|
||||
XCTAssertEqual(article.language, "en")
|
||||
}
|
||||
}
|
||||
|
||||
func test4fsodonlineAttachments() {
|
||||
|
||||
// Thanks to Marco for finding me some Atom podcast feeds. Apparently they’re super-rare.
|
||||
|
||||
let d = parserData("4fsodonline", "atom", "http://4fsodonline.blogspot.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
for article in parsedFeed.items {
|
||||
|
||||
XCTAssertTrue(article.attachments!.count > 0)
|
||||
let attachment = article.attachments!.first!
|
||||
|
||||
XCTAssertTrue(attachment.url.hasPrefix("http://www.blogger.com/video-play.mp4?"))
|
||||
XCTAssertNil(attachment.sizeInBytes)
|
||||
XCTAssertEqual(attachment.mimeType!, "video/mp4")
|
||||
}
|
||||
}
|
||||
|
||||
func testExpertOpinionENTAttachments() {
|
||||
|
||||
// Another from Marco.
|
||||
|
||||
let d = parserData("expertopinionent", "atom", "http://expertopinionent.typepad.com/my-blog/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
for article in parsedFeed.items {
|
||||
|
||||
guard let attachments = article.attachments else {
|
||||
continue
|
||||
}
|
||||
|
||||
XCTAssertEqual(attachments.count, 1)
|
||||
let attachment = attachments.first!
|
||||
|
||||
XCTAssertTrue(attachment.url.hasSuffix(".mp3"))
|
||||
XCTAssertNil(attachment.sizeInBytes)
|
||||
XCTAssertEqual(attachment.mimeType!, "audio/mpeg")
|
||||
}
|
||||
}
|
||||
}
|
139
Parser/Tests/ParserTests/DateParserTests.swift
Normal file
139
Parser/Tests/ParserTests/DateParserTests.swift
Normal file
@ -0,0 +1,139 @@
|
||||
//
|
||||
// RSDateParserTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Maurice Parker on 4/1/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import Parser
|
||||
|
||||
final class DateParserTests: XCTestCase {
|
||||
|
||||
func testDateWithString() {
|
||||
var expectedDateResult = dateWithValues(2010, 5, 28, 21, 3, 38)
|
||||
|
||||
var d = date("Fri, 28 May 2010 21:03:38 +0000")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("Fri, 28 May 2010 21:03:38 +00:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("Fri, 28 May 2010 21:03:38 -00:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("Fri, 28 May 2010 21:03:38 -0000")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("Fri, 28 May 2010 21:03:38 GMT")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("2010-05-28T21:03:38+00:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("2010-05-28T21:03:38+0000")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("2010-05-28T21:03:38-0000")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("2010-05-28T21:03:38-00:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
d = date("2010-05-28T21:03:38Z")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
expectedDateResult = dateWithValues(2010, 7, 13, 17, 6, 40)
|
||||
d = date("2010-07-13T17:06:40+00:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
expectedDateResult = dateWithValues(2010, 4, 30, 12, 0, 0)
|
||||
d = date("30 Apr 2010 5:00 PDT")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
expectedDateResult = dateWithValues(2010, 5, 21, 21, 22, 53)
|
||||
d = date("21 May 2010 21:22:53 GMT")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
expectedDateResult = dateWithValues(2010, 6, 9, 5, 0, 0)
|
||||
d = date("Wed, 09 Jun 2010 00:00 EST")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
expectedDateResult = dateWithValues(2010, 6, 23, 3, 43, 50)
|
||||
d = date("Wed, 23 Jun 2010 03:43:50 Z")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
expectedDateResult = dateWithValues(2010, 6, 22, 3, 57, 49)
|
||||
d = date("2010-06-22T03:57:49+00:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
|
||||
expectedDateResult = dateWithValues(2010, 11, 17, 13, 40, 07)
|
||||
d = date("2010-11-17T08:40:07-05:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
}
|
||||
|
||||
func testAtomDateWithMissingTCharacter() {
|
||||
let expectedDateResult = dateWithValues(2010, 11, 17, 13, 40, 07)
|
||||
let d = date("2010-11-17 08:40:07-05:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
}
|
||||
|
||||
func testFeedbinDate() {
|
||||
let expectedDateResult = dateWithValues(2019, 9, 27, 21, 01, 48)
|
||||
let d = date("2019-09-27T21:01:48.000000Z")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
}
|
||||
|
||||
func testMillisecondDate() {
|
||||
let expectedDateResult = dateWithValues(2021, 03, 29, 10, 46, 56, 516)
|
||||
let d = date("2021-03-29T10:46:56.516+00:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
}
|
||||
|
||||
func testExtraMillisecondPrecisionDate() {
|
||||
let expectedDateResult = dateWithValues(2021, 03, 29, 10, 46, 56, 516)
|
||||
let d = date("2021-03-29T10:46:56.516941+00:00")
|
||||
XCTAssertEqual(d, expectedDateResult)
|
||||
}
|
||||
|
||||
func testW3CParsingPerformance() {
|
||||
|
||||
// 0.0001 seconds on my Mac Studio M1
|
||||
self.measure {
|
||||
_ = date("2021-03-29T10:46:56.516941+00:00")
|
||||
}
|
||||
}
|
||||
|
||||
func testPubDateParsingPerformance() {
|
||||
|
||||
// 0.0001 seconds on my Mac Studio M1
|
||||
self.measure {
|
||||
_ = date("21 May 2010 21:22:53 GMT")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension DateParserTests {
|
||||
|
||||
func date(_ string: String) -> Date? {
|
||||
let d = Data(string.utf8)
|
||||
return DateParser.date(data: d)
|
||||
}
|
||||
}
|
||||
|
||||
func dateWithValues(_ year: Int, _ month: Int, _ day: Int, _ hour: Int, _ minute: Int, _ second: Int, _ millisecond: Int = 0) -> Date {
|
||||
var dateComponents = DateComponents()
|
||||
dateComponents.calendar = Calendar.current
|
||||
dateComponents.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
|
||||
dateComponents.year = year
|
||||
dateComponents.month = month
|
||||
dateComponents.day = day
|
||||
dateComponents.hour = hour
|
||||
dateComponents.minute = minute
|
||||
dateComponents.second = second
|
||||
dateComponents.nanosecond = millisecond * 1000000
|
||||
|
||||
return dateComponents.date!
|
||||
}
|
157
Parser/Tests/ParserTests/EntityDecodingTests.swift
Normal file
157
Parser/Tests/ParserTests/EntityDecodingTests.swift
Normal file
@ -0,0 +1,157 @@
|
||||
//
|
||||
// EntityDecodingTests.swift
|
||||
// RSParserTests
|
||||
//
|
||||
// Created by Brent Simmons on 12/30/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Parser
|
||||
|
||||
final class EntityDecodingTests: XCTestCase {
|
||||
|
||||
func test39Decoding() {
|
||||
|
||||
// Bug found by Manton Reece — the ' entity was not getting decoded by NetNewsWire in JSON Feeds from micro.blog.
|
||||
|
||||
let s = "These are the times that try men's souls."
|
||||
let decoded = decodedString(s)
|
||||
|
||||
XCTAssertEqual(decoded, "These are the times that try men's souls.")
|
||||
}
|
||||
|
||||
func testEntityAtBeginning() {
|
||||
|
||||
let s = "'leading single quote"
|
||||
let decoded = decodedString(s)
|
||||
|
||||
XCTAssertEqual(decoded, "'leading single quote")
|
||||
}
|
||||
|
||||
func testEntityAtEnd() {
|
||||
|
||||
let s = "trailing single quote'"
|
||||
let decoded = decodedString(s)
|
||||
|
||||
XCTAssertEqual(decoded, "trailing single quote'")
|
||||
}
|
||||
|
||||
func testEntityInMiddle() {
|
||||
|
||||
let s = "entity ç in middle"
|
||||
let decoded = decodedString(s)
|
||||
|
||||
XCTAssertEqual(decoded, "entity ç in middle")
|
||||
}
|
||||
|
||||
func testMultipleEntitiesInARow() {
|
||||
|
||||
let s = "çèmult……iple 'æ"entities÷♥"
|
||||
let decoded = decodedString(s)
|
||||
|
||||
XCTAssertEqual(decoded, "çèmult……iple 'æ\"entities÷♥")
|
||||
}
|
||||
|
||||
func testFakeoutEntities() {
|
||||
|
||||
var s = "&&;&#;&#x;&#X;& ;&# \t;&\r&&&&&;"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "#;&#x;&#X;& {"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = " &lsquo "
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "&&&&&&&&&&&&&&&&&&&;;;;;;&;&;&##;#X::&;&;&;&"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
}
|
||||
|
||||
func testFakeSquirrelEntities() {
|
||||
|
||||
var s = "&squirrel;"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "&squirrel;&#squirrel;"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "&squirrel;&#squirrel;&#xsquirrel;&#Xsquirrel;"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "'squirrel;"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "&squirrel;&#squirrel;&#xsquirrel;&#Xsquirrel;'squirrel;"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "&squirrel;&#squirrel;&#xsquirrel;&#Xsquirrel;'squirrel;&&;;;;&;&;&#squi#;#rrelX::&;&;&;&"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
}
|
||||
|
||||
func testLongFakeoutEntities() {
|
||||
|
||||
var s = "&thisIsALongNotRealEntityThatShouldBeHandledPerfectlyWellByTheParserBasicallyIgnored;"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "�"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "�"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "�"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "�"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
|
||||
s = "�"
|
||||
XCTAssertEqual(decodedString(s), s)
|
||||
}
|
||||
|
||||
func testOnlyEntity() {
|
||||
var s = "…"
|
||||
var decoded = decodedString(s)
|
||||
|
||||
XCTAssertEqual(decoded, "…")
|
||||
|
||||
s = "…"
|
||||
decoded = decodedString(s)
|
||||
XCTAssertEqual(decoded, "…")
|
||||
|
||||
s = "'"
|
||||
decoded = decodedString(s)
|
||||
XCTAssertEqual(decoded, "'")
|
||||
|
||||
s = "§"
|
||||
decoded = decodedString(s)
|
||||
XCTAssertEqual(decoded, "§")
|
||||
|
||||
s = "£"
|
||||
decoded = decodedString(s)
|
||||
XCTAssertEqual(decoded, "£")
|
||||
}
|
||||
|
||||
func testPerformance() {
|
||||
|
||||
// 0.003 sec on my M1 Mac Studio.
|
||||
let s = stringForResource("DaringFireball", "html")
|
||||
|
||||
self.measure {
|
||||
_ = decodedString(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stringForResource(_ filename: String, _ fileExtension: String) -> String {
|
||||
|
||||
let filename = "Resources/\(filename)"
|
||||
let path = Bundle.module.path(forResource: filename, ofType: fileExtension)!
|
||||
return try! String(contentsOfFile: path)
|
||||
}
|
||||
|
||||
func decodedString(_ s: String) -> String {
|
||||
|
||||
HTMLEntityDecoder.decodedString(s)
|
||||
}
|
236
Parser/Tests/ParserTests/FeedParserTypeTests.swift
Normal file
236
Parser/Tests/ParserTests/FeedParserTypeTests.swift
Normal file
@ -0,0 +1,236 @@
|
||||
//
|
||||
// FeedParserTypeTests.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/25/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import Parser
|
||||
|
||||
final class FeedParserTypeTests: XCTestCase {
|
||||
|
||||
// MARK: HTML
|
||||
|
||||
func testDaringFireballHTMLType() {
|
||||
|
||||
let d = parserData("DaringFireball", "html", "http://daringfireball.net/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .notAFeed)
|
||||
}
|
||||
|
||||
func testFurboHTMLType() {
|
||||
|
||||
let d = parserData("furbo", "html", "http://furbo.org/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .notAFeed)
|
||||
}
|
||||
|
||||
func testInessentialHTMLType() {
|
||||
|
||||
let d = parserData("inessential", "html", "http://inessential.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .notAFeed)
|
||||
}
|
||||
|
||||
func testSixColorsHTMLType() {
|
||||
|
||||
let d = parserData("sixcolors", "html", "https://sixcolors.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .notAFeed)
|
||||
}
|
||||
|
||||
// MARK: RSS
|
||||
|
||||
func testEMarleyRSSType() {
|
||||
|
||||
let d = parserData("EMarley", "rss", "https://medium.com/@emarley")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testScriptingNewsRSSType() {
|
||||
|
||||
let d = parserData("scriptingNews", "rss", "http://scripting.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testKatieFloydRSSType() {
|
||||
|
||||
let d = parserData("KatieFloyd", "rss", "https://katiefloyd.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testMantonRSSType() {
|
||||
|
||||
let d = parserData("manton", "rss", "http://manton.org/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testDCRainmakerRSSType() {
|
||||
|
||||
let d = parserData("dcrainmaker", "xml", "https://www.dcrainmaker.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testMacworldRSSType() {
|
||||
|
||||
let d = parserData("macworld", "rss", "https://www.macworld.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testNatashaTheRobotRSSType() {
|
||||
|
||||
let d = parserData("natasha", "xml", "https://www.natashatherobot.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testDontHitSaveRSSWithBOMType() {
|
||||
|
||||
let d = parserData("donthitsave", "xml", "http://donthitsave.com/donthitsavefeed.xml")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testBioRDF() {
|
||||
let d = parserData("bio", "rdf", "http://connect.biorxiv.org/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
func testPHPXML() {
|
||||
let d = parserData("phpxml", "rss", "https://www.fcutrecht.net/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rss)
|
||||
}
|
||||
|
||||
// MARK: Atom
|
||||
|
||||
func testDaringFireballAtomType() {
|
||||
|
||||
// File extension is .rss, but it’s really an Atom feed.
|
||||
let d = parserData("DaringFireball", "rss", "http://daringfireball.net/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .atom)
|
||||
}
|
||||
|
||||
func testOneFootTsunamiAtomType() {
|
||||
|
||||
let d = parserData("OneFootTsunami", "atom", "http://onefoottsunami.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .atom)
|
||||
}
|
||||
|
||||
func testRussCoxAtomType() {
|
||||
let d = parserData("russcox", "atom", "https://research.swtch.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .atom)
|
||||
}
|
||||
|
||||
// MARK: RSS-in-JSON
|
||||
|
||||
func testScriptingNewsJSONType() {
|
||||
|
||||
let d = parserData("ScriptingNews", "json", "http://scripting.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .rssInJSON)
|
||||
}
|
||||
|
||||
// MARK: JSON Feed
|
||||
|
||||
func testInessentialJSONFeedType() {
|
||||
|
||||
let d = parserData("inessential", "json", "http://inessential.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .jsonFeed)
|
||||
}
|
||||
|
||||
func testAllThisJSONFeedType() {
|
||||
|
||||
let d = parserData("allthis", "json", "http://leancrew.com/allthis/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .jsonFeed)
|
||||
}
|
||||
|
||||
func testCurtJSONFeedType() {
|
||||
|
||||
let d = parserData("curt", "json", "http://curtclifton.net/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .jsonFeed)
|
||||
}
|
||||
|
||||
func testPixelEnvyJSONFeedType() {
|
||||
|
||||
let d = parserData("pxlnv", "json", "http://pxlnv.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .jsonFeed)
|
||||
}
|
||||
|
||||
func testRoseJSONFeedType() {
|
||||
|
||||
let d = parserData("rose", "json", "https://www.rosemaryorchard.com/")
|
||||
let type = FeedType.feedType(d.data)
|
||||
XCTAssertTrue(type == .jsonFeed)
|
||||
}
|
||||
|
||||
// MARK: Unknown
|
||||
|
||||
func testPartialAllThisUnknownFeedType() {
|
||||
|
||||
// In the case of this feed, the partial data isn’t enough to detect that it’s a JSON Feed.
|
||||
// The type detector should return .unknown rather than .notAFeed.
|
||||
|
||||
let d = parserData("allthis-partial", "json", "http://leancrew.com/allthis/")
|
||||
let type = FeedType.feedType(d.data, isPartialData: true)
|
||||
XCTAssertEqual(type, .unknown)
|
||||
}
|
||||
|
||||
// MARK: Performance
|
||||
|
||||
func testFeedTypePerformance() {
|
||||
|
||||
// 0.000 on my 2012 iMac.
|
||||
|
||||
let d = parserData("EMarley", "rss", "https://medium.com/@emarley")
|
||||
self.measure {
|
||||
let _ = FeedType.feedType(d.data)
|
||||
}
|
||||
}
|
||||
|
||||
func testFeedTypePerformance2() {
|
||||
|
||||
// 0.000 on my 2012 iMac.
|
||||
|
||||
let d = parserData("inessential", "json", "http://inessential.com/")
|
||||
self.measure {
|
||||
let _ = FeedType.feedType(d.data)
|
||||
}
|
||||
}
|
||||
|
||||
func testFeedTypePerformance3() {
|
||||
|
||||
// 0.000 on my 2012 iMac.
|
||||
|
||||
let d = parserData("DaringFireball", "html", "http://daringfireball.net/")
|
||||
self.measure {
|
||||
let _ = FeedType.feedType(d.data)
|
||||
}
|
||||
}
|
||||
|
||||
func testFeedTypePerformance4() {
|
||||
|
||||
// 0.001 on my 2012 iMac.
|
||||
|
||||
let d = parserData("DaringFireball", "rss", "http://daringfireball.net/")
|
||||
self.measure {
|
||||
let _ = FeedType.feedType(d.data)
|
||||
}
|
||||
}
|
||||
}
|
41
Parser/Tests/ParserTests/HTMLLinkTests.swift
Normal file
41
Parser/Tests/ParserTests/HTMLLinkTests.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// HTMLLinkTests.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/25/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Parser
|
||||
|
||||
final class HTMLLinkTests: XCTestCase {
|
||||
|
||||
func testSixColorsPerformance() {
|
||||
|
||||
// 0.003 sec on my 2012 iMac
|
||||
let d = parserData("sixcolors", "html", "http://sixcolors.com/")
|
||||
self.measure {
|
||||
let _ = HTMLLinkParser.htmlLinks(with: d)
|
||||
}
|
||||
}
|
||||
|
||||
func testSixColorsLink() {
|
||||
|
||||
let d = parserData("sixcolors", "html", "http://sixcolors.com/")
|
||||
let links = HTMLLinkParser.htmlLinks(with: d)
|
||||
|
||||
let linkToFind = "https://www.theincomparable.com/theincomparable/290/index.php"
|
||||
let textToFind = "this week’s episode of The Incomparable"
|
||||
|
||||
var found = false
|
||||
for oneLink in links {
|
||||
if let urlString = oneLink.urlString, let text = oneLink.text, urlString == linkToFind, text == textToFind {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertTrue(found)
|
||||
XCTAssertEqual(links.count, 131)
|
||||
}
|
||||
}
|
153
Parser/Tests/ParserTests/HTMLMetadataTests.swift
Normal file
153
Parser/Tests/ParserTests/HTMLMetadataTests.swift
Normal file
@ -0,0 +1,153 @@
|
||||
//
|
||||
// HTMLMetadataTests.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/25/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Parser
|
||||
|
||||
final class HTMLMetadataTests: XCTestCase {
|
||||
|
||||
func testDaringFireball() {
|
||||
|
||||
let d = parserData("DaringFireball", "html", "http://daringfireball.net/")
|
||||
let metadata = HTMLMetadataParser.metadata(with: d)
|
||||
|
||||
XCTAssertEqual(metadata.favicons?.first?.urlString, "http://daringfireball.net/graphics/favicon.ico?v=005")
|
||||
|
||||
XCTAssertEqual(metadata.feedLinks?.count, 1)
|
||||
|
||||
let feedLink: HTMLMetadataFeedLink = (metadata.feedLinks?.first!)!
|
||||
XCTAssertNil(feedLink.title)
|
||||
XCTAssertEqual(feedLink.type, "application/atom+xml")
|
||||
XCTAssertEqual(feedLink.urlString, "http://daringfireball.net/feeds/main")
|
||||
}
|
||||
|
||||
func testDaringFireballPerformance() {
|
||||
|
||||
// 0.002 sec on my 2012 iMac
|
||||
let d = parserData("DaringFireball", "html", "http://daringfireball.net/")
|
||||
self.measure {
|
||||
let _ = HTMLMetadataParser.metadata(with: d)
|
||||
}
|
||||
}
|
||||
|
||||
func testFurbo() {
|
||||
|
||||
let d = parserData("furbo", "html", "http://furbo.org/")
|
||||
let metadata = HTMLMetadataParser.metadata(with: d)
|
||||
|
||||
XCTAssertEqual(metadata.favicons?.first?.urlString, "http://furbo.org/favicon.ico")
|
||||
|
||||
XCTAssertEqual(metadata.feedLinks?.count, 1)
|
||||
|
||||
let feedLink = (metadata.feedLinks?.first!)!
|
||||
XCTAssertEqual(feedLink.title, "Iconfactory News Feed")
|
||||
XCTAssertEqual(feedLink.type, "application/rss+xml")
|
||||
}
|
||||
|
||||
func testFurboPerformance() {
|
||||
|
||||
// 0.001 sec on my 2012 iMac
|
||||
let d = parserData("furbo", "html", "http://furbo.org/")
|
||||
self.measure {
|
||||
let _ = HTMLMetadataParser.metadata(with: d)
|
||||
}
|
||||
}
|
||||
|
||||
func testInessential() {
|
||||
|
||||
let d = parserData("inessential", "html", "http://inessential.com/")
|
||||
let metadata = HTMLMetadataParser.metadata(with: d)
|
||||
|
||||
XCTAssertNil(metadata.favicons?.first?.urlString)
|
||||
|
||||
XCTAssertEqual(metadata.feedLinks?.count, 1)
|
||||
let feedLink = (metadata.feedLinks?.first!)!
|
||||
XCTAssertEqual(feedLink.title, "RSS")
|
||||
XCTAssertEqual(feedLink.type, "application/rss+xml")
|
||||
XCTAssertEqual(feedLink.urlString, "http://inessential.com/xml/rss.xml")
|
||||
|
||||
XCTAssertEqual(metadata.appleTouchIcons?.count ?? 0, 0);
|
||||
}
|
||||
|
||||
func testInessentialPerformance() {
|
||||
|
||||
// 0.001 sec on my 2012 iMac
|
||||
let d = parserData("inessential", "html", "http://inessential.com/")
|
||||
self.measure {
|
||||
let _ = HTMLMetadataParser.metadata(with: d)
|
||||
}
|
||||
}
|
||||
|
||||
func testCocoPerformance() {
|
||||
|
||||
// 0.004 sec on my 2012 iMac
|
||||
let d = parserData("coco", "html", "https://www.theatlantic.com/entertainment/archive/2017/11/coco-is-among-pixars-best-movies-in-years/546695/")
|
||||
self.measure {
|
||||
let _ = HTMLMetadataParser.metadata(with: d)
|
||||
}
|
||||
}
|
||||
|
||||
func testSixColors() {
|
||||
|
||||
let d = parserData("sixcolors", "html", "http://sixcolors.com/")
|
||||
let metadata = HTMLMetadataParser.metadata(with: d)
|
||||
|
||||
XCTAssertEqual(metadata.favicons?.first?.urlString, "https://sixcolors.com/images/favicon.ico")
|
||||
|
||||
XCTAssertEqual(metadata.feedLinks?.count, 1);
|
||||
let feedLink = (metadata.feedLinks?.first!)!
|
||||
XCTAssertEqual(feedLink.title, "RSS");
|
||||
XCTAssertEqual(feedLink.type, "application/rss+xml");
|
||||
XCTAssertEqual(feedLink.urlString, "http://feedpress.me/sixcolors");
|
||||
|
||||
XCTAssertEqual(metadata.appleTouchIcons!.count, 6);
|
||||
let icon = metadata.appleTouchIcons![3];
|
||||
XCTAssertEqual(icon.rel, "apple-touch-icon");
|
||||
XCTAssertEqual(icon.sizes, "120x120");
|
||||
XCTAssertEqual(icon.urlString, "https://sixcolors.com/apple-touch-icon-120.png");
|
||||
}
|
||||
|
||||
func testSixColorsPerformance() {
|
||||
|
||||
// 0.002 sec on my 2012 iMac
|
||||
let d = parserData("sixcolors", "html", "http://sixcolors.com/")
|
||||
self.measure {
|
||||
let _ = HTMLMetadataParser.metadata(with: d)
|
||||
}
|
||||
}
|
||||
|
||||
func testCocoOGImage() {
|
||||
|
||||
let d = parserData("coco", "html", "https://www.theatlantic.com/entertainment/archive/2017/11/coco-is-among-pixars-best-movies-in-years/546695/")
|
||||
let metadata = HTMLMetadataParser.metadata(with: d)
|
||||
let openGraphData = metadata.openGraphProperties!
|
||||
let image = openGraphData.image!
|
||||
XCTAssert(image.url == "https://cdn.theatlantic.com/assets/media/img/mt/2017/11/1033101_first_full_length_trailer_arrives_pixars_coco/facebook.jpg?1511382177")
|
||||
}
|
||||
|
||||
func testCocoTwitterImage() {
|
||||
|
||||
let d = parserData("coco", "html", "https://www.theatlantic.com/entertainment/archive/2017/11/coco-is-among-pixars-best-movies-in-years/546695/")
|
||||
let metadata = HTMLMetadataParser.metadata(with: d)
|
||||
let twitterData = metadata.twitterProperties!
|
||||
let imageURL = twitterData.imageURL!
|
||||
XCTAssert(imageURL == "https://cdn.theatlantic.com/assets/media/img/mt/2017/11/1033101_first_full_length_trailer_arrives_pixars_coco/facebook.jpg?1511382177")
|
||||
}
|
||||
|
||||
func testYouTube() {
|
||||
// YouTube is a special case — the feed links appear after the head section, in the body section.
|
||||
let d = parserData("YouTubeTheVolvoRocks", "html", "https://www.youtube.com/user/TheVolvorocks")
|
||||
let metadata = HTMLMetadataParser.metadata(with: d)
|
||||
|
||||
XCTAssertEqual(metadata.feedLinks!.count, 1);
|
||||
let feedLink = metadata.feedLinks!.first!
|
||||
XCTAssertEqual(feedLink.title, "RSS");
|
||||
XCTAssertEqual(feedLink.type, "application/rss+xml");
|
||||
XCTAssertEqual(feedLink.urlString, "https://www.youtube.com/feeds/videos.xml?channel_id=UCct7QF2jcWRY6dhXWMSq9LQ");
|
||||
}
|
||||
}
|
124
Parser/Tests/ParserTests/JSONFeedParserTests.swift
Normal file
124
Parser/Tests/ParserTests/JSONFeedParserTests.swift
Normal file
@ -0,0 +1,124 @@
|
||||
//
|
||||
// JSONFeedParserTests.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/26/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Parser
|
||||
|
||||
final class JSONFeedParserTests: XCTestCase {
|
||||
|
||||
func testInessentialPerformance() {
|
||||
|
||||
// 0.001 sec on my 2012 iMac.
|
||||
let d = parserData("inessential", "json", "http://inessential.com/")
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testDaringFireballPerformance() {
|
||||
|
||||
// 0.009 sec on my 2012 iMac.
|
||||
let d = parserData("DaringFireball", "json", "http://daringfireball.net/")
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testGettingFaviconAndIconURLs() async {
|
||||
|
||||
let d = parserData("DaringFireball", "json", "http://daringfireball.net/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssert(parsedFeed.faviconURL == "https://daringfireball.net/graphics/favicon-64.png")
|
||||
XCTAssert(parsedFeed.iconURL == "https://daringfireball.net/graphics/apple-touch-icon.png")
|
||||
}
|
||||
|
||||
func testAllThis() async {
|
||||
|
||||
let d = parserData("allthis", "json", "http://leancrew.com/allthis/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertEqual(parsedFeed.items.count, 12)
|
||||
}
|
||||
|
||||
func testCurt() async {
|
||||
|
||||
let d = parserData("curt", "json", "http://curtclifton.net/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertEqual(parsedFeed.items.count, 26)
|
||||
|
||||
var didFindTwitterQuitterArticle = false
|
||||
for article in parsedFeed.items {
|
||||
if article.title == "Twitter Quitter" {
|
||||
didFindTwitterQuitterArticle = true
|
||||
XCTAssertTrue(article.contentHTML!.hasPrefix("<p>I’ve decided to close my Twitter account. William Van Hecke <a href=\"https://tinyletter.com/fet/letters/microcosmographia-xlxi-reasons-to-stay-on-twitter\">makes a convincing case</a>"))
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertTrue(didFindTwitterQuitterArticle)
|
||||
}
|
||||
|
||||
func testPixelEnvy() async {
|
||||
|
||||
let d = parserData("pxlnv", "json", "http://pxlnv.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssertEqual(parsedFeed.items.count, 20)
|
||||
|
||||
}
|
||||
|
||||
func testRose() async {
|
||||
let d = parserData("rose", "json", "http://www.rosemaryorchard.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssertEqual(parsedFeed.items.count, 84)
|
||||
}
|
||||
|
||||
func test3960() async {
|
||||
let d = parserData("3960", "json", "http://journal.3960.org/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssertEqual(parsedFeed.items.count, 20)
|
||||
XCTAssertEqual(parsedFeed.language, "de-DE")
|
||||
|
||||
for item in parsedFeed.items {
|
||||
XCTAssertEqual(item.language, "de-DE")
|
||||
}
|
||||
}
|
||||
|
||||
func testAuthors() async {
|
||||
let d = parserData("authors", "json", "https://example.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssertEqual(parsedFeed.items.count, 4)
|
||||
|
||||
let rootAuthors = Set([
|
||||
ParsedAuthor(name: "Root Author 1", url: nil, avatarURL: nil, emailAddress: nil),
|
||||
ParsedAuthor(name: "Root Author 2", url: nil, avatarURL: nil, emailAddress: nil)
|
||||
])
|
||||
let itemAuthors = Set([
|
||||
ParsedAuthor(name: "Item Author 1", url: nil, avatarURL: nil, emailAddress: nil),
|
||||
ParsedAuthor(name: "Item Author 2", url: nil, avatarURL: nil, emailAddress: nil)
|
||||
])
|
||||
let legacyItemAuthors = Set([
|
||||
ParsedAuthor(name: "Legacy Item Author", url: nil, avatarURL: nil, emailAddress: nil)
|
||||
])
|
||||
|
||||
XCTAssertEqual(parsedFeed.authors?.count, 2)
|
||||
XCTAssertEqual(parsedFeed.authors, rootAuthors)
|
||||
|
||||
let noAuthorsItem = parsedFeed.items.first { $0.uniqueID == "Item without authors" }!
|
||||
XCTAssertEqual(noAuthorsItem.authors, nil)
|
||||
|
||||
let legacyAuthorItem = parsedFeed.items.first { $0.uniqueID == "Item with legacy author" }!
|
||||
XCTAssertEqual(legacyAuthorItem.authors, legacyItemAuthors)
|
||||
|
||||
let modernAuthorsItem = parsedFeed.items.first { $0.uniqueID == "Item with modern authors" }!
|
||||
XCTAssertEqual(modernAuthorsItem.authors, itemAuthors)
|
||||
|
||||
let bothAuthorsItem = parsedFeed.items.first { $0.uniqueID == "Item with both" }!
|
||||
XCTAssertEqual(bothAuthorsItem.authors, itemAuthors)
|
||||
}
|
||||
}
|
87
Parser/Tests/ParserTests/OPMLTests.swift
Normal file
87
Parser/Tests/ParserTests/OPMLTests.swift
Normal file
@ -0,0 +1,87 @@
|
||||
//
|
||||
// OPMLTests.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/25/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import Parser
|
||||
|
||||
final class OPMLTests: XCTestCase {
|
||||
|
||||
let subsData = parserData("Subs", "opml", "http://example.org/")
|
||||
|
||||
func testOPMLParsingPerformance() {
|
||||
|
||||
// 0.003 sec on my M1 Mac Studio 2022
|
||||
self.measure {
|
||||
let _ = OPMLParser.document(with: self.subsData)
|
||||
}
|
||||
}
|
||||
|
||||
func testNotOPML() {
|
||||
|
||||
let d = parserData("DaringFireball", "rss", "http://daringfireball.net/")
|
||||
XCTAssertNil(OPMLParser.document(with: d))
|
||||
}
|
||||
|
||||
func testSubsStructure() {
|
||||
let opmlDocument = OPMLParser.document(with: subsData)
|
||||
XCTAssertNotNil(opmlDocument)
|
||||
|
||||
XCTAssertEqual("Subs", opmlDocument!.title)
|
||||
XCTAssertEqual("http://example.org/", opmlDocument!.url)
|
||||
recursivelyCheckOPMLStructure(opmlDocument!)
|
||||
}
|
||||
|
||||
|
||||
func testFindingTitles() {
|
||||
// https://github.com/brentsimmons/NetNewsWire/issues/527
|
||||
// Fix a bug where titles aren’t found when there’s no title attribute in the OPML,
|
||||
// which appears to be true with OPML generated by The Old Reader.
|
||||
|
||||
let d = parserData("SubsNoTitleAttributes", "opml", "http://example.org/")
|
||||
let opmlDocument = OPMLParser.document(with: d)
|
||||
recursivelyCheckOPMLStructure(opmlDocument!)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension OPMLTests {
|
||||
|
||||
func recursivelyCheckOPMLStructure(_ item: OPMLItem) {
|
||||
let feedSpecifier = item.feedSpecifier
|
||||
if !(item is OPMLDocument) {
|
||||
XCTAssertNotNil(item.attributes!.opml_text)
|
||||
}
|
||||
|
||||
// If it has no children, it should have a feed specifier. The converse is also true.
|
||||
var isFolder = item.items != nil && item.items!.count > 0
|
||||
if !isFolder && item.attributes?.opml_title == "Skip" {
|
||||
isFolder = true
|
||||
}
|
||||
|
||||
if !isFolder {
|
||||
XCTAssertNotNil(feedSpecifier!.title)
|
||||
XCTAssertNotNil(feedSpecifier!.feedURL)
|
||||
}
|
||||
else {
|
||||
XCTAssertNil(feedSpecifier)
|
||||
}
|
||||
|
||||
if item.items != nil && item.items!.count > 0 {
|
||||
for oneItem in item.items! {
|
||||
recursivelyCheckOPMLStructure(oneItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parserData(_ filename: String, _ fileExtension: String, _ url: String) -> ParserData {
|
||||
let filename = "Resources/\(filename)"
|
||||
let path = Bundle.module.path(forResource: filename, ofType: fileExtension)!
|
||||
let data = try! Data(contentsOf: URL(fileURLWithPath: path))
|
||||
return ParserData(url: url, data: data)
|
||||
}
|
36
Parser/Tests/ParserTests/RSSInJSONParserTests.swift
Normal file
36
Parser/Tests/ParserTests/RSSInJSONParserTests.swift
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// RSSInJSONParserTests.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/26/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Parser
|
||||
|
||||
final class RSSInJSONParserTests: XCTestCase {
|
||||
|
||||
func testScriptingNewsPerformance() {
|
||||
|
||||
// 0.003 sec on my 2012 iMac.
|
||||
let d = parserData("ScriptingNews", "json", "http://scripting.com/")
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testFeedLanguage() {
|
||||
let d = parserData("ScriptingNews", "json", "http://scripting.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssertEqual(parsedFeed.language, "en-us")
|
||||
}
|
||||
}
|
||||
|
||||
extension FeedParser {
|
||||
|
||||
static func parse(_ parserData: ParserData) throws -> ParsedFeed? {
|
||||
|
||||
try FeedParser.parse(urlString: parserData.url, data: parserData.data)
|
||||
}
|
||||
}
|
238
Parser/Tests/ParserTests/RSSParserTests.swift
Normal file
238
Parser/Tests/ParserTests/RSSParserTests.swift
Normal file
@ -0,0 +1,238 @@
|
||||
//
|
||||
// RSSParserTests.swift
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 6/26/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Parser
|
||||
|
||||
final class RSSParserTests: XCTestCase {
|
||||
|
||||
func testScriptingNewsPerformance() {
|
||||
|
||||
// 0.004 sec on my 2012 iMac.
|
||||
// 0.002 2022 Mac Studio
|
||||
let d = parserData("scriptingNews", "rss", "http://scripting.com/")
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testKatieFloydPerformance() {
|
||||
|
||||
// 0.004 sec on my 2012 iMac.
|
||||
// 0.001 2022 Mac Studio
|
||||
let d = parserData("KatieFloyd", "rss", "http://katiefloyd.com/")
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testEMarleyPerformance() {
|
||||
|
||||
// 0.001 sec on my 2012 iMac.
|
||||
// 0.0004 2022 Mac Studio
|
||||
let d = parserData("EMarley", "rss", "https://medium.com/@emarley")
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testMantonPerformance() {
|
||||
|
||||
// 0.002 sec on my 2012 iMac.
|
||||
// 0.0006 2022 Mac Studio
|
||||
let d = parserData("manton", "rss", "http://manton.org/")
|
||||
self.measure {
|
||||
let _ = try! FeedParser.parse(d)
|
||||
}
|
||||
}
|
||||
|
||||
func testNatashaTheRobot() async {
|
||||
|
||||
let d = parserData("natasha", "xml", "https://www.natashatherobot.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssertEqual(parsedFeed.items.count, 10)
|
||||
}
|
||||
|
||||
func testTheOmniShowAttachments() async {
|
||||
|
||||
let d = parserData("theomnishow", "rss", "https://theomnishow.omnigroup.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.items.count > 0)
|
||||
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNotNil(article.attachments)
|
||||
XCTAssertEqual(article.attachments!.count, 1)
|
||||
let attachment = Array(article.attachments!).first!
|
||||
XCTAssertNotNil(attachment.mimeType)
|
||||
XCTAssertNotNil(attachment.sizeInBytes)
|
||||
XCTAssert(attachment.url.contains("cloudfront"))
|
||||
XCTAssertGreaterThanOrEqual(attachment.sizeInBytes!, 22275279)
|
||||
XCTAssertEqual(attachment.mimeType, "audio/mpeg")
|
||||
}
|
||||
}
|
||||
|
||||
func testTheOmniShowUniqueIDs() async {
|
||||
|
||||
let d = parserData("theomnishow", "rss", "https://theomnishow.omnigroup.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.items.count > 0)
|
||||
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNotNil(article.uniqueID)
|
||||
XCTAssertTrue(article.uniqueID.hasPrefix("https://theomnishow.omnigroup.com/episode/"))
|
||||
}
|
||||
}
|
||||
|
||||
func testMacworldUniqueIDs() async {
|
||||
|
||||
// Macworld’s feed doesn’t have guids, so they should be calculated unique IDs.
|
||||
|
||||
let d = parserData("macworld", "rss", "https://www.macworld.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNotNil(article.uniqueID)
|
||||
XCTAssertEqual(article.uniqueID.count, 32) // calculated unique IDs are MD5 hashes
|
||||
}
|
||||
}
|
||||
|
||||
func testMacworldAuthors() async {
|
||||
|
||||
// Macworld uses names instead of email addresses (despite the RSS spec saying they should be email addresses).
|
||||
|
||||
let d = parserData("macworld", "rss", "https://www.macworld.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.items.count > 0)
|
||||
|
||||
for article in parsedFeed.items {
|
||||
|
||||
let author = article.authors!.first!
|
||||
XCTAssertNil(author.emailAddress)
|
||||
XCTAssertNil(author.url)
|
||||
XCTAssertNotNil(author.name)
|
||||
}
|
||||
}
|
||||
|
||||
func testMonkeyDomGuids() async {
|
||||
|
||||
// https://coding.monkeydom.de/posts.rss has a bug in the feed (at this writing):
|
||||
// It has guids that are supposed to be permalinks, per the spec —
|
||||
// except that they’re not actually permalinks. The RSS parser should
|
||||
// detect this situation, and every article in the feed should have a permalink.
|
||||
|
||||
let d = parserData("monkeydom", "rss", "https://coding.monkeydom.de/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.items.count > 0)
|
||||
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNil(article.url)
|
||||
XCTAssertNotNil(article.uniqueID)
|
||||
}
|
||||
}
|
||||
|
||||
func testEmptyContentEncoded() async {
|
||||
// The ATP feed (at the time of this writing) has some empty content:encoded elements. The parser should ignore those.
|
||||
// https://github.com/brentsimmons/NetNewsWire/issues/529
|
||||
|
||||
let d = parserData("atp", "rss", "http://atp.fm/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.items.count > 0)
|
||||
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNotNil(article.contentHTML)
|
||||
}
|
||||
}
|
||||
|
||||
func testFeedKnownToHaveGuidsThatArentPermalinks() async {
|
||||
let d = parserData("livemint", "xml", "https://www.livemint.com/rss/news")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.items.count > 0)
|
||||
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNil(article.url)
|
||||
}
|
||||
}
|
||||
|
||||
func testAuthorsWithTitlesInside() async {
|
||||
// This feed uses atom authors, and we don’t want author/title to be used as item/title.
|
||||
// https://github.com/brentsimmons/NetNewsWire/issues/943
|
||||
let d = parserData("cloudblog", "rss", "https://cloudblog.withgoogle.com/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.items.count > 0)
|
||||
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNotEqual(article.title, "Product Manager, Office of the CTO")
|
||||
XCTAssertNotEqual(article.title, "Developer Programs Engineer")
|
||||
XCTAssertNotEqual(article.title, "Product Director")
|
||||
}
|
||||
}
|
||||
|
||||
func testTitlesWithInvalidFeedWithImageStructures() async {
|
||||
// This invalid feed has <image> elements inside <item>s.
|
||||
// 17 Jan 2021 bug report — we’re not parsing titles in this feed.
|
||||
let d = parserData("aktuality", "rss", "https://www.aktuality.sk/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertTrue(parsedFeed.items.count > 0)
|
||||
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNotNil(article.title)
|
||||
}
|
||||
}
|
||||
|
||||
func testFeedLanguage() async {
|
||||
let d = parserData("manton", "rss", "http://manton.org/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssertEqual(parsedFeed.language, "en-US")
|
||||
}
|
||||
|
||||
func testFeedWithGB2312Encoding() {
|
||||
// This feed has an encoding we don’t run into very often.
|
||||
// https://github.com/Ranchero-Software/NetNewsWire/issues/1477
|
||||
let d = parserData("kc0011", "rss", "http://kc0011.net/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssert(parsedFeed.items.count > 0)
|
||||
for article in parsedFeed.items {
|
||||
XCTAssertNotNil(article.contentHTML)
|
||||
}
|
||||
}
|
||||
|
||||
func testVincodeDates() {
|
||||
let d = parserData("vincode", "rss", "https://vincode.io/feed.xml")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
XCTAssert(parsedFeed.items.count > 0)
|
||||
|
||||
var didFindFirstTestArticle = false
|
||||
var didFindSecondTestArticle = false
|
||||
for article in parsedFeed.items {
|
||||
|
||||
if article.title == "Drag Boat Race in Parker, AZ" {
|
||||
didFindFirstTestArticle = true
|
||||
// Tue, 07 Mar 2023 15:15:29 -0500
|
||||
let expectedDatePublished = dateWithValues(2023, 3, 7, 20, 15, 29)
|
||||
XCTAssertEqual(article.datePublished, expectedDatePublished)
|
||||
}
|
||||
else if article.title == "Ventura’s System Settings" {
|
||||
didFindSecondTestArticle = true
|
||||
// Sun, 30 Oct 2022 11:58:26 -0500
|
||||
let expectedDatePublished = dateWithValues(2022, 10, 30, 16, 58, 26)
|
||||
XCTAssertEqual(article.datePublished, expectedDatePublished)
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertTrue(didFindFirstTestArticle)
|
||||
XCTAssertTrue(didFindSecondTestArticle)
|
||||
}
|
||||
}
|
642
Parser/Tests/ParserTests/Resources/3960.json
Normal file
642
Parser/Tests/ParserTests/Resources/3960.json
Normal file
File diff suppressed because one or more lines are too long
149
Parser/Tests/ParserTests/Resources/489.rss
Normal file
149
Parser/Tests/ParserTests/Resources/489.rss
Normal file
@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
|
||||
<channel>
|
||||
<title><![CDATA[Stories by Hodl Hodl on Medium]]></title>
|
||||
<description><![CDATA[Stories by Hodl Hodl on Medium]]></description>
|
||||
<link>https://medium.com/@hodlhodl?source=rss-b1f3d322dadf------2</link>
|
||||
<image>
|
||||
<url>https://cdn-images-1.medium.com/fit/c/150/150/1*PJ4xeTc0v0DOWgJIb25k8Q.jpeg</url>
|
||||
<title>Stories by Hodl Hodl on Medium</title>
|
||||
<link>https://medium.com/@hodlhodl?source=rss-b1f3d322dadf------2</link>
|
||||
</image>
|
||||
<generator>Medium</generator>
|
||||
<lastBuildDate>Thu, 22 Nov 2018 05:50:04 GMT</lastBuildDate>
|
||||
<atom:link href="https://medium.com/feed/@hodlhodl" rel="self" type="application/rss+xml"/>
|
||||
<webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
|
||||
<atom:link href="http://medium.superfeedr.com" rel="hub"/>
|
||||
<item>
|
||||
<title><![CDATA[Hodl Hodl closes funding round]]></title>
|
||||
<link>https://medium.com/@hodlhodl/hodl-hodl-closes-funding-round-417d97952d42?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/417d97952d42</guid>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<category><![CDATA[funding]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Mon, 19 Nov 2018 08:10:26 GMT</pubDate>
|
||||
<atom:updated>2018-11-19T08:42:59.267Z</atom:updated>
|
||||
<content:encoded><![CDATA[<p>For a long time Hodl Hodl remained self-funded. It was our own money that paid for the development and the team. This year we realized we needed to grow and move forward faster so we decided to start looking for external funding. We initially talked to a number of VCs and after months of negotiations it became obvious this wouldn’t work. Some declined us, some were constantly putting us on hold. We figured it’s just the way it works with VCs (read an awesome article <a href="http://paulgraham.com/fr.html">“How to raise money”</a> by Paul Graham on the subject, very helpful if you’re raising money for the first time).</p><p>In retrospect, we don’t really think VC funding would’ve been the right fit for a Bitcoin-company like ours. We don’t do KYC/AML and we’re not bending and selling our customers (and not because we’re so good and honest, but because that’s part of the value we provide and, thus, part of our business model) — and, if we took that VC money, our feeling was, we’d start bending.</p><p>Instead, we were very lucky to be approached by a number of bitcoiners who attended our “<a href="https://bh2018.hodlhodl.com">Baltic Honeybadger</a>” conference, know us personally and decided that they wanted to invest in Hodl Hodl. And so they did.</p><p>So today, we’re proud to announce that <a href="https://twitter.com/WhalePanda">WhalePanda</a>, <a href="https://twitter.com/anambroid">Ambroid</a>, <a href="https://twitter.com/Marsmensch">Marsmensch</a> and two more undisclosed persons became the first investors of Hodl Hodl.</p><p>In 2019 Hodl Hodl will be:</p><ol><li>Improving the P2P Exchange platform</li><li>Releasing P2P prediction contracts market (aka P2P Bitcoin Futures)</li><li>Releasing a number of other stealth features that will hopefully be incredibly useful to the Bitcoin economy.</li></ol><p><strong>In general, Hodl Hodl will become THE platform for multisig contracts on Bitcoin.</strong></p><p>We would like to thank all our investors for believing in us and we hope to not only deliver what is expected of us, but become good friends with them all over time. They’ve been really helpful and were encouraging us from Hodl Hodl’s launch so we duly appreciate that.</p><p>And finally, some good news for our customers: we’ll lower the exchange commission to 0.4% for December 2018, until the end of the year.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=417d97952d42" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Update on Hodl Hodl: New payment type “Cryptocurrency”]]></title>
|
||||
<link>https://medium.com/@hodlhodl/update-on-hodl-hodl-new-payment-type-cryptocurrency-ca7b7ab94b2f?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/ca7b7ab94b2f</guid>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Fri, 09 Nov 2018 14:40:34 GMT</pubDate>
|
||||
<atom:updated>2018-11-09T14:40:34.747Z</atom:updated>
|
||||
<content:encoded><![CDATA[<p>Dear Hodlers,</p><p>We want introduce to you an update at Hodl Hodl — we have added a new payment type “Cryptocurrency”.</p><p><strong>Quick summary</strong></p><p>From now on, every user can buy and sell Bitcoin and Litecoin, for other cryptocurrencies at Hodl Hodl.</p><p><strong>About</strong></p><p>When creating an offer, every user has to specify the payment method(-s) that he will accept. And every payment method is tied to a specific payment type: for example, payment methods “SWIFT” and “SEPA” are tied to the payment type “Bank wire”.</p><p>With this update, in the list of payment types, you can find a new one: “Cryptocurrency”.</p><p>It is tied to different payment methods such as: “Dash”, “Monero”, “Ethereum”, etc., which can be used to buy and sell your bitcoins and litecoins.</p><p><strong>Further plans</strong></p><p>We also have plans related to cryptocurrencies as payment methods. In the future, our customers will be able to set prices in terms of cryptocurrency, not fiat, as it is currently.</p><p>So you could create a Bitcoin buy or sell offer, for the price of 60 monero’s per 1 Bitcoin.</p><p><strong>Propose a payment method</strong></p><p>In case you don’t see the cryptocurrency you need in the list, you can propose one in the page “Payment methods” (which you can find in the user menu in the right top corner).</p><p>Once administrator approves your request — you will able to create an offer with the newly added cryptocurrency.</p><p><strong>Previously</strong></p><p>We had a payment method “Cryptocurrency” related to the payment type “Online payment system”, and with this update, we have deleted this payment method and disabled all offers using it.</p><p>Therefore, if you were the owner of such an offer, we kindly ask you to update your offer(-s), please set new payment methods using the exact name of the cryptocurrency you want, and don’t forget, you can set multiple payment methods in the same offer.</p><p><strong>Reach us via</strong></p><ul><li>Hodl Hodl exchange: <a href="http://hodlhodl.com/">hodlhodl.com</a></li><li>TESTNET Hodl Hodl exchange: <a href="http://testnet.hodlhodl.com/">testnet.hodlhodl.com</a></li><li>E-mail: support@hodlhodl.com</li><li>Blog: <a href="https://medium.com/@hodlhodl">https://medium.com/@hodlhodl</a></li><li>Twitter: <a href="https://twitter.com/hodlhodl">https://twitter.com/hodlhodl</a></li><li>Telegram: <a href="https://t.me/HodlHodl">https://t.me/HodlHodl</a></li><li>Reddit: <a href="https://www.reddit.com/r/hodlhodl/">https://www.reddit.com/r/hodlhodl</a></li><li>Slack: <a href="https://goo.gl/zaMnCn">https://goo.gl/zaMnCn</a></li><li>Facebook: <a href="https://www.facebook.com/hodlexchange/">https://www.facebook.com/hodlexchange</a></li><li>YouTube: <a href="https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg">https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg</a></li></ul><p><strong>Hodl!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ca7b7ab94b2f" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Introducing a new feature: Lower exchange fees for staying online]]></title>
|
||||
<link>https://medium.com/@hodlhodl/introducing-a-new-feature-lower-exchange-fees-for-staying-online-302906f8b49f?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/302906f8b49f</guid>
|
||||
<category><![CDATA[exchange]]></category>
|
||||
<category><![CDATA[litecoin]]></category>
|
||||
<category><![CDATA[p2p]]></category>
|
||||
<category><![CDATA[cryptocurrency]]></category>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Thu, 08 Nov 2018 14:02:57 GMT</pubDate>
|
||||
<atom:updated>2018-11-08T14:02:57.512Z</atom:updated>
|
||||
<content:encoded><![CDATA[<p>Dear Hodlers,</p><p>Today we introduce a new feature at Hodl Hodl — lower exchange fees for staying online.</p><p><strong>Quick summary</strong></p><p>Based on feedback from our users, we understand that everyone wants to see other users online at Hodl Hodl, otherwise, when starting a trade, nobody knows when it will advance from there.</p><p>So from now on, every user will get an exchange fee discount for staying online.</p><p><strong>Description</strong></p><p>Starting from today, Hodl Hodl now gives a discount of 20% off the exchange fee for being online for at least 8 hours within the past 24 hours.</p><p>We placed a timer in every user’s dashboard, which indicates how much time you need to be online until your discount is applied. When you reach the required time online — we’ll indicate in your dashboard that you are receiving the exchange fee discount.</p><p>Please be advised, the “time online discount” is applied at the moment the trade starts.</p><p><strong>Reach us via</strong></p><ul><li>Hodl Hodl exchange: <a href="http://hodlhodl.com/">hodlhodl.com</a></li><li>TESTNET Hodl Hodl exchange: <a href="http://testnet.hodlhodl.com/">testnet.hodlhodl.com</a></li><li>E-mail: support@hodlhodl.com</li><li>Blog: <a href="https://medium.com/@hodlhodl">https://medium.com/@hodlhodl</a></li><li>Twitter: <a href="https://twitter.com/hodlhodl">https://twitter.com/hodlhodl</a></li><li>Telegram: <a href="https://t.me/HodlHodl">https://t.me/HodlHodl</a></li><li>Reddit: <a href="https://www.reddit.com/r/hodlhodl/">https://www.reddit.com/r/hodlhodl</a></li><li>Slack: <a href="https://goo.gl/zaMnCn">https://goo.gl/zaMnCn</a></li><li>Facebook: <a href="https://www.facebook.com/hodlexchange/">https://www.facebook.com/hodlexchange</a></li><li>YouTube: <a href="https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg">https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg</a></li></ul><p><strong>Hodl!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=302906f8b49f" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Hodl Hodl announces OTC trading desk and brokerage company Tenbagger]]></title>
|
||||
<link>https://medium.com/@hodlhodl/hodl-hodl-announces-otc-trading-desk-and-brokerage-company-tenbagger-fed4be91db6?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/fed4be91db6</guid>
|
||||
<category><![CDATA[exchange]]></category>
|
||||
<category><![CDATA[p2p]]></category>
|
||||
<category><![CDATA[litecoin]]></category>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<category><![CDATA[cryptocurrency]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Fri, 02 Nov 2018 14:41:47 GMT</pubDate>
|
||||
<atom:updated>2018-11-02T14:41:47.679Z</atom:updated>
|
||||
<content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T4YcnmS72gYkOBPYIWv5pg.png" /></figure><p>We are proud to announce our OTC trading desk!</p><p>From now on, Hodl Hodl, in partnership with our EU licensed broker <a href="https://tenbagger.co/">Tenbagger</a>, will match and guide counter-parties, allowing you to buy and sell bitcoins with same day settlement, at the most competitive rates.</p><p>There has been a vibrant OTC Bitcoin trading market operating in parallel to the existing exchanges, but none of them are offering non-custodial escrow services for cryptocurrencies which would eliminate the risk of losing funds.</p><p>Hodl Hodl has developed non-custodial cryptocurrency escrow that leverages the full potential of Bitcoin’s blockchain, and ensures the highest level of security. For each trade, we create a unique multisig escrow account on Bitcoin’s blockchain, ensuring transparency and the highest level of security.</p><p>This also doesn’t affect our current operations. We are still non-KYC/AML non-custodial P2P exchange suitable for any type of trade — small or large. We are sticking with our original plan to help people buy Bitcoin/Litecoin in an easy and secure way.</p><p>Why choose OTC trading with us?</p><p>- Secure transactions</p><p>- Competitive rates</p><p>- Same day settlement</p><p>- Private and personalized trading experience</p><p>- 24/7 personalized support</p><p>To contact us:</p><ul><li>Hodl Hodl exchange: <a href="https://hodlhodl.com/">hodlhodl.com</a></li><li>TESTNET Hodl Hodl exchange: <a href="https://testnet.hodlhodl.com/">testnet.hodlhodl.com</a></li><li>E-mail: otc@hodlhodl.com</li><li>Blog: <a href="https://medium.com/@hodlhodl">https://medium.com/@hodlhodl</a></li><li>Twitter: <a href="https://twitter.com/hodlhodl">https://twitter.com/hodlhodl</a></li><li>Telegram: <a href="https://t.me/HodlHodl">https://t.me/HodlHodl</a></li><li>Reddit: <a href="https://www.reddit.com/r/hodlhodl">https://www.reddit.com/r/hodlhodl</a></li><li>Slack: <a href="https://goo.gl/zaMnCn">https://goo.gl/zaMnCn</a></li><li>Facebook: <a href="https://www.facebook.com/hodlexchange">https://www.facebook.com/hodlexchange</a></li><li>YouTube: <a href="https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg">https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg</a></li></ul><p><strong>Hodl!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fed4be91db6" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Hodl Hodl introduces 2 out of 3 multisig escrow]]></title>
|
||||
<link>https://medium.com/@hodlhodl/hodl-hodl-introduces-2-out-of-3-multisig-escrow-b2110580e036?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/b2110580e036</guid>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<category><![CDATA[cryptocurrency]]></category>
|
||||
<category><![CDATA[p2p]]></category>
|
||||
<category><![CDATA[exchange]]></category>
|
||||
<category><![CDATA[litecoin]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Mon, 22 Oct 2018 14:38:05 GMT</pubDate>
|
||||
<atom:updated>2018-10-22T14:38:05.903Z</atom:updated>
|
||||
<content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fKGPumd7UGnsqnLjV54iZg.png" /></figure><p>Hey Hodlers,</p><p>Today <a href="http://hodlhodl.com/">Hodl Hodl</a>, a P2P cryptocurrency exchange, introduces a new type of multisig escrow account available for contracts at the exchange. From now on, during offer creation, every user will choose what type of contract will be created by the offer: 2-out-of-2 or 2-out-of-3.</p><p><strong>About</strong></p><p>We have recently added a new type of escrow, and it’s already available for trades, it requires 2 out of 3 keys for making a release. This means, that from now on, in 2 out of 3 contracts, buyers have some control over the funds locked in escrow, since one of the keys belongs to himself, and the other two belong to the seller and Hodl Hodl. Also, 2 out of 2 contracts are still available for trading.</p><p><strong>How does it work?</strong></p><p>In every trade, Hodl Hodl exchange generates a unique multisig escrow cryptocurrency address, where the seller locks the funds, and then buyer sends the payment. Only after seller has received the payment, he releases funds from escrow directly to the buyers wallet.</p><p>In a regular 2 out of 3 contract, where everything goes well, buyer’s key is not needed — it only comes into play if the contract was disputed, and Hodl Hodl administrator resolved it in favor of buyer. In this case, buyer is able to sign a release transaction with his key and receive the funds without seller’s participation. This is how the 2 out of 3 contract type works.</p><p><strong>Differences between these types of contracts</strong></p><p>Now, you may have a question: what does the term “contract type” mean? Contract type indicates how many keys are needed to make a release from escrow:</p><ul><li>Contract type “2 out of 2”: this means, that in contracts of this type, 2 keys are needed for a release, and in total there are 2 keys to escrow: one belongs to the seller, and one to Hodl Hodl.</li><li>Contract type “2 out of 3”: this means, that in contracts of this type, 2 keys are needed for a release, and in total there are 3 keys to escrow: one belongs to the seller, one to the buyer, and one to Hodl Hodl.</li></ul><p>During the offer creation process, every user specifies what type of contract will be created with this specific offer. Once the offer is created, the contract type will be shown in the offer list for other users.</p><p>In case you already had offer(-s) before today, these contract types will remain 2 out of 2, so if you want, you can now edit your offer(-s).</p><p><strong>Reach us via</strong></p><p>Hodl Hodl exchange: <a href="https://hodlhodl.com/">hodlhodl.com</a></p><p>TESTNET Hodl Hodl exchange: <a href="https://testnet.hodlhodl.com/">testnet.hodlhodl.com</a></p><p>E-mail: <a href="mailto:support@hodlhodl.com">support@hodlhodl.com</a></p><p>Blog: <a href="https://medium.com/@hodlhodl">https://medium.com/@hodlhodl</a></p><p>Twitter: <a href="https://twitter.com/hodlhodl">https://twitter.com/hodlhodl</a></p><p>Telegram: <a href="https://t.me/HodlHodl">https://t.me/HodlHodl</a></p><p>Reddit: <a href="https://www.reddit.com/r/hodlhodl">https://www.reddit.com/r/hodlhodl</a></p><p>Slack: <a href="https://goo.gl/zaMnCn">https://goo.gl/zaMnCn</a></p><p>Facebook: <a href="https://www.facebook.com/hodlexchange">https://www.facebook.com/hodlexchange</a></p><p>YouTube: <a href="https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg">https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg</a></p><p><strong>Hodl!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b2110580e036" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Introducing a new feature: Chat attachments]]></title>
|
||||
<link>https://medium.com/@hodlhodl/introducing-a-new-feature-chat-attachments-bb0d749b4381?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/bb0d749b4381</guid>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<category><![CDATA[cryptocurrency]]></category>
|
||||
<category><![CDATA[exchange]]></category>
|
||||
<category><![CDATA[litecoin]]></category>
|
||||
<category><![CDATA[p2p]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Fri, 05 Oct 2018 14:53:27 GMT</pubDate>
|
||||
<atom:updated>2018-10-05T14:58:45.675Z</atom:updated>
|
||||
<content:encoded><![CDATA[<p><strong>Introducing a new feature: Chat attachments</strong></p><p>Dear all,</p><p>Today we are introducing a new feature at Hodl Hodl — chat attachments.</p><p><strong>Quick summary</strong></p><p>After receiving requests from our users, we’ve decided to add a new feature. At first glance, it seems to be a minor one, but it is crucial for trading cryptocurrencies via a P2P platform — to allow sending files in the contract’s chat window. Chat attachments would be very beneficial in case of dispute, users will have an opportunity to exchange information quick, which will reduce the time of solving the dispute.</p><p><strong>Description</strong></p><p>Each user now has the “paper clip” icon in the contract chat window, by clicking it, you can now send files to the chat.</p><p>An important feature to consider is that you may send an attachment to the administrator only, meaning your counterparty cannot see it — you may do so by leaving the checkbox “Show attachment to counterparty” blank.</p><p>Conversely, you must check the box to send an attachment to your counterparty.</p><p><strong>Reach</strong> <strong>us</strong> <strong>via</strong></p><p>Hodl Hodl exchange: hodlhodl.com</p><p>TESTNET Hodl Hodl exchange: testnet.hodlhodl.com</p><p>E-mail: support@hodlhodl.com</p><p>Blog: https://medium.com/@hodlhodl</p><p>Twitter: https://twitter.com/hodlhodl</p><p>Telegram: https://t.me/HodlHodl</p><p>Reddit: https://www.reddit.com/r/hodlhodl</p><p>Slack: https://goo.gl/zaMnCn</p><p>Facebook: https://www.facebook.com/hodlexchange</p><p>YouTube: https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg</p><p><strong>Hodl!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bb0d749b4381" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Hodl Hodl’s upcoming features]]></title>
|
||||
<link>https://medium.com/@hodlhodl/hodl-hodls-upcoming-features-854248d843a5?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/854248d843a5</guid>
|
||||
<category><![CDATA[exchange]]></category>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<category><![CDATA[cryptocurrency]]></category>
|
||||
<category><![CDATA[litecoin]]></category>
|
||||
<category><![CDATA[p2p]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Fri, 28 Sep 2018 13:59:06 GMT</pubDate>
|
||||
<atom:updated>2018-09-28T13:59:06.252Z</atom:updated>
|
||||
<content:encoded><![CDATA[<style>body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}</style><blockquote class="twitter-tweet" data-conversation="none" data-align="center" data-dnt="true"><p>Hodl Hodl's upcoming features</p><p> — <a href="https://twitter.com/hodlhodl/status/1045673927614320640">@hodlhodl</a></p></blockquote><script src="//platform.twitter.com/widgets.js" charset="utf-8"></script><script>function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});</script><script>if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}</script><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=854248d843a5" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Highlights of Baltic Honeybadger 2018]]></title>
|
||||
<link>https://medium.com/@hodlhodl/highlights-of-baltic-honeybadger-2018-d920a791d0ab?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/d920a791d0ab</guid>
|
||||
<category><![CDATA[bh2018]]></category>
|
||||
<category><![CDATA[baltics]]></category>
|
||||
<category><![CDATA[2018]]></category>
|
||||
<category><![CDATA[honey-badger]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Wed, 26 Sep 2018 14:05:32 GMT</pubDate>
|
||||
<atom:updated>2018-09-26T14:05:32.621Z</atom:updated>
|
||||
<content:encoded><![CDATA[<style>body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}</style><blockquote class="twitter-tweet" data-conversation="none" data-align="center" data-dnt="true"><p>Highlights of Baltic Honeybadger 2018</p><p> — <a href="https://twitter.com/hodlhodl/status/1044949899048091649">@hodlhodl</a></p></blockquote><script src="//platform.twitter.com/widgets.js" charset="utf-8"></script><script>function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});</script><script>if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}</script><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d920a791d0ab" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Baltic Honeybadger 2018]]></title>
|
||||
<link>https://medium.com/@hodlhodl/baltic-honeybadger-2018-1c70eaa53d5c?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/1c70eaa53d5c</guid>
|
||||
<category><![CDATA[cryptocurrency]]></category>
|
||||
<category><![CDATA[litecoin]]></category>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<category><![CDATA[exchange]]></category>
|
||||
<category><![CDATA[p2p]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Fri, 21 Sep 2018 13:34:11 GMT</pubDate>
|
||||
<atom:updated>2018-09-21T13:34:54.429Z</atom:updated>
|
||||
<content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/824/1*VcGfuImO6NhF9sgRIVK8XQ.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1c70eaa53d5c" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Introducing a new feature: offer balance]]></title>
|
||||
<link>https://medium.com/@hodlhodl/introducing-a-new-feature-offer-balance-341c9e3ff0e7?source=rss-b1f3d322dadf------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/341c9e3ff0e7</guid>
|
||||
<category><![CDATA[exchange]]></category>
|
||||
<category><![CDATA[litecoin]]></category>
|
||||
<category><![CDATA[p2p]]></category>
|
||||
<category><![CDATA[cryptocurrency]]></category>
|
||||
<category><![CDATA[bitcoin]]></category>
|
||||
<dc:creator><![CDATA[Hodl Hodl]]></dc:creator>
|
||||
<pubDate>Wed, 19 Sep 2018 13:26:44 GMT</pubDate>
|
||||
<atom:updated>2018-09-19T13:29:51.979Z</atom:updated>
|
||||
<content:encoded><![CDATA[<h3>Introducing a new feature: the offer balance</h3><p>Dear all,</p><p>Today we introduce to you a new feature at Hodl Hodl — the offer balance.</p><p><strong>Quick summary</strong></p><p>Some users may have a certain amount of fiat or cryptocurrency that they’re willing to trade at Hodl Hodl, so we decided to add an additional field to the offer creation form — where our customers can specify the total amount they are willing to trade through an offer.</p><p><strong>Description</strong></p><p>When creating an offer, every user is now able to specify the offer balance — the total amount of fiat that the offer creator is willing to trade. This is the max amount that may be purchased through the offer, in terms of fiat, or if it’s a sell offer, in terms of cryptocurrency (in fiat equivalent).</p><p>The balance is reduced by the amount of each completed contract through the offer, and when the remaining balance has been expended, the offer becomes inactive unless the user updates this field.</p><p><strong>Reach us via</strong></p><ul><li>Hodl Hodl exchange: <a href="http://hodlhodl.com/">hodlhodl.com</a></li><li>TESTNET Hodl Hodl exchange: <a href="http://testnet.hodlhodl.com/">testnet.hodlhodl.com</a></li><li>E-mail: support@hodlhodl.com</li><li>Blog: <a href="https://medium.com/@hodlhodl">https://medium.com/@hodlhodl</a></li><li>Twitter: <a href="https://twitter.com/hodlhodl">https://twitter.com/hodlhodl</a></li><li>Telegram: <a href="https://t.me/HodlHodl">https://t.me/HodlHodl</a></li><li>Reddit: <a href="https://www.reddit.com/r/hodlhodl/">https://www.reddit.com/r/hodlhodl</a></li><li>Slack: <a href="https://goo.gl/zaMnCn">https://goo.gl/zaMnCn</a></li><li>Facebook: <a href="https://www.facebook.com/hodlexchange/">https://www.facebook.com/hodlexchange</a></li><li>YouTube: <a href="https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg">https://www.youtube.com/channel/UCgujEoZqX_FfDTLb3Uuhsdg</a></li></ul><p><strong>Hodl!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=341c9e3ff0e7" width="1" height="1">]]></content:encoded>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
1
Parser/Tests/ParserTests/Resources/4fsodonline.atom
Normal file
1
Parser/Tests/ParserTests/Resources/4fsodonline.atom
Normal file
File diff suppressed because one or more lines are too long
1821
Parser/Tests/ParserTests/Resources/DaringFireball.atom
Normal file
1821
Parser/Tests/ParserTests/Resources/DaringFireball.atom
Normal file
File diff suppressed because it is too large
Load Diff
1341
Parser/Tests/ParserTests/Resources/DaringFireball.html
Executable file
1341
Parser/Tests/ParserTests/Resources/DaringFireball.html
Executable file
File diff suppressed because it is too large
Load Diff
584
Parser/Tests/ParserTests/Resources/DaringFireball.json
Normal file
584
Parser/Tests/ParserTests/Resources/DaringFireball.json
Normal file
File diff suppressed because one or more lines are too long
2278
Parser/Tests/ParserTests/Resources/DaringFireball.rss
Executable file
2278
Parser/Tests/ParserTests/Resources/DaringFireball.rss
Executable file
File diff suppressed because it is too large
Load Diff
97
Parser/Tests/ParserTests/Resources/EMarley.rss
Executable file
97
Parser/Tests/ParserTests/Resources/EMarley.rss
Executable file
@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
|
||||
<channel>
|
||||
<title><![CDATA[Stories by Liz Marley on Medium]]></title>
|
||||
<description><![CDATA[Stories by Liz Marley on Medium]]></description>
|
||||
<link>https://medium.com/@emarley?source=rss-b4981c59ffa5------2</link>
|
||||
<image>
|
||||
<url>https://d262ilb51hltx0.cloudfront.net/fit/c/150/150/0*I9s5OlzJw_En0NzC.jpg</url>
|
||||
<title>Stories by Liz Marley on Medium</title>
|
||||
<link>https://medium.com/@emarley?source=rss-b4981c59ffa5------2</link>
|
||||
</image>
|
||||
<generator>Medium</generator>
|
||||
<lastBuildDate>Sun, 28 Aug 2016 17:27:51 GMT</lastBuildDate>
|
||||
<atom:link href="https://medium.com/feed/@emarley" rel="self" type="application/rss+xml"/>
|
||||
<webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
|
||||
<atom:link href="http://medium.superfeedr.com" rel="hub"/>
|
||||
<item>
|
||||
<title><![CDATA[UI Automation & screenshots]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-snippet">Here’s a partial collection of links from my talk today…</p><p class="medium-feed-link"><a href="https://medium.com/@emarley/ui-automation-screenshots-c44a41af38d1?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/ui-automation-screenshots-c44a41af38d1?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/c44a41af38d1</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Sat, 07 May 2016 23:53:30 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[They didn’t.]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-snippet">“The [software developer tool] team clearly doesn’t use [that tool] themselves.”</p><p class="medium-feed-link"><a href="https://medium.com/@emarley/they-didn-t-3a4dab489f45?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/they-didn-t-3a4dab489f45?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/3a4dab489f45</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Sat, 09 Jan 2016 15:29:25 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Side quest: Drawing]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@emarley/side-quest-drawing-b959ded1a1a4?source=rss-b4981c59ffa5------2"><img src="https://d262ilb51hltx0.cloudfront.net/max/700/1*9TewpOfYBlH8kDIbZmlWDA.jpeg" width="700"></a></p><p class="medium-feed-link"><a href="https://medium.com/@emarley/side-quest-drawing-b959ded1a1a4?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/side-quest-drawing-b959ded1a1a4?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/b959ded1a1a4</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Wed, 09 Dec 2015 03:37:35 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[And if I somehow lose the iPad Pro, I can find that with Find My iPhone.]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-link"><a href="https://medium.com/@emarley/and-if-i-somehow-lose-the-ipad-pro-i-can-find-that-with-find-my-iphone-e9aa43486521?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/and-if-i-somehow-lose-the-ipad-pro-i-can-find-that-with-find-my-iphone-e9aa43486521?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/e9aa43486521</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Mon, 23 Nov 2015 19:38:20 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Though not as much more weight as you might expect.]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-link"><a href="https://medium.com/@emarley/though-not-as-much-more-weight-as-you-might-expect-7b33fe989f6e?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/though-not-as-much-more-weight-as-you-might-expect-7b33fe989f6e?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/7b33fe989f6e</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Mon, 23 Nov 2015 19:37:38 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[I avoided art classes in high school and college because I was afraid they would hurt my GPA.]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-link"><a href="https://medium.com/@emarley/i-avoided-art-classes-in-high-school-and-college-because-i-was-afraid-they-would-hurt-my-gpa-ab916601f2ad?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/i-avoided-art-classes-in-high-school-and-college-because-i-was-afraid-they-would-hurt-my-gpa-ab916601f2ad?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/ab916601f2ad</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Mon, 23 Nov 2015 19:37:13 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Finding Value]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-snippet">I lose things a lot. Sometimes they’re just misplaced, sometimes gone forever. I don’t know if I have ever run out of ink in a pen—there’s…</p><p class="medium-feed-link"><a href="https://medium.com/@emarley/finding-value-20a90bf5ebf?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/finding-value-20a90bf5ebf?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/20a90bf5ebf</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Mon, 23 Nov 2015 19:34:18 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Replaying this post in my head last night, I regret this word.]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-snippet">Keyboard shortcuts, and other little details may be programmatically simple to set up, but they are still an important part of an app’s…</p><p class="medium-feed-link"><a href="https://medium.com/@emarley/replaying-this-post-in-my-head-last-night-i-regret-this-word-d8ed0b43f0f9?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/replaying-this-post-in-my-head-last-night-i-regret-this-word-d8ed0b43f0f9?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/d8ed0b43f0f9</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Tue, 10 Nov 2015 18:08:19 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[Betterment]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-snippet">I moved from Senior Test Pilot to Software Engineer last month.</p><p class="medium-feed-link"><a href="https://medium.com/@emarley/betterment-e0ef45fcd284?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/betterment-e0ef45fcd284?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/e0ef45fcd284</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Tue, 10 Nov 2015 02:17:46 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title><![CDATA[This is a test.]]></title>
|
||||
<description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@emarley/this-is-a-test-6ab141a1c5b5?source=rss-b4981c59ffa5------2"><img src="https://d262ilb51hltx0.cloudfront.net/max/2000/1*aTkSAe_3uz1xVtnszIL-Og.jpeg" width="6528"></a></p><p class="medium-feed-snippet">This is only a test.</p><p class="medium-feed-link"><a href="https://medium.com/@emarley/this-is-a-test-6ab141a1c5b5?source=rss-b4981c59ffa5------2">Continue reading on »</a></p></div>]]></description>
|
||||
<link>https://medium.com/@emarley/this-is-a-test-6ab141a1c5b5?source=rss-b4981c59ffa5------2</link>
|
||||
<guid isPermaLink="false">https://medium.com/p/6ab141a1c5b5</guid>
|
||||
<dc:creator><![CDATA[Liz Marley]]></dc:creator>
|
||||
<pubDate>Sun, 20 Sep 2015 07:00:44 GMT</pubDate>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
352
Parser/Tests/ParserTests/Resources/KatieFloyd.rss
Executable file
352
Parser/Tests/ParserTests/Resources/KatieFloyd.rss
Executable file
File diff suppressed because one or more lines are too long
673
Parser/Tests/ParserTests/Resources/OneFootTsunami.atom
Executable file
673
Parser/Tests/ParserTests/Resources/OneFootTsunami.atom
Executable file
@ -0,0 +1,673 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/css" href="http://onefoottsunami.com/wordpress/wp-content/themes/OFTtheme/style-rss.css"?><feed
|
||||
xmlns="http://www.w3.org/2005/Atom"
|
||||
xmlns:thr="http://purl.org/syndication/thread/1.0"
|
||||
xml:lang=""
|
||||
xml:base="http://onefoottsunami.com/wp-atom.php"
|
||||
>
|
||||
<title type="text">One Foot Tsunami</title>
|
||||
<subtitle type="text">Slightly less disappointing than it sounds</subtitle>
|
||||
|
||||
<updated>2015-09-08T14:21:41Z</updated>
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://onefoottsunami.com" />
|
||||
<id>http://onefoottsunami.com/feed/atom/</id>
|
||||
<link rel="self" type="application/atom+xml" href="http://onefoottsunami.com/feed/atom/" />
|
||||
|
||||
<generator uri="http://wordpress.org/" version="4.3">WordPress</generator>
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Pillow Fight Leaves 24 Concussed]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.nytimes.com/2015/09/05/us/at-west-point-annual-pillow-fight-becomes-weaponized.html?mwrsm=Email&_r=1&pagewanted=all" />
|
||||
<id>http://onefoottsunami.com/?p=14863</id>
|
||||
<updated>2015-09-07T18:14:11Z</updated>
|
||||
<published>2015-09-08T14:21:41Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/09/08/pillow-fight-leaves-24-concussed/"><![CDATA[<p>At West Point, freshman cadets have long had an annual massive nighttime pillow fight to build esprit de corps. This year, it <a href="http://www.nytimes.com/2015/09/05/us/at-west-point-annual-pillow-fight-becomes-weaponized.html?mwrsm=Email&_r=1&pagewanted=all">turned violent</a>.</p><br><a href="http://onefoottsunami.com/2015/09/08/pillow-fight-leaves-24-concussed/" title="Permanent Link to 'Pillow Fight Leaves 24 Concussed' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html"><![CDATA[Perverse Incentives]]></title>
|
||||
<link rel="alternate" type="text/html" href="http://onefoottsunami.com/2015/09/07/perverse-incentives-2/" />
|
||||
<id>http://onefoottsunami.com/?p=14861</id>
|
||||
<updated>2015-09-07T12:21:22Z</updated>
|
||||
<published>2015-09-07T12:21:01Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/09/07/perverse-incentives-2/"><![CDATA[<p>In China and Taiwan, drivers who’ve hit someone with their car may attempt to <em>kill</em> the person. Why?</p>
|
||||
|
||||
<blockquote><p>[I]f you cripple a man, you pay for the injured person’s care for a lifetime. But if you kill the person, you “only have to pay once, like a burial fee.”</p></blockquote>
|
||||
|
||||
<p>Because the legal system has often failed to prosecute these murders, <a href="http://www.slate.com/articles/news_and_politics/foreigners/2015/09/why_drivers_in_china_intentionally_kill_the_pedestrians_they_hit_china_s.html">a perverse incentive has been created</a>. Once a driver hits an individual, the financially prudent move is for him to kill the injured party, rather than allowing them to live with a severe injury.</p>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Space Jam Forever]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.rollingstone.com/sports/features/space-jam-forever-the-website-that-wouldnt-die-20150819?print=true" />
|
||||
<id>http://onefoottsunami.com/?p=14858</id>
|
||||
<updated>2015-09-04T14:31:25Z</updated>
|
||||
<published>2015-09-04T14:31:31Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/09/04/space-jam-forever/"><![CDATA[<p>In 2010, a user on Reddit <a href="https://www.reddit.com/r/todayilearned/comments/esxwd/til_that_the_space_jam_website_is_still_up_and/">discovered</a> that the website for the 1996 movie “Space Jam” was inexplicably still online. Almost 5 years later, and nearly 20 years after the film was released, that’s still true. It’s <a href="http://www.warnerbros.com/archive/spacejam/movie/jam.htm">a wonder to behold</a>. Now, Rolling Stone has done a wonderful <a href="http://www.rollingstone.com/sports/features/space-jam-forever-the-website-that-wouldnt-die-20150819?print=true">archaeological dig</a> on a piece of the ancient Internet, well preserved.</p><br><a href="http://onefoottsunami.com/2015/09/04/space-jam-forever/" title="Permanent Link to 'Space Jam Forever' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html"><![CDATA[Head of the Charles]]></title>
|
||||
<link rel="alternate" type="text/html" href="http://onefoottsunami.com/2015/09/03/head-of-the-charles/" />
|
||||
<id>http://onefoottsunami.com/?p=14854</id>
|
||||
<updated>2015-09-03T03:34:24Z</updated>
|
||||
<published>2015-09-03T14:32:12Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/09/03/head-of-the-charles/"><![CDATA[<p>As America grew in the 1800 and 1900s, many of our waterways became incredibly polluted. Cleveland’s Cuyahoga River actually managed to <a href="http://clevelandhistorical.org/items/show/63">catch fire</a> on multiple occasions. Likewise, Boston’s Charles River was famously polluted by both sewage runoff and industrial wastewater. That impression of a foul waterway is now deeply engrained in the minds of locals, reinforced constantly by the oft-heard classic Standells’ song “Dirty Water”.</p>
|
||||
|
||||
<p>However, Herculean efforts made since 1995 have improved the river’s quality. Recently folks have even begun <a href="http://www.thecharles.org/projects-and-programs/swimmable-charles/">swimming in the Charles again</a>, at least when water quality permits it. The EPA has tracked the steady improvement, now rating the Charles as one of cleanest urban rivers in America.</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150903charlesriver/swamthecharles.jpg" style="width: 500px; height: 500px;" alt="'I Swam The Charles' Bumper Sticker" />
|
||||
<br />[<span style="font-size: smaller;">Photo courtesy of <a href="https://instagram.com/p/ww6aqwtjeZ/">P. Kafasis</a></span>]</p>
|
||||
|
||||
<p>Still, does <em>anyone</em> actually want to drink water from the Charles River? Boston-based beermaker Harpoon aims to find out. Their new “Charles River Pale Ale” contains a not-so-secret ingredient: <a href="http://www.bostonglobe.com/metro/2015/09/01/the-secret-ingredient-harpoon-brewery-new-beer-charles-river-water/qsGtGU3IMTwI3yXSUo9xkM/story.html">300 gallons of Charles River water</a>. While locals are likely gagging and reflexively spitting at the very thought, Harpoon is assuring the public the suds will be both safe and delicious.<p>
|
||||
|
||||
|
||||
<p>In fact, the water was treated by Desalitech, a local water desalination company, purified to make it ideal for drinking. So taking a little taste of the Charles, at least in this form, probably won’t kill you. And hey, if/when you survive the experience, you can wear a shirt in this vein:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150903charlesriver/atetheworm.jpg" style="width: 600px; height: 450px;" alt="Milton from Office Space in a shirt reading 'I Ate The Worm!!'" />
|
||||
<br />I Drank the River!!</p>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[No More Sense Than an Amish Bus Driver]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.freep.com/story/opinion/contributors/2015/09/01/s-time-remove-kentucky-clerk-kim-davis/71505026/" />
|
||||
<id>http://onefoottsunami.com/?p=14852</id>
|
||||
<updated>2015-09-02T17:32:10Z</updated>
|
||||
<published>2015-09-02T17:31:10Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/09/02/no-more-sense-than-an-amish-bus-driver/"><![CDATA[<p>Back in June, supporters of marriage equality <a href="http://onefoottsunami.com/2015/06/29/one-and-the-same/">declared victory in America</a> with the Supreme Court’s ruling on <em>Obergefell v. Hodges</em>. Of late, Kentucky county clerk Kim Davis has been making news by defying the authority of the highest courts in the land, refusing to issue any marriage licenses whatsoever. Surprise surprise, Ms. Davis is a tremendous hypocrite, as <a href="http://www.usnews.com/news/articles/2015/09/01/kentucky-clerk-fighting-gay-marriage-has-wed-four-times">US News reports</a>:</p>
|
||||
|
||||
<blockquote><p>The marriages are documented in court records obtained by U.S. News, which show that Rowan County Clerk Kim Davis divorced three times, first in 1994, then 2006 and again in 2008.</p>
|
||||
|
||||
<p>She gave birth to twins five months after divorcing her first husband. They were fathered by her third husband but adopted by her second. Davis worked at the clerk’s office at the time of each divorce and has since remarried.</p></blockquote>
|
||||
|
||||
<p>If Ms. Davis doesn’t wish to do her job, she should resign or expect to be removed, as John Corvino <a href="http://www.freep.com/story/opinion/contributors/2015/09/01/s-time-remove-kentucky-clerk-kim-davis/71505026/">ably discusses</a>:</p>
|
||||
|
||||
<blockquote><p>If [Davis’s] conscience renders her unable to issue marriage licenses to those legally qualified, then the right thing for her to do is resign. After all, issuing marriage licenses is not a peripheral, non-essential part of being county clerk — it’s a central job function. Her current stance makes no more sense than that of an Amish person who expects to retain a job as a bus driver.</p></blockquote>
|
||||
|
||||
<p>Here’s hoping this odious woman is found in contempt of court tomorrow, and quickly removed from her job, or at least from the spotlight.</p><br><a href="http://onefoottsunami.com/2015/09/02/no-more-sense-than-an-amish-bus-driver/" title="Permanent Link to 'No More Sense Than an Amish Bus Driver' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Self-Driving Cars vs. Fixed-Gear Bikes]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.washingtonpost.com/news/innovations/wp/2015/08/26/how-fixed-gear-bikes-can-confuse-googles-self-driving-cars/?postshare=921440689194781" />
|
||||
<id>http://onefoottsunami.com/?p=14850</id>
|
||||
<updated>2015-09-01T16:22:03Z</updated>
|
||||
<published>2015-09-01T16:22:00Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/09/01/self-driving-cars-vs-fixed-gear-bikes/"><![CDATA[<p>When a rider on a fixed-gear bicycle arrived at an intersection with a Google self-driving car, the car <a href="http://www.washingtonpost.com/news/innovations/wp/2015/08/26/how-fixed-gear-bikes-can-confuse-googles-self-driving-cars/?postshare=921440689194781">didn’t quite know what to do</a>.</p><br><a href="http://onefoottsunami.com/2015/09/01/self-driving-cars-vs-fixed-gear-bikes/" title="Permanent Link to 'Self-Driving Cars vs. Fixed-Gear Bikes' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[That’s Not What Dolphins Do]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="" />
|
||||
<id>http://onefoottsunami.com/?p=14848</id>
|
||||
<updated>2015-08-31T15:38:01Z</updated>
|
||||
<published>2015-08-31T15:37:47Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/31/thats-not-what-dolphins-do/"><![CDATA[<p>Surfer Elinor Dempsey didn’t catch any waves when she hit the ocean on Saturday, but she did get <a href="http://www.sanluisobispo.com/2015/08/29/3783230_surfer-unhurt-after-shark-attack.html?rh=1">a pretty good story</a>. As she waited in the water, something approached her from underneath.</p>
|
||||
|
||||
<blockquote><p>“First I thought it was a dolphin and I thought, ‘What the hell is he doing?’ ” she said. “And he kind of landed on my board. Then I realized he had taken a chunk. And I was, like, that’s not what dolphins do.”</p></blockquote>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150831bittenboard.jpg" style="width: 512px; height: 341px;" alt="Shark Bite photo" />
|
||||
<br />Better the board than her hand</p>
|
||||
|
||||
<p><em>That</em> is indeed not what dolphins do.</p><br><a href="http://onefoottsunami.com/2015/08/31/thats-not-what-dolphins-do/" title="Permanent Link to 'That’s Not What Dolphins Do' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[It’s Also a Very High-End Hunting Load for Ducks, Geese, or Turkeys]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="" />
|
||||
<id>http://onefoottsunami.com/?p=14846</id>
|
||||
<updated>2015-08-28T00:41:21Z</updated>
|
||||
<published>2015-08-28T14:41:06Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/28/its-also-a-very-high-end-hunting-load-for-ducks-geese-or-turkeys/"><![CDATA[<p>Well of course company is <a href="http://www.fieldandstream.com/blogs/field-notes/idaho-company-selling-drone-specific-ammunition?LMeW6Yx7lVML6w2j.01">selling ammunition specifically marketed for shooting down drones</a>. Of course they are.</p><br><a href="http://onefoottsunami.com/2015/08/28/its-also-a-very-high-end-hunting-load-for-ducks-geese-or-turkeys/" title="Permanent Link to 'It’s Also a Very High-End Hunting Load for Ducks, Geese, or Turkeys' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Waste Not, Want Not]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="https://thenanfang.com/chinese-female-passenger-drinks-entire-bottle-cognac-airport-security-rather-throw/" />
|
||||
<id>http://onefoottsunami.com/?p=14839</id>
|
||||
<updated>2015-08-27T17:16:50Z</updated>
|
||||
<published>2015-08-27T17:15:34Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/27/waste-not-want-not/"><![CDATA[<p>What do you do when you buy an expensive bottle of cognac but you’re not permitted to take it on your flight? Maybe you throw it out. You might gift it to a stranger. Perhaps you could throw an impromptu party in the airport. You’d be sure to make some new friends. Whatever you do, avoid the path taken by one Ms. Zhao:</p>
|
||||
|
||||
<blockquote><p>[S]he sat down in a corner and drank the entire bottle of cognac herself.</p></blockquote>
|
||||
|
||||
<p>As you’d probably imagine, <a href="https://thenanfang.com/chinese-female-passenger-drinks-entire-bottle-cognac-airport-security-rather-throw/">this did not turn out well</a>.</p><br><a href="http://onefoottsunami.com/2015/08/27/waste-not-want-not/" title="Permanent Link to 'Waste Not, Want Not' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html"><![CDATA[At Least He Can Still Go to the Same Church]]></title>
|
||||
<link rel="alternate" type="text/html" href="http://onefoottsunami.com/2015/08/26/at-least-he-can-still-go-to-the-same-church/" />
|
||||
<id>http://onefoottsunami.com/?p=14836</id>
|
||||
<updated>2015-08-26T15:43:55Z</updated>
|
||||
<published>2015-08-26T14:43:17Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/26/at-least-he-can-still-go-to-the-same-church/"><![CDATA[<p>We open with a montage of newspaper articles, quickly showing JIMMY McGINTY’s criminal arc.
|
||||
|
||||
<p align="center">JIMMY is caught.
|
||||
<br />(“NOTORIOUS MOB KILLER JIMMY McGINTY ARRESTED!”)</p>
|
||||
|
||||
<p align="center">He turns state’s evidence and aids the prosecution.
|
||||
<br />(“McGINTY TESTIFIES AGAINST LEFTY HANNIGAN”)</p>
|
||||
|
||||
<p align="center">The trial brings down the last vestiges of the Boston Mafia.
|
||||
<br />(“LEFTY HANNIGAN SENTENCED TO LIFE”)</p>
|
||||
|
||||
<p align="center">Finally, he disappears.
|
||||
<br />(“WHERE IS JIMMY McGINTY NOW?”)</p>
|
||||
|
||||
<p>CUT TO:</p>
|
||||
|
||||
<p><strong>INT. STEREOTYPICAL ITALIAN RESTAURANT – NIGHT</strong></p>
|
||||
|
||||
<p>Open on the CHEF, a older man with pale, freckled skin and bright red hair noticeably peeking out from under his chef’s hat. He is wearing an apron and a bushy mustache that is quite clearly fake. </p>
|
||||
|
||||
<p>The CHEF approaches—</p>
|
||||
|
||||
<p>A TABLE dressed in a red checkered tablecloth, with a candle lit atop it.</p>
|
||||
|
||||
<p>—Where a lone female CUSTOMER, sits wearing a simple gray dress. She is persuing the menu.</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p style="width: 375px;"><strong>CHEF</strong>
|
||||
<br />(in a ridiculously over-the-top Italian-American accent — think “It’sa me, Mario!”)
|
||||
<br />Buonasera, bella! What-a you like-a to have tonight?</p>
|
||||
|
||||
<p style="width: 375px;"><strong>CUSTOMER</strong>
|
||||
<br />(hesitant)
|
||||
<br />Well, I’m not sure. I thought this was an Italian restaurant…</p>
|
||||
|
||||
<p style="width: 375px;"><strong>CHEF</strong>
|
||||
<br />(with delight)
|
||||
<br />Oh, sì, sì! It is, it is!</p>
|
||||
|
||||
<p style="width: 375px;"><strong>CUSTOMER</strong>
|
||||
<br />(still hesitant)
|
||||
<br />But I don’t recognize any of these dishes. “Black pudding”? “Limerick Ham”? “Corned Beef and Cabbage”, now that’s an <em>Irish</em> dish!</p>
|
||||
|
||||
<p style="width: 375px;"><strong>CHEF</strong>
|
||||
<br />(shaken, slips into a very real Irish-American accent)
|
||||
<br />Ah, no, no, cailín
|
||||
<br />(Quickly recovering his over-the-top Italian-American accent)
|
||||
<br />Err, we Italians have-a that as well! But I-a tell you what. I’m-a gonna make you the specialty of the house! You-a trust me, no? After all, it’sa me, Mario! I own-a this place!</p>
|
||||
|
||||
<p style="width: 375px;"><strong>CUSTOMER</strong>
|
||||
<br />(relieved)
|
||||
<br />Well, alright. That sounds lovely. Thank you, Mario!</p>
|
||||
</div>
|
||||
|
||||
<p>MARIO walks quickly to the back, through the inward swinging right kitchen door, then immediately back out the outward swinging left kitchen door with a tray he carries with two hands.</p>
|
||||
|
||||
<p>On the tray is a plate which appears to contain a large tortilla covered in chunky tomato soup, with grated orange American cheddar cheese cooked on top. It is a comically poor imitation of Italy’s most famous dish.</p>
|
||||
|
||||
<p>MARIO places the tray on the CUSTOMER’S table with a flourish.</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p style="width: 375px;"><strong>CHEF</strong>
|
||||
<br />Buon appetito!</p>
|
||||
|
||||
<p style="width: 375px;"><strong>CUSTOMER</strong>
|
||||
<br />(Staring at the plate, extremely hesitant)
|
||||
<br />Uh…
|
||||
<br />(Now staring intently at “MARIO”, noticing his red hair and fake mustache)
|
||||
<br />What do you call this dish, “Mario”?</p>
|
||||
|
||||
<p style="width: 375px;"><strong>MARIO</strong>
|
||||
<br />(a ridiculously over-the-top Italian-American accent)
|
||||
<br />Ah, you are not-a the first person to ask! In fact, a-so many a-people ask, I name-a the restaurant after a-my reply!</p>
|
||||
|
||||
<p><strong>PULL BACK TO RESTAURANT EXTERIOR, REVEALING THIS SIGN</strong></p>
|
||||
</div>
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150826thatsapizza@2x.jpg" srcset="http://onefoottsunami.com/wordpress/wp-content/uploads/20150826thatsapizza.jpg 1x, http://onefoottsunami.com/wordpress/wp-content/uploads/20150826thatsapizza@2x.jpg 2x" style="width: 560px; height: 375px" alt="That'sa Pizza!"></p>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Tomato Seasoning]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://time.com/4006235/israel-heinz-ketchup/" />
|
||||
<id>http://onefoottsunami.com/?p=14832</id>
|
||||
<updated>2015-08-25T04:38:31Z</updated>
|
||||
<published>2015-08-25T13:38:17Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/25/tomato-seasoning/"><![CDATA[<p>Over in Israel, Heinz is <a href="http://www.haaretz.com/news/israel/1.671800">no longer allowed to sell its most popular condiment as “ketchup”</a>.</p>
|
||||
|
||||
<blockquote><p>Heinz will no longer be allowed to label its red sauce as “ketchup” in Hebrew in Israel after local food manufacturer Osem successfully argued that its competitor’s product doesn’t meet the definition of Israel’s standards institute, Israeli news site Ynet reported.</p></blockquote>
|
||||
|
||||
<p>Instead, Heinz must use the ridiculous euphemism “tomato seasoning” to label their product. Then again, given that “tomato seasoning” sounds like the classy way a fancy restaurant would refer to the side they bring with their “pomme frites”, I’m not sure how much of a punishment this really is.</p><br><a href="http://onefoottsunami.com/2015/08/25/tomato-seasoning/" title="Permanent Link to 'Tomato Seasoning' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[“I Love the Thing That I Most Wish Had Not Happened”]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.gq.com/story/stephen-colbert-gq-cover-story?currentPage=all" />
|
||||
<id>http://onefoottsunami.com/?p=14829</id>
|
||||
<updated>2015-08-24T07:23:02Z</updated>
|
||||
<published>2015-08-24T13:40:35Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/24/i-love-the-thing-that-i-most-wish-had-not-happened/"><![CDATA[<p>GQ has <a href="http://www.gq.com/story/stephen-colbert-gq-cover-story">a rather wonderful piece</a> on the once and future Stephen Colbert, discussing the future of late night, a past full of loss, and being present in the present.</p><br><a href="http://onefoottsunami.com/2015/08/24/i-love-the-thing-that-i-most-wish-had-not-happened/" title="Permanent Link to '“I Love the Thing That I Most Wish Had Not Happened”' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[The UK’s Most Disappointing New Visitor Attraction]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.thisiscolossal.com/2015/08/dismaland/" />
|
||||
<id>http://onefoottsunami.com/?p=14825</id>
|
||||
<updated>2015-08-21T14:26:07Z</updated>
|
||||
<published>2015-08-21T13:51:54Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/21/the-uks-most-disappointing-new-visitor-attraction/"><![CDATA[<p>I’ve always found guerrilla artist Banksy interesting enough, particularly when <a href="http://onefoottsunami.com/2013/11/01/genuine-fake-banksys/">selling his own pieces as fakes</a>. However, his new creation is really something else. Over in England, he’s created a dystopian theme park named <a href="http://www.dismaland.co.uk/">Dismaland</a>, and it’ll be open to the public for the next month. Christopher Jobson <a href="http://www.thisiscolossal.com/2015/08/dismaland/">reports in detail</a>:</p>
|
||||
|
||||
<blockquote><p>The event has all the hallmark details of a traditional Banksy event from a shroud of ultimate secrecy (the event area was plastered in notices designating it as filming location for a movie titled Gray Fox) to general themes of apocalypse, anti-consumerism, and anti-corporate messages. However there’s one major deviation: the emphasis of Dismalanded is largely on other artists’ work instead of Banksy himself.</p></blockquote>
|
||||
|
||||
<p>This trippy piece alone is enough to make me jealous of those who can go:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150821dismaland.jpg" style="width: 500px; height: 500px;" alt="Ariel, Sort Of" />
|
||||
<br />Do not adjust your eyeballs.</p>
|
||||
|
||||
<p>I have to imagine Disney’s lawyers will be all over this whole thing. Heck, the park even bans them (“The following items are strictly prohibited: knives, spray cans, illegal drugs, and lawyers from the Walt Disney corporation.”). If you have a chance to go, it definitely seems worth it.</p><br><a href="http://onefoottsunami.com/2015/08/21/the-uks-most-disappointing-new-visitor-attraction/" title="Permanent Link to 'The UK’s Most Disappointing New Visitor Attraction' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html"><![CDATA[Pretty Damned Good for Around a Thousand Pixels]]></title>
|
||||
<link rel="alternate" type="text/html" href="http://onefoottsunami.com/2015/08/20/pretty-damned-good-for-around-a-thousand-pixels/" />
|
||||
<id>http://onefoottsunami.com/?p=14814</id>
|
||||
<updated>2015-08-21T03:21:12Z</updated>
|
||||
<published>2015-08-20T13:43:08Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/20/pretty-damned-good-for-around-a-thousand-pixels/"><![CDATA[<p>Back in October, I wrote about <a href="https://cash.me/app/SDMRNHB">Square Cash</a>, my favorite service for both exchanging money with friends as well as being amused by <a href="http://onefoottsunami.com/2014/10/03/square-cash-and-the-worlds-worst-negotiator/">the imagined negotiating process of an complete moron</a>. Last year’s post provided me with a brief trickle of $1 referral bonuses, netting me something like $18, so naturally I’ve been itching for another chance to write about <a href="https://cash.me/app/SDMRNHB">Square Cash</a>. Square has since upped their referral bonus to $5 for both sides, so, ya know: <a href="http://cash.me/app/SDMRNHB">Sign up for Square Cash</a> and get yourself a Lincoln.<sup id="fnr1-20150820tubmancash"><a href="#fn1-20150820tubmancash">1</a></sup></p>
|
||||
|
||||
<p>Anyhow, the <a href="https://cash.me/app/SDMRNHB">Square Cash</a> iPhone app was recently updated to include support for the Apple Watch. Our glorious future truly has arrived, because it’s now possible to send money to your friends right from your wrist. Open the app and tap your desired recipient to see a screen like this:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150820tubmancash/applewatch.png" style="width: 312px; height: 390px;" alt="The Apple Watch Square Cash app" /><br />“Select Amounts” is kind of a weird instruction.</p>
|
||||
|
||||
<p>To send cash, you tap the relevant bills to add up to the desired whole number (no change!) you wish to send, then tap “Pay”. Within seconds, and without any further verification or chance of cancelling, your money will be flying off to someone else’s bank account. As your money wings away, there’s even a ridiculous animated image of dollar bills fluttering down.</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150820tubmancash/justpaid.png" style="width: 312px; height: 390px;" alt="The Apple Watch Square Cash app" /><br />No matter how long you stare at this image, it won’t move, because it’s just a still. Feel free to cash $PBones to see the full animated version though.</p>
|
||||
|
||||
<p>I’ve previously written that <a href="http://onefoottsunami.com/2015/05/21/you-do-not-have-to-make-a-watch-app/">you do not have to make an Apple Watch app</a>. However, good third-party apps for the watch are certainly possible.<sup id="fnr2-20150820tubmancash"><a href="#fn2-20150820tubmancash">2</a></sup> The Square Cash watch app is definitely well made, and it offers functionality I’ll describe as at least potentially useful, which means it’s better than most Apple Watch apps to date. Perhaps the best thing the Square Cash Apple Watch app does, however, is advance the cause of gender equality.</p>
|
||||
|
||||
<p>Allow me to back up slightly. You may have seen a recent push to put Harriet Tubman on America’s $20 bill, fully replacing Andrew Jackson. Hey, according to <a href="http://www.nytimes.com/2015/07/05/opinion/sunday/take-jackson-off-the-20-bill-put-a-woman-in-his-place.html?pagewanted=all">this article</a>, Old Hickory might not have minded the change:</p>
|
||||
|
||||
<blockquote><p>[Jackson] also hated paper currency and vetoed the reauthorization of the Second Bank of the United States, a predecessor of the Federal Reserve.</p></blockquote>
|
||||
|
||||
<p>This proposed change has also led to other women being considered for placement on American currency, and it appears that the next re-design of the $10 bill will at least provide Alexander Hamilton <a href="https://thenew10.treasury.gov">with a female co-star</a>. That’s some progress, at least, but the idea of placing Harriet Tubman on the $20 has also laid bare <a href="http://blacksportsonline.com/home/2015/05/facebook-guy-thinks-harriet-tubman-is-rosa-parks/">some incredible stupidity</a>. Take a deep breath and try to absorb this:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150820tubmancash/jimmy.gif" style="width: 450px; height: 411px; border: 1px solid black;" alt="Jimmy's Stupid Comment" /><br />I think it’s the exclamation point that really gets me.</p>
|
||||
|
||||
<p>Oh jeez. I honestly try to avoid highlighting this sort of depressing idiocy. I figure it’s best to let it die quietly in the dark, rather than than live and spread in the light. Some things are just so feebleminded that they must be skewered, however, and this is one of those things. So, how dumb are you, Jimmy Pecoul? Let me count the ways.</p>
|
||||
|
||||
<h4>A list of the ways in which Jimmy Pecoul has shown off his ignorance, in increasing order of stupidity</h4>
|
||||
|
||||
<ul>
|
||||
<li><p><strong>Problem #1: Thinking that only presidents belong on our banknotes</strong></p>
|
||||
|
||||
<p>While this is wrong, I wouldn’t be surprised if a not-insignificant number of people believe this, and think that both Alexander Hamilton (on the $10 bill) and Benjamin Franklin (on the $100 bill) were presidents.</p></li>
|
||||
|
||||
<li><p><strong>Problem #2: Mistaking Harriet Tubman for Rosa Parks</strong></p>
|
||||
|
||||
<p>These are two entirely different woman, who are famous for their work winning progress in different areas (abolitionism for Harriet Tubman and civil rights activism for Rosa Parks), and who were active nearly a full century apart.</p></li>
|
||||
|
||||
<li><p><strong>Problem #3: Having no understanding of what Rosa Parks did</strong></p>
|
||||
|
||||
<p>Rosa Parks did not “stand up to bullies on a bus”. Rosa Parks defied a despicable law and set off the <a href="https://en.wikipedia.org/wiki/Montgomery_Bus_Boycott">Montgomery bus boycott</a>, which helped bring about the end of segregation in America.</p>
|
||||
</li>
|
||||
|
||||
<li><p><strong>Stupidity #4: Thinking he’ll stop using $20 bills</strong></p>
|
||||
|
||||
<p>I like to imagine how this might go. Jimmy would have to avoid just about every ATM in America, for one thing. The interactions with cashiers, waiters, bartenders and the like ought to be something to see as well. I’d give him a week managing to boycott the bill, and that’s being generous.</p>
|
||||
</li>
|
||||
|
||||
<li><p><strong>Stupidity #5: Believing that “most” people will stop using the $20 bill</strong></p>
|
||||
|
||||
<p>I doubt even <em>one</em> person in the entire country would stop using yuppie singles if the picture on them changed from Jackson to Tubman. Most? <em>MOST?</em> Jesus <a href="http://onefoottsunami.com/2015/02/09/jesus-crist/">Crist</a>.</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<p>We’ll just ignore the incredibly foolish acts of posting this publicly to Facebook for the whole world to snigger at, as well as thinking anyone gives a single good goddamn what his “vote” is on this matter, because if we don’t my head might explode. Let’s get back to Harriet Tubman and Square Cash (<a href="https://cash.me/app/SDMRNHB">Square Cash</a>!). Have another look at the buttons for selecting the amount of money you wish to send:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150820tubmancash/buttons.png" style="width: 312px; height: 271px;" alt="The Apple Watch Square Cash app's buttons" /></p>
|
||||
|
||||
<p>Each button features a pretty good portrait of the corresponding man who appears on that denomination’s bill, but Andrew Jackson on the $20 doesn’t look quite right.</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150820tubmancash/tubmanpixels.png" style="width: 34px; height: 43px;" alt="The face on Square Cash's $20 bill" /></p>
|
||||
|
||||
<p>Of course, that’s not Andrew Jackson at all — it’s Harriet Tubman! The image appears to be based on <a href="http://www.nps.gov/media/photo/gallery.htm?id=C8C22F7E-155D-451F-675CD5F8A8A77596">an 1895 portrait of Mrs. Tubman</a> which is part of the collection of America’s National Portrait Gallery. Here’s a side-by-side comparison:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150820tubmancash/sidebyside.jpg" style="width: 64px; height: 35px;" alt="Pixel Tubman and Photo Tubman, side by side" /></p>
|
||||
|
||||
<p>And here it is, blown up:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150820tubmancash/sidebyside-256.jpg" style="width: 256px; height: 140px;" alt="Enlarged Pixel Tubman and Photo Tubman, side by side" />
|
||||
<br />The pixel version has managed to turn that dour frown upside down.</p>
|
||||
|
||||
<p>Not bad! Lest you have any lingering doubt as to the true identity of this image, Square has <a href="http://www.buzzfeed.com/brendanklinkenberg/square-put-harriet-tubmans-picture-on-the-20-bill#.ncwxEzNWBM">confirmed</a> that the image does indeed represent Tubman, with a spokesman stating “We put Harriet Tubman on the $20 bill because she is an American hero”. Well done. It’s a small gesture, but it’s a good one nonetheless.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><strong>Update (August 20th, 2015):</strong> Square’s creative director Robert Anderson used his own accidental invention (the <a href="http://qz.com/135149/the-first-ever-hashtag-reply-and-retweet-as-twitter-users-invented-them/">@-reply</a>) to link me to a <a href="https://twitter.com/rsa/status/634390182775681026">higher resolution version of the Tubman image</a>. He also <a href="https://twitter.com/rsa/status/634391578770079744">confirmed</a> that the 1895 portrait seen above was indeed the inspiration for the cartoon version. Neat!</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150820tubmancash/higherres.png" style="width: 520px; height: 290px;" alt="A higher-res $20 Tubman" />
|
||||
<br />A higher-res Tubman Twenty</p>
|
||||
|
||||
<hr class="footnote" />
|
||||
|
||||
<p class="footnotesheader">Footnotes:</p>
|
||||
<ol class="footnotes">
|
||||
<li id="fn1-20150820tubmancash"><p>The bill, not the McConaughey-endorsed vehicle.
|
||||
<a href="#fnr1-20150820tubmancash" class="footnoteBackLink" title="Jump back to footnote 1 in the text.">↩︎</a></p></li>
|
||||
<li id="fn2-20150820tubmancash"><p>I should note that the Apple Watch app from USAA has been updated since I mocked it in that post. Now, in addition to showing your account balance (and allowing you to refresh that account balance), the app will show any transactions from the last seven days. That’s actually mildly useful!
|
||||
<a href="#fnr2-20150820tubmancash" class="footnoteBackLink" title="Jump back to footnote 2 in the text.">↩︎</a></p></li>
|
||||
</ol>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Early Notes on the Ashley Madison Hack]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.theawl.com/2015/08/notes-on-the-ashley-madison-hack" />
|
||||
<id>http://onefoottsunami.com/?p=14809</id>
|
||||
<updated>2015-08-19T16:13:25Z</updated>
|
||||
<published>2015-08-19T16:13:24Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/19/early-notes-on-the-ashley-madison-hack/"><![CDATA[<p>Over at The Awl, John Herman <a href="http://www.theawl.com/2015/08/notes-on-the-ashley-madison-hack">writes about</a> the Ashley Madison hack. The data from this hack appears to be close to being easily searchable by the public, and while the consequences of that have been joked about, they deserve closer consideration:</p>
|
||||
|
||||
<blockquote><p>I’m not sure anyone is really reckoning with how big this could be, yet. If the data becomes as public and available as seems likely right now, we’re talking about tens of millions of people who will be publicly confronted with choices they thought they made in private…Here were millions of people expecting the highest level of privacy that the commercial web could offer as they conducted business they likely wanted to keep between two people (even if a great number of the emails are junk, or attached to casual gawkers, the leak claims to contain nine million transaction records). This hack could be ruinous—personally, professionally, financially—for them and their families. </p></blockquote>
|
||||
|
||||
<p>While it would be easy to say that the people who used this site deserve whatever happens, the fallout from this hack will affect far more than just the users and the implications for the future are also well-worth considering.</p><br><a href="http://onefoottsunami.com/2015/08/19/early-notes-on-the-ashley-madison-hack/" title="Permanent Link to 'Early Notes on the Ashley Madison Hack' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Unlikely to Be a Viable Alternative]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.thestar.com/news/crime/2015/08/17/toronto-police-exploring-clown-guns-for-officers.html" />
|
||||
<id>http://onefoottsunami.com/?p=14807</id>
|
||||
<updated>2015-08-18T17:11:58Z</updated>
|
||||
<published>2015-08-18T17:11:55Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/18/unlikely-to-be-a-viable-alternative/"><![CDATA[<p>Police departments in Canada and the US are <a href="http://www.thestar.com/news/crime/2015/08/17/toronto-police-exploring-clown-guns-for-officers.html">experimenting</a> with a new, less-than-lethal use-of-force option. A device called “The Alternative” allows an officer to fire a single shot gun which may take down a suspect, without killing them. If the shot fails to take down the suspect, the officer’s gun returns to its normal, lethal state.</p>
|
||||
|
||||
<p>It’s an interesting enough idea, and the physics of the device itself seem practical. However, it’s difficult to imagine officers attaching the device to their service weapon in the middle of a confrontation. Perhaps even less likely is police departments adopting a device nicknamed the “Clown Gun”.</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150818clowngun.jpg" style="width: 600px; height: 490px;" alt="The Clown Gun Explained" />
|
||||
<br />The silver ball was originally bright orange and resembled a clown’s nose.</p><br><a href="http://onefoottsunami.com/2015/08/18/unlikely-to-be-a-viable-alternative/" title="Permanent Link to 'Unlikely to Be a Viable Alternative' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[How to Win Contests and Influence Mexican Soap Stars]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://qz.com/476914/i-built-a-twitter-bot-that-entered-and-won-1000-online-contests-for-me/" />
|
||||
<id>http://onefoottsunami.com/?p=14805</id>
|
||||
<updated>2015-08-17T17:37:20Z</updated>
|
||||
<published>2015-08-17T17:37:14Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/17/how-to-win-contests-and-influence-mexican-soap-stars/"><![CDATA[<p>Hunter Scott <a href="http://qz.com/476914/i-built-a-twitter-bot-that-entered-and-won-1000-online-contests-for-me/">won a whole lot of contests</a> via Twitter, all thanks to a bot.</p>
|
||||
|
||||
<blockquote><p>My favorite thing that I won was a cowboy hat autographed by the stars of a Mexican soap opera that I had never heard of.</p></blockquote>
|
||||
|
||||
<p>Few of the prizes were valuable, but the whole experiment is amusing, and that’s worth something.</p><br><a href="http://onefoottsunami.com/2015/08/17/how-to-win-contests-and-influence-mexican-soap-stars/" title="Permanent Link to 'How to Win Contests and Influence Mexican Soap Stars' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Chicago Falcons]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="" />
|
||||
<id>http://onefoottsunami.com/?p=14803</id>
|
||||
<updated>2015-08-14T04:59:10Z</updated>
|
||||
<published>2015-08-14T13:59:12Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/14/chicago-falcons/"><![CDATA[<p>If you want to see ridiculous good photos of peregrine falcons living on a balcony in Chicago, <a href="https://www.audubon.org/news/peregrines-and-photographer-bunk-out-chicago-mans-apartment">look no further</a>.</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150814peregrine@2x.jpg" srcset="http://onefoottsunami.com/wordpress/wp-content/uploads/20150814peregrine.jpg 1x, http://onefoottsunami.com/wordpress/wp-content/uploads/20150814peregrine@2x.jpg 2x" style="width: 600px; height: 400px" alt="Peregrine Stalking Image">
|
||||
<br />Peregrine Falcon on Patrol</p><br><a href="http://onefoottsunami.com/2015/08/14/chicago-falcons/" title="Permanent Link to 'Chicago Falcons' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[You Reap What You Sow]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://nymag.com/daily/intelligencer/2015/08/fox-news-picked-trump-over-megyn-kelly.html" />
|
||||
<id>http://onefoottsunami.com/?p=14801</id>
|
||||
<updated>2015-08-13T04:52:36Z</updated>
|
||||
<published>2015-08-13T13:44:30Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/13/you-reap-what-you-sow/"><![CDATA[<p>Megyn Kelly is <a href="http://nymag.com/daily/intelligencer/2015/08/fox-news-picked-trump-over-megyn-kelly.html">getting the short end of the stick</a> from her network as they work to sooth the hurt feelings of one <a href="http://onefoottsunami.com/2015/07/21/you-get-one-donald/">Donald Trump</a>. It’s a deplorable situation all around, but at the same time, what did Kelly expect when she went to work for Fox “News”?</p><br><a href="http://onefoottsunami.com/2015/08/13/you-reap-what-you-sow/" title="Permanent Link to 'You Reap What You Sow' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html"><![CDATA[Please Report to the Principal’s Office]]></title>
|
||||
<link rel="alternate" type="text/html" href="http://onefoottsunami.com/2015/08/12/please-report-to-the-principals-office/" />
|
||||
<id>http://onefoottsunami.com/?p=14784</id>
|
||||
<updated>2015-08-11T18:30:28Z</updated>
|
||||
<published>2015-08-12T14:34:52Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/12/please-report-to-the-principals-office/"><![CDATA[<p>Speaking of <a href="http://onefoottsunami.com/2015/08/07/get-off-my-back/">anxiety-inducing email subject lines</a>, here’s a doozie:</p>
|
||||
|
||||
<ul class="quotes"><li><p>A chat about your bad photos?</p></li></ul>
|
||||
|
||||
<p>When Apple removed the Camera Roll feature of iOS, I briefly used an app called <a href="http://www.myroll.com/myroll">MyRoll</a>. Now, the company is emailing me about a new app called <a href="https://geo.itunes.apple.com/us/app/gallery-doctor/id926864471?mt=8&at=11l5FI">Gallery Doctor</a>, which claims to identify and help you remove bad photos, thereby saving you space on your iPhone. That’s great and all, but how about an uplifting introduction, instead of an email that feels like it’s chastizing me?</p>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[There Is, of Course, No Gun]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.runnersworld.com/runners-stories/for-inmates-the-wall-has-a-totally-different-meaning" />
|
||||
<id>http://onefoottsunami.com/?p=14798</id>
|
||||
<updated>2015-08-11T00:09:01Z</updated>
|
||||
<published>2015-08-11T13:08:56Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/11/there-is-of-course-no-gun/"><![CDATA[<p>Michael Heald has written <a href="http://www.runnersworld.com/runners-stories/for-inmates-the-wall-has-a-totally-different-meaning">an incredible story</a> of running a half-marathon behind the walls of Oregon State Penitentiary, where the phrase “The Wall” takes on a very different meaning.</p><br><a href="http://onefoottsunami.com/2015/08/11/there-is-of-course-no-gun/" title="Permanent Link to 'There Is, of Course, No Gun' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Unwelcome and Superfluous]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.hexjam.com/uk/sex-relationships/i-spent-a-month-replying-to-all-of-my-pr-emails-with-i-love-you" />
|
||||
<id>http://onefoottsunami.com/?p=14796</id>
|
||||
<updated>2015-08-10T15:56:03Z</updated>
|
||||
<published>2015-08-10T15:55:54Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/10/unwelcome-and-superfluous/"><![CDATA[<p>Writer Ralph Jones gets a lot of press releases. Recently, he started <a href="http://www.hexjam.com/uk/sex-relationships/i-spent-a-month-replying-to-all-of-my-pr-emails-with-i-love-you">replying to all of them</a> with the phrase “I love you”.</p><br><a href="http://onefoottsunami.com/2015/08/10/unwelcome-and-superfluous/" title="Permanent Link to 'Unwelcome and Superfluous' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html"><![CDATA[Get Off My Back, CVS]]></title>
|
||||
<link rel="alternate" type="text/html" href="http://onefoottsunami.com/2015/08/07/get-off-my-back/" />
|
||||
<id>http://onefoottsunami.com/?p=14754</id>
|
||||
<updated>2015-08-02T15:49:38Z</updated>
|
||||
<published>2015-08-07T14:16:37Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/07/get-off-my-back/"><![CDATA[<p>Sometimes I receive an email that fills me with existential sorrow. An email with a subject like:</p>
|
||||
|
||||
<ul class="quotes"><li><p>Paul, Are You Making the Most of Our App?</p></li></ul>
|
||||
|
||||
<p>Well god, CVS, I guess I really just don’t know. Am I? The implication is clearly that I’m not making the most of your goddamned app. I don’t need this pressure though, man. Hell, you’re clearly aware that I <em>have</em> the app.</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150807cvs@2x.png" srcset="http://onefoottsunami.com/wordpress/wp-content/uploads/20150807cvs.png 1x, http://onefoottsunami.com/wordpress/wp-content/uploads/20150807cvs@2x.png 2x" style="width: 320px; height: 270px" alt="CVS Image"></p>
|
||||
|
||||
<p>But that’s not enough for you, is it? You’ve gotta tell me about the features of an app I already have installed and insist that I use them. But I’m not in the habit of printing out many photos anymore, and I’m not on any pills. You just let me use my iPhone as I like, and I’ll continue buying the assorted cold medicine, greeting cards, and clandestine bars of candy that I usually do. Deal?</p>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html">Link: <![CDATA[Drones Are Delivering Contraband to Prisons]]></title>
|
||||
|
||||
|
||||
|
||||
<link rel="alternate" type="text/html" href="http://www.fastcompany.com/3049413/fast-feed/drones-are-delivering-contraband-to-prisons" />
|
||||
<id>http://onefoottsunami.com/?p=14789</id>
|
||||
<updated>2015-08-06T14:43:29Z</updated>
|
||||
<published>2015-08-06T14:43:28Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/06/drones-are-delivering-contraband-to-prisons/"><![CDATA[<p>Well of course <a href="http://www.fastcompany.com/3049413/fast-feed/drones-are-delivering-contraband-to-prisons">drones are being used to deliver illicit goods into prisons</a>.</p><br><a href="http://onefoottsunami.com/2015/08/06/drones-are-delivering-contraband-to-prisons/" title="Permanent Link to 'Drones Are Delivering Contraband to Prisons' on One Foot Tsunami">∞ Permalink</a>]]></content>
|
||||
</entry>
|
||||
|
||||
|
||||
|
||||
<entry>
|
||||
<author>
|
||||
<name>Paul Kafasis</name>
|
||||
</author>
|
||||
<title type="html"><![CDATA[Planes, “Planes”, and Automated Fare Pricing]]></title>
|
||||
<link rel="alternate" type="text/html" href="http://onefoottsunami.com/2015/08/05/planes-planes-and-automated-fare-pricing/" />
|
||||
<id>http://onefoottsunami.com/?p=14756</id>
|
||||
<updated>2015-08-05T16:59:42Z</updated>
|
||||
<published>2015-08-05T15:50:23Z</published>
|
||||
<content type="html" xml:base="http://onefoottsunami.com/2015/08/05/planes-planes-and-automated-fare-pricing/"><![CDATA[<p>Let’s say that you wanted to get from Dayton, Ohio to central New Jersey, as friend of the site Chris DiNoia recently wanted to do. On United.com, you might select Newark as your destination, and get a result like this:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/newark@2x.png" srcset="http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/newark.png 1x, http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/newark@2x.png 2x" style="width: 600px; height: 100px; border: 1px solid black;" alt="Flying into Newark"></p>
|
||||
|
||||
<p>$353 is rather pricey for a one-way flight. Let’s check some other options. Philadelphia is about 30 minutes farther than Newark from central Jersey, and presumably you’re <a href="http://onefoottsunami.com/2015/08/03/well-i-guess-its-bust/">not a defenseless robot</a>, so you should be safe there for a few minutes. Set Philadelphia as your destination instead, and hey, why not turn on the “Search Nearby Airports” checkbox?</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/philadelphia@2x.png" srcset="http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/philadelphia.png 1x, http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/philadelphia@2x.png 2x" style="width: 600px; 216px; border: 1px solid black;" alt="Flying into Philadelphia"></p>
|
||||
|
||||
<p>Hey, now there’s a better deal. You can get home for just $149! But hang on a sec. A close look shows that rather than Philadelphia International Airport (PHL), this flight winds up at “ZFV”, which is labeled as a “rail station”. How exactly is a plane going to land there?</p>
|
||||
|
||||
<p>Examine this flight, and you’ll see something bizarre:</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/train@2x.png" srcset="http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/train.png 1x, http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/train@2x.png 2x" style="width: 265px; 96px; border: 1px solid black;" alt="Train Service">
|
||||
<br />“NOTE: This is Train Service” is a truly amazing warning.</p>
|
||||
|
||||
<p>United Airlines is apparently code-sharing with Amtrak’s passenger railroad service to get you to Philadelphia. They’re also referring to Philadelphia’s 30th Street Rail Station (that’s what ZFV stands for) as an airport. Do you think the conductor announces that train as “United flight 3174”? Man, I hope so.</p>
|
||||
|
||||
<p>But the wacky train-instead-of-plane isn’t even the half of it. Take a look at the first half of this itinerary, and compare it to the original search. It’s the exact same flight!</p>
|
||||
|
||||
<p class="centeredimage"><img src="http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/comparison.png" srcset="http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/comparison.png 1x, http://onefoottsunami.com/wordpress/wp-content/uploads/20150805planestrains/comparison.png 2x" style="width: 483px; 220px; border: 1px solid black;" alt="Comparison"></p>
|
||||
|
||||
<p>This type of airline pricing nonsense is not entirely uncommon. Opting for the lower fare and then getting off at Newark even has a name, ”<a href="https://en.wikipedia.org/wiki/Airline_booking_ploys#Hidden_city_ticketing">Hidden city ticketing</a>”. Still, it’s not very often that you can save over $200 just by missing a train.</p>]]></content>
|
||||
</entry>
|
||||
|
||||
</feed>
|
||||
|
||||
|
||||
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/
|
||||
|
||||
Minified using disk
|
||||
Page Caching using xcache
|
||||
Database Caching 14/22 queries in 0.009 seconds using disk
|
||||
|
||||
Served from: onefoottsunami.com @ 2015-09-08 23:39:37 by W3 Total Cache -->
|
945
Parser/Tests/ParserTests/Resources/ScriptingNews.json
Normal file
945
Parser/Tests/ParserTests/Resources/ScriptingNews.json
Normal file
@ -0,0 +1,945 @@
|
||||
{
|
||||
"rss": {
|
||||
"version": "2.0",
|
||||
"xmlns:source": "http://source.scripting.com/",
|
||||
"channel": {
|
||||
"title": "Scripting News",
|
||||
"link": "http://scripting.com/",
|
||||
"description": "Scripting News, the weblog started in 1997 that bootstrapped the blogging revolution.",
|
||||
"pubDate": "Mon, 26 Jun 2017 19:40:58 GMT",
|
||||
"lastBuildDate": "Mon, 26 Jun 2017 19:41:48 GMT",
|
||||
"language": "en-us",
|
||||
"copyright": "© 1994-2017 <a href=\"http://davewiner.com/\">Dave Winer</a>.",
|
||||
"generator": "oldSchool v0.42c",
|
||||
"docs": "https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md",
|
||||
"source:localTime": "Mon, June 26, 2017 3:41 PM EDT",
|
||||
"cloud": {
|
||||
"domain": "rpc.rsscloud.io",
|
||||
"port": 5337,
|
||||
"path": "/pleaseNotify",
|
||||
"registerProcedure": "",
|
||||
"protocol": "http-post"
|
||||
},
|
||||
"source:account": [
|
||||
{
|
||||
"service": "twitter",
|
||||
"#value": "davewiner"
|
||||
},
|
||||
{
|
||||
"service": "facebook",
|
||||
"#value": "dave.winer.12"
|
||||
},
|
||||
{
|
||||
"service": "github",
|
||||
"#value": "scripting"
|
||||
},
|
||||
{
|
||||
"service": "linkedin",
|
||||
"#value": "scripting"
|
||||
}
|
||||
],
|
||||
"item": [
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/26.html#a080605",
|
||||
"description": "Good morning students and teachers! 🍏 ",
|
||||
"pubDate": "Mon, 26 Jun 2017 12:20:05 GMT",
|
||||
"guid": "http://scripting.com/2017/06/26.html#a080605",
|
||||
"source:outline": {
|
||||
"text": "Good morning students and teachers! :green_apple: ",
|
||||
"created": "Mon, 26 Jun 2017 12:20:05 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a080605"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/26.html#a030658",
|
||||
"description": "This is the <a href=\"https://twitter.com/kennethn/status/879367091953868801\">human side</a> of <a href=\"http://scripting.com/2017/03/08/theWorldIsSocialistPartIi.html\">Health care is socialist</a>. ",
|
||||
"pubDate": "Mon, 26 Jun 2017 19:40:58 GMT",
|
||||
"guid": "http://scripting.com/2017/06/26.html#a030658",
|
||||
"source:outline": {
|
||||
"text": "This is the <a href=\"https://twitter.com/kennethn/status/879367091953868801\">human side</a> of <a href=\"http://scripting.com/2017/03/08/theWorldIsSocialistPartIi.html\">Health care is socialist</a>. ",
|
||||
"created": "Mon, 26 Jun 2017 19:40:58 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a030658"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/26.html#a020604",
|
||||
"description": "Interesting Politico <a href=\"http://www.politico.com/magazine/story/2017/06/26/trump-president-style-mayor-215294\">piece</a> posits that Trump acts as if he's mayor of the United States. If <a href=\"https://en.wikipedia.org/wiki/Mayor_of_New_York_City\">NYC</a> is his model, that mayor is esp powerless, because the governor of the state also has a lot of power over the city. It's approx 1/2 of the population of the state, and probably much more than 1/2 of the money. For example, the <a href=\"https://en.wikipedia.org/wiki/Metropolitan_Transportation_Authority\">MTA</a>, which runs the buses and subway, is run by the state, not the city.",
|
||||
"pubDate": "Mon, 26 Jun 2017 18:10:04 GMT",
|
||||
"guid": "http://scripting.com/2017/06/26.html#a020604",
|
||||
"source:outline": {
|
||||
"text": "Interesting Politico <a href=\"http://www.politico.com/magazine/story/2017/06/26/trump-president-style-mayor-215294\">piece</a> posits that Trump acts as if he's mayor of the United States. If <a href=\"https://en.wikipedia.org/wiki/Mayor_of_New_York_City\">NYC</a> is his model, that mayor is esp powerless, because the governor of the state also has a lot of power over the city. It's approx 1/2 of the population of the state, and probably much more than 1/2 of the money. For example, the <a href=\"https://en.wikipedia.org/wiki/Metropolitan_Transportation_Authority\">MTA</a>, which runs the buses and subway, is run by the state, not the city.",
|
||||
"created": "Mon, 26 Jun 2017 18:10:04 GMT",
|
||||
"type": "outline",
|
||||
"image": "http://scripting.com/images/2017/06/26/quimby.png",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a020604"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/26.html#a020602",
|
||||
"description": "<a href=\"https://github.com/scripting/Scripting-News/issues/12\">Brent asks</a> if the <i>length</i> in enclosures in <a href=\"https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md\">RSS-in-JSON</a> is a number or string. That's what the test podcast <a href=\"http://scripting.com/2017/06/26.html#a010633\">below</a> is for. ",
|
||||
"pubDate": "Mon, 26 Jun 2017 18:05:02 GMT",
|
||||
"guid": "http://scripting.com/2017/06/26.html#a020602",
|
||||
"source:outline": {
|
||||
"text": "<a href=\"https://github.com/scripting/Scripting-News/issues/12\">Brent asks</a> if the <i>length</i> in enclosures in \"RSS-in-JSON\" is a number or string. That's what the test podcast <a href=\"http://scripting.com/2017/06/26.html#a010633\">below</a> is for. ",
|
||||
"created": "Mon, 26 Jun 2017 18:05:02 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a020602"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/26.html#a010633",
|
||||
"description": "From time to time I have to do a podcast to test things out. This is one of those times. Let's see what happens. ",
|
||||
"pubDate": "Mon, 26 Jun 2017 17:29:33 GMT",
|
||||
"guid": "http://scripting.com/2017/06/26.html#a010633",
|
||||
"enclosure": {
|
||||
"url": "http://scripting.com/2017/06/26/yetAnotherTestPodcast.m4a",
|
||||
"type": "audio/mpeg",
|
||||
"length": 277413
|
||||
},
|
||||
"source:outline": {
|
||||
"text": "From time to time I have to do a podcast to test things out. This is one of those times. Let's see what happens. ",
|
||||
"created": "Mon, 26 Jun 2017 17:29:33 GMT",
|
||||
"type": "outline",
|
||||
"enclosure": "http://scripting.com/2017/06/26/yetAnotherTestPodcast.m4a",
|
||||
"enclosureType": "audio/mpeg",
|
||||
"enclosureLength": "277413",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a010633"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/26.html#a110603",
|
||||
"description": "Body shaming is wrong no matter who you're using as the example. Someone is being hurt by <a href=\"https://twitter.com/xeni/status/879354857374732288\">this</a>. No, I don't care how much you have suffered.",
|
||||
"pubDate": "Mon, 26 Jun 2017 15:26:03 GMT",
|
||||
"guid": "http://scripting.com/2017/06/26.html#a110603",
|
||||
"source:outline": {
|
||||
"text": "Body shaming is wrong no matter who you're using as the example. Someone is being hurt by <a href=\"https://twitter.com/xeni/status/879354857374732288\">this</a>. No, I don't care how much you have suffered.",
|
||||
"created": "Mon, 26 Jun 2017 15:26:03 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a110603"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/26.html#a090616",
|
||||
"description": "I need <a href=\"https://github.com/scripting/xmlViewer/blob/master/xmlviewer.js\">an app</a> to <a href=\"http://xmlviewer.scripting.com/?url=http://scripting.com/rss.xml\">view</a> RSS feeds in the browser because Chrome and Safari refuse to let me do that. I'd love to hear the reason why. ",
|
||||
"pubDate": "Mon, 26 Jun 2017 13:24:16 GMT",
|
||||
"guid": "http://scripting.com/2017/06/26.html#a090616",
|
||||
"source:outline": {
|
||||
"text": "I need <a href=\"https://github.com/scripting/xmlViewer/blob/master/xmlviewer.js\">an app</a> to <a href=\"http://xmlviewer.scripting.com/?url=http://scripting.com/rss.xml\">view</a> RSS feeds in the browser because Chrome and Safari refuse to let me do that. I'd love to hear the reason why. ",
|
||||
"created": "Mon, 26 Jun 2017 13:24:16 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a090616"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Subscribable feed lists give power to users",
|
||||
"link": "http://scripting.com/2017/06/26.html#a080636",
|
||||
"description": "<p>An interesting <a href=\"https://github.com/kylewm/woodwind/issues/7#issuecomment-311000922\">comment</a> from <a href=\"https://github.com/chrisaldrich\">Chris Aldrich</a> about subscribing to lists of feeds in a thread on the <a href=\"https://github.com/kylewm/woodwind\">Woodwind</a> app site on GitHub. </p>\n<p>Here's the basic idea. There's a difference between importing OPML into a reader and subscribing to it. The latter is very powerful, for the user, but a lot of RSS reader devs may not want their users to have that much power. It's not a very hard feature to implement. </p>\n<p>The idea has been <a href=\"https://duckduckgo.com/?q=site%3Ascripting.com+%22reading+list%22&t=hz&ia=web\">much-discussed</a> here. We call them reading lists. Michael Arrington even wrote a <a href=\"https://techcrunch.com/2005/10/16/my-thoughts-on-reading-lists/\">TechCrunch piece</a> about it in 2005. </p>\n<p>Subscribable OPML is something all my readers have been able to do through an OPML feature called <a href=\"http://dev.opml.org/spec2.html#inclusion\">inclusion</a>. I wrote a <a href=\"https://github.com/scripting/river5/blob/master/docs/DROPBOXSUBSCRIPTIONLISTS.md\">howto</a> for a <a href=\"https://github.com/scripting/river5\">River5</a> user re inclusion just last week.</p>\n<p><a href=\"https://techcrunch.com/2006/05/07/share-your-opml/\">Share Your OPML</a> was a service I operated for a while. It made it possible to manage your OPML separate from the reader you used. It was meant to encourage readers to support subscribable OPML. I'm looking for an excuse to bring it back, but first we need a base of shared feed lists. </p>\n<p>A lot of good stuff can be done if feed readers are willing to delegate list management to other services. IMHO the only reason a reader developer <i>wouldn't</i> do it is because they want to lock users in. If I let you edit your feed list elsewhere that means you could give the list to another vendor and have a choice which to use. It's really something users should demand, esp if you're paying for the service. </p>\n",
|
||||
"pubDate": "Mon, 26 Jun 2017 12:24:36 GMT",
|
||||
"guid": "http://scripting.com/2017/06/26.html#a080636",
|
||||
"source:outline": {
|
||||
"text": "Subscribable feed lists give power to users",
|
||||
"created": "Mon, 26 Jun 2017 12:24:36 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "An interesting <a href=\"https://github.com/kylewm/woodwind/issues/7#issuecomment-311000922\">comment</a> from <a href=\"https://github.com/chrisaldrich\">Chris Aldrich</a> about subscribing to lists of feeds in a thread on the <a href=\"https://github.com/kylewm/woodwind\">Woodwind</a> app site on GitHub. ",
|
||||
"created": "Mon, 26 Jun 2017 12:24:49 GMT",
|
||||
"image": "http://static.scripting.com/larryKing/images/2014/05/25/goodHumor.gif",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a080649"
|
||||
},
|
||||
{
|
||||
"text": "Here's the basic idea. There's a difference between importing OPML into a reader and subscribing to it. The latter is very powerful, for the user, but a lot of RSS reader devs may not want their users to have that much power. It's not a very hard feature to implement. ",
|
||||
"created": "Mon, 26 Jun 2017 12:40:32 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a080632"
|
||||
},
|
||||
{
|
||||
"text": "The idea has been <a href=\"https://duckduckgo.com/?q=site%3Ascripting.com+%22reading+list%22&t=hz&ia=web\">much-discussed</a> here. We call them reading lists. Michael Arrington even wrote a <a href=\"https://techcrunch.com/2005/10/16/my-thoughts-on-reading-lists/\">TechCrunch piece</a> about it in 2005. ",
|
||||
"created": "Mon, 26 Jun 2017 12:26:01 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a080601"
|
||||
},
|
||||
{
|
||||
"text": "Subscribable OPML is something all my readers have been able to do through an OPML feature called <a href=\"http://dev.opml.org/spec2.html#inclusion\">inclusion</a>. I wrote a <a href=\"https://github.com/scripting/river5/blob/master/docs/DROPBOXSUBSCRIPTIONLISTS.md\">howto</a> for a \"River5\" user re inclusion just last week.",
|
||||
"created": "Mon, 26 Jun 2017 12:27:15 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a080615"
|
||||
},
|
||||
{
|
||||
"text": "<a href=\"https://techcrunch.com/2006/05/07/share-your-opml/\">Share Your OPML</a> was a service I operated for a while. It made it possible to manage your OPML separate from the reader you used. It was meant to encourage readers to support subscribable OPML. I'm looking for an excuse to bring it back, but first we need a base of shared feed lists. ",
|
||||
"created": "Mon, 26 Jun 2017 12:33:10 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a080610"
|
||||
},
|
||||
{
|
||||
"text": "A lot of good stuff can be done if feed readers are willing to delegate list management to other services. IMHO the only reason a reader developer <i>wouldn't</i> do it is because they want to lock users in. If I let you edit your feed list elsewhere that means you could give the list to another vendor and have a choice which to use. It's really something users should demand, esp if you're paying for the service. ",
|
||||
"created": "Mon, 26 Jun 2017 12:47:13 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a080613"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/26.html#a080636"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/25.html#a080631",
|
||||
"description": "Good morning Internet guzzlers! 🍺",
|
||||
"pubDate": "Sun, 25 Jun 2017 12:27:31 GMT",
|
||||
"guid": "http://scripting.com/2017/06/25.html#a080631",
|
||||
"source:outline": {
|
||||
"text": "Good morning Internet guzzlers! :beer:",
|
||||
"created": "Sun, 25 Jun 2017 12:27:31 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/25.html#a080631"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/25.html#a080606",
|
||||
"description": "So glad I stopped worrying <a href=\"https://duckduckgo.com/?q=site%3Ascripting.com+facebook&t=hz&ia=web\">about</a> Facebook and am now blogging <i>Old School</i> style on <a href=\"http://scripting.com/\">scripting.com</a>. I've found my <a href=\"https://en.wikipedia.org/wiki/Sea_legs\">sea legs</a> once again. ",
|
||||
"pubDate": "Sun, 25 Jun 2017 12:43:06 GMT",
|
||||
"guid": "http://scripting.com/2017/06/25.html#a080606",
|
||||
"source:outline": {
|
||||
"text": "So glad I stopped worrying <a href=\"https://duckduckgo.com/?q=site%3Ascripting.com+facebook&t=hz&ia=web\">about</a> Facebook and am now blogging <i>Old School</i> style on <a href=\"http://scripting.com/\">scripting.com</a>. I've found my <a href=\"https://en.wikipedia.org/wiki/Sea_legs\">sea legs</a> once again. ",
|
||||
"created": "Sun, 25 Jun 2017 12:43:06 GMT",
|
||||
"type": "tweet",
|
||||
"tweetId": "878956761302147072",
|
||||
"tweetUserName": "davewiner",
|
||||
"permalink": "http://scripting.com/2017/06/25.html#a080606"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/25.html#a080623",
|
||||
"description": "Dan Shafer <a href=\"https://www.facebook.com/vicky.elder.77/posts/10211645108716478?pnref=story\">died</a>. I knew Dan from the Mac developer community in the 80s, hired <a href=\"https://en.wikipedia.org/wiki/Dan_Shafer\">him</a> to write the first docs for <a href=\"http://en.wikipedia.org/wiki/UserLand_Software\">Frontier</a>. Bon voyage mi amigo! 💥",
|
||||
"pubDate": "Sun, 25 Jun 2017 12:56:23 GMT",
|
||||
"guid": "http://scripting.com/2017/06/25.html#a080623",
|
||||
"source:outline": {
|
||||
"text": "Dan Shafer <a href=\"https://www.facebook.com/vicky.elder.77/posts/10211645108716478?pnref=story\">died</a>. I knew Dan from the Mac developer community in the 80s, hired <a href=\"https://en.wikipedia.org/wiki/Dan_Shafer\">him</a> to write the first docs for \"Frontier\". Bon voyage mi amigo! :boom:",
|
||||
"created": "Sun, 25 Jun 2017 12:56:23 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/25.html#a080623"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/25.html#a080624",
|
||||
"description": "Money was a big issue yesterday in the nascent tech blogosphere. First, you do this because you love it, not because it pays well. (It doesn't pay at all.) Now I'd like to take you back to a discsussion that was had many years ago that resulted in this conclusion. You don't make money from this work, but it leads to opportunities where you can make money. Ideas and information make their way to you and if you are so-inclined you can make money by investing in those ideas. No sure things, but some bloggers have made billions, and others have made millions. And others have made a decent living. Not <i>from</i> their blog but <i>because</i> they blog. ",
|
||||
"pubDate": "Sun, 25 Jun 2017 12:29:24 GMT",
|
||||
"guid": "http://scripting.com/2017/06/25.html#a080624",
|
||||
"source:outline": {
|
||||
"text": "Money was a big issue yesterday in the nascent tech blogosphere. First, you do this because you love it, not because it pays well. (It doesn't pay at all.) Now I'd like to take you back to a discsussion that was had many years ago that resulted in this conclusion. You don't make money from this work, but it leads to opportunities where you can make money. Ideas and information make their way to you and if you are so-inclined you can make money by investing in those ideas. No sure things, but some bloggers have made billions, and others have made millions. And others have made a decent living. Not <i>from</i> their blog but <i>because</i> they blog. ",
|
||||
"created": "Sun, 25 Jun 2017 12:29:24 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/25.html#a080624"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/25.html#a080631",
|
||||
"description": "In 2015 I <a href=\"http://scripting.com/2015/06/25/dropboxCouldBeKingOfTheOnepageApp.html\">wrote</a> that Dropbox could be the king of the one-page app. Because storage is the thing the web doesn't, on its own, do, and storage is the thing Dropbox does best. And they have an API, and they understood the connection to one-page-apps earlier than anyone. But it didn't happen. I've emailed with people at Dropbox from time to time and the best explanation I can come up with is that they are focused in different areas. It seems to me, from my outside perch, that they are trying to become a competitor to <a href=\"https://www.google.com/docs/about/\">Google's</a> and <a href=\"https://en.wikipedia.org/wiki/Microsoft_Office\">Microsoft's</a> Office products. I was hoping they'd become a platform, focusing on distribution and investment in startups.",
|
||||
"pubDate": "Sun, 25 Jun 2017 12:32:31 GMT",
|
||||
"guid": "http://scripting.com/2017/06/25.html#a080631",
|
||||
"source:outline": {
|
||||
"text": "In 2015 I <a href=\"http://scripting.com/2015/06/25/dropboxCouldBeKingOfTheOnepageApp.html\">wrote</a> that Dropbox could be the king of the one-page app. Because storage is the thing the web doesn't, on its own, do, and storage is the thing Dropbox does best. And they have an API, and they understood the connection to one-page-apps earlier than anyone. But it didn't happen. I've emailed with people at Dropbox from time to time and the best explanation I can come up with is that they are focused in different areas. It seems to me, from my outside perch, that they are trying to become a competitor to <a href=\"https://www.google.com/docs/about/\">Google's</a> and <a href=\"https://en.wikipedia.org/wiki/Microsoft_Office\">Microsoft's</a> Office products. I was hoping they'd become a platform, focusing on distribution and investment in startups.",
|
||||
"created": "Sun, 25 Jun 2017 12:32:31 GMT",
|
||||
"type": "outline",
|
||||
"image": "http://radio3.io/icons/clarus.gif",
|
||||
"permalink": "http://scripting.com/2017/06/25.html#a080631"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Code mode is for real",
|
||||
"link": "http://scripting.com/2017/06/25.html#a110613",
|
||||
"description": "<p><a href=\"http://donthitsave.com/comic/2016/03/25/code-mode\"><img src=\"http://scripting.com/images/2017/06/25/humor.png\" width=\"300\" height=\"285\" border=\"0\" alt=\"Code mode is a real thing.\"></a></p>\n",
|
||||
"pubDate": "Sun, 25 Jun 2017 15:53:13 GMT",
|
||||
"guid": "http://scripting.com/2017/06/25.html#a110613",
|
||||
"source:outline": {
|
||||
"text": "Code mode is for real",
|
||||
"created": "Sun, 25 Jun 2017 15:53:13 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "<a href=\"http://donthitsave.com/comic/2016/03/25/code-mode\"><img src=\"http://scripting.com/images/2017/06/25/humor.png\" width=\"300\" height=\"285\" border=\"0\" alt=\"Code mode is a real thing.\"></a>",
|
||||
"created": "Sun, 25 Jun 2017 15:53:18 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/25.html#a110618"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/25.html#a110613"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a100601",
|
||||
"description": "Good morning sports fans! 🏈",
|
||||
"pubDate": "Sat, 24 Jun 2017 14:57:01 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a100601",
|
||||
"source:outline": {
|
||||
"text": "Good morning sports fans! :football:",
|
||||
"created": "Sat, 24 Jun 2017 14:57:01 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a100601"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a040606",
|
||||
"description": "<a href=\"http://scripting.com/2017/03/08/theWorldIsSocialistPartIi.html\">Health care is socialist</a> is getting a bunch of new reads today thanks to some powerful RTs.",
|
||||
"pubDate": "Sat, 24 Jun 2017 20:50:06 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a040606",
|
||||
"source:outline": {
|
||||
"text": "<a href=\"http://scripting.com/2017/03/08/theWorldIsSocialistPartIi.html\">Health care is socialist</a> is getting a bunch of new reads today thanks to some powerful RTs.",
|
||||
"created": "Sat, 24 Jun 2017 20:50:06 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a040606"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a020644",
|
||||
"description": "Today is emoji day ⭐️ on Scripting News. We have all the ⭐️ best emoji. And they're free, for you, the best ⭐️ people in the universe, the readers of ⭐️ this humble blog. ⭐️ ",
|
||||
"pubDate": "Sat, 24 Jun 2017 18:34:44 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a020644",
|
||||
"source:outline": {
|
||||
"text": "Today is emoji day :star: on Scripting News. We have all the :star: best emoji. And they're free, for you, the best :star: people in the universe, the readers of :star: this humble blog. :star: ",
|
||||
"created": "Sat, 24 Jun 2017 18:34:44 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a020644"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a020620",
|
||||
"description": "It makes sense that because of <a href=\"https://en.wikipedia.org/wiki/Reconciliation_(United_States_Congress)\">reconcilliation</a> Repubs have a hard time repealing ObamaCare. It seems fair that you should have to have 60 votes to repeal something that required 60 votes to pass. How will they explain it to the voters they've been lying to about \"repeal and replace.\" Double-talk. Swamp-talk. 👍",
|
||||
"pubDate": "Sat, 24 Jun 2017 18:20:20 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a020620",
|
||||
"source:outline": {
|
||||
"text": "It makes sense that because of <a href=\"https://en.wikipedia.org/wiki/Reconciliation_(United_States_Congress)\">reconcilliation</a> Repubs have a hard time repealing ObamaCare. It seems fair that you should have to have 60 votes to repeal something that required 60 votes to pass. How will they explain it to the voters they've been lying to about \"repeal and replace.\" Double-talk. Swamp-talk. :+1:",
|
||||
"created": "Sat, 24 Jun 2017 18:20:20 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a020620"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a020649",
|
||||
"description": "I've started a <a href=\"https://gitter.im/scriptingnews/Lobby\">chatroom</a> on Gitter. Not sure what I'll use it for. Also not sure if it's open for anyone to join. I want it to be. The community <a href=\"http://guidelines.scripting.com/\">guidelines</a> apply. Keep it short, respectful and on-topic, and <i>no spam. </i>⚾️",
|
||||
"pubDate": "Sat, 24 Jun 2017 18:05:49 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a020649",
|
||||
"source:outline": {
|
||||
"text": "I've started a <a href=\"https://gitter.im/scriptingnews/Lobby\">chatroom</a> on Gitter. Not sure what I'll use it for. Also not sure if it's open for anyone to join. I want it to be. The community <a href=\"http://guidelines.scripting.com/\">guidelines</a> apply. Keep it short, respectful and on-topic, and <i>no spam. </i>:baseball:",
|
||||
"created": "Sat, 24 Jun 2017 18:05:49 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a020649"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a110617",
|
||||
"description": "BTW, don't ask me to pitch RSS, I won't do it, because the request is based on a misunderstanding. RSS is not a product, it's a format. I have never made a dime from it. You have as much at stake in its success as I do. So I always turn it around and ask the person who asked me for a pitch to instead pitch <i>me</i> on it. I don't budge on this. Ever. 🏀",
|
||||
"pubDate": "Sat, 24 Jun 2017 15:19:17 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a110617",
|
||||
"source:outline": {
|
||||
"text": "BTW, don't ask me to pitch RSS, I won't do it, because the request is based on a misunderstanding. RSS is not a product, it's a format. I have never made a dime from it. You have as much at stake in its success as I do. So I always turn it around and ask the person who asked me for a pitch to instead pitch <i>me</i> on it. I don't budge on this. Ever. :basketball:",
|
||||
"created": "Sat, 24 Jun 2017 15:19:17 GMT",
|
||||
"type": "outline",
|
||||
"image": "http://scripting.com/images/2017/06/24/rssTShirt.png",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a110617"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a110610",
|
||||
"description": "Is anyone using the <a href=\"http://scripting.com/rss.json\">JSON version</a> of the Scripting News feed in their feed reader? It's a bit of a trick question, in a way, because as far as I know, only <a href=\"https://github.com/scripting/river5\">River5</a> and <a href=\"http://this.how/electricRiver\">Electric River</a> support this format. If so, send me an email at my address, on the About page <a href=\"http://scripting.com/about.html\">here</a>. 🍰",
|
||||
"pubDate": "Sat, 24 Jun 2017 15:01:10 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a110610",
|
||||
"source:outline": {
|
||||
"text": "Is anyone using the <a href=\"http://scripting.com/rss.json\">JSON version</a> of the Scripting News feed in their feed reader? It's a bit of a trick question, in a way, because as far as I know, only \"River5\" and \"Electric River\" support this format. If so, send me an email at my address, on the About page <a href=\"http://scripting.com/about.html\">here</a>. :cake:",
|
||||
"created": "Sat, 24 Jun 2017 15:01:10 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a110610"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a010650",
|
||||
"description": "Journalists keep making a serious technology error -- assuming the only damage Russia can do to our government is during elections. 🇺🇸 ",
|
||||
"pubDate": "Sat, 24 Jun 2017 05:50:50 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a010650",
|
||||
"source:outline": {
|
||||
"text": "Journalists keep making a serious technology error -- assuming the only damage Russia can do to our government is during elections. :us: ",
|
||||
"created": "Sat, 24 Jun 2017 05:50:50 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a010650"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/24.html#a100632",
|
||||
"description": "I get my health insurance through the <a href=\"https://en.wikipedia.org/wiki/Patient_Protection_and_Affordable_Care_Act\">ACA</a>. It's very much alive, <a href=\"http://www.politico.com/story/2017/06/23/sean-spicer-obamacare-repeal-bill-239895\">Spicer</a>. Without it I would not have health insurance. 🍋 ",
|
||||
"pubDate": "Sat, 24 Jun 2017 14:26:32 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a100632",
|
||||
"source:outline": {
|
||||
"text": "I get my health insurance through the <a href=\"https://en.wikipedia.org/wiki/Patient_Protection_and_Affordable_Care_Act\">ACA</a>. It's very much alive, <a href=\"http://www.politico.com/story/2017/06/23/sean-spicer-obamacare-repeal-bill-239895\">Spicer</a>. Without it I would not have health insurance. :lemon: ",
|
||||
"created": "Sat, 24 Jun 2017 14:26:32 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a100632"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "The sad state of tech news in 2017",
|
||||
"link": "http://scripting.com/2017/06/24.html#a090648",
|
||||
"description": "<p>It won't take much to reboot the tech blogosphere, just a few bloggers with ideas who listen to each other and want to work with each other. That was the <a href=\"http://scripting.com/davenet/1996/02/08/holdinghandsincyberspace.html\">idea</a> behind blogrolls, to visibly show the relationships. </p>\n<ul>\n\t<li>I'm still thinking about how to integrate a blogroll with the new design of <a href=\"http://scripting.com/\">Scripting News</a>. I pushed everything aside to get a clean look for the new site. I want to avoid bringing it all back. </li>\n\t</ul>\n<p>So, we have a few people who are writing and listening. That's a needed first step. Next we need a way to announce and hear about new tech products. Not just ones that get VC backing or come from big companies. We already hear about those products through <a href=\"http://techmeme.com/\">TechMeme</a> and the pubs that contribute to it. We also have platform-specific news about tech products, it's more limited, but it's there.</p>\n<p>It will likely start with word of mouth among the bloggers. If <a href=\"http://altplatform.org/2017/06/20/building-a-blogroll-in-2017/\">Richard</a> is using a product and speaks highly of it, I'm likely to take a look. Especially if he says it fits into what I'm doing through the open formats my software already supports. </p>\n<p>Then I want a river, a place where I can go to find out quickly what's new, in the way of products, not BigCo bluster or another $250 million VC deal. I want to know what my peers are doing. So I can learn from them, and so we can make our products work with theirs. </p>\n<p><i>Work together</i> is a phrase you'll hear me use a lot. It's the potential of tech, but it often isn't the attitude of tech. Even the smallest most independent developers dream of dominating. You can't work with people who dominate, even if they win. </p>\n<p>I want to hear about products that are open to connecting to mine. </p>\n<p>There have been times, often defined by news sources, that have created huge swells of compatible technology. To name a few: InfoWorld, PC WEEK, MacWEEK, TechCrunch. Very fond memories of the communities that gathered around each of those. </p>\n<p>It's time for another. The opportunity is there. It's been a long time since we had an open development community that worked to create great new user experience without lockin. It's like riding a bicycle or swimming, you don't forget how to do it. And like tennis or baseball, you can't play without partners and competition. </p>\n",
|
||||
"pubDate": "Sat, 24 Jun 2017 13:43:48 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a090648",
|
||||
"source:outline": {
|
||||
"text": "The sad state of tech news in 2017",
|
||||
"created": "Sat, 24 Jun 2017 13:43:48 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "It won't take much to reboot the tech blogosphere, just a few bloggers with ideas who listen to each other and want to work with each other. That was the <a href=\"http://scripting.com/davenet/1996/02/08/holdinghandsincyberspace.html\">idea</a> behind blogrolls, to visibly show the relationships. ",
|
||||
"created": "Sat, 24 Jun 2017 13:43:55 GMT",
|
||||
"subs": [
|
||||
{
|
||||
"text": "I'm still thinking about how to integrate a blogroll with the new design of \"Scripting News\". I pushed everything aside to get a clean look for the new site. I want to avoid bringing it all back. ",
|
||||
"created": "Sat, 24 Jun 2017 14:32:17 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a100617"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a090655"
|
||||
},
|
||||
{
|
||||
"text": "So, we have a few people who are writing and listening. That's a needed first step. Next we need a way to announce and hear about new tech products. Not just ones that get VC backing or come from big companies. We already hear about those products through \"TechMeme\" and the pubs that contribute to it. We also have platform-specific news about tech products, it's more limited, but it's there.",
|
||||
"created": "Sat, 24 Jun 2017 13:44:09 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a090609"
|
||||
},
|
||||
{
|
||||
"text": "It will likely start with word of mouth among the bloggers. If <a href=\"http://altplatform.org/2017/06/20/building-a-blogroll-in-2017/\">Richard</a> is using a product and speaks highly of it, I'm likely to take a look. Especially if he says it fits into what I'm doing through the open formats my software already supports. ",
|
||||
"created": "Sat, 24 Jun 2017 13:52:04 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a090604"
|
||||
},
|
||||
{
|
||||
"text": "Then I want a river, a place where I can go to find out quickly what's new, in the way of products, not BigCo bluster or another $250 million VC deal. I want to know what my peers are doing. So I can learn from them, and so we can make our products work with theirs. ",
|
||||
"created": "Sat, 24 Jun 2017 13:52:04 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a090604"
|
||||
},
|
||||
{
|
||||
"text": "<i>Work together</i> is a phrase you'll hear me use a lot. It's the potential of tech, but it often isn't the attitude of tech. Even the smallest most independent developers dream of dominating. You can't work with people who dominate, even if they win. ",
|
||||
"created": "Sat, 24 Jun 2017 13:54:33 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a090633"
|
||||
},
|
||||
{
|
||||
"text": "I want to hear about products that are open to connecting to mine. ",
|
||||
"created": "Sat, 24 Jun 2017 20:53:41 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a040641"
|
||||
},
|
||||
{
|
||||
"text": "There have been times, often defined by news sources, that have created huge swells of compatible technology. To name a few: InfoWorld, PC WEEK, MacWEEK, TechCrunch. Very fond memories of the communities that gathered around each of those. ",
|
||||
"created": "Sat, 24 Jun 2017 13:48:02 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a090602"
|
||||
},
|
||||
{
|
||||
"text": "It's time for another. The opportunity is there. It's been a long time since we had an open development community that worked to create great new user experience without lockin. It's like riding a bicycle or swimming, you don't forget how to do it. And like tennis or baseball, you can't play without partners and competition. ",
|
||||
"created": "Sat, 24 Jun 2017 13:57:02 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a090602"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a090648"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Republican-inspired art",
|
||||
"link": "http://scripting.com/2017/06/24.html#a100632",
|
||||
"description": "<p><a href=\"http://boingboing.net/2017/06/23/the-white-house-is-having-off.html\"><img src=\"http://scripting.com/images/2017/06/24/spicer.png\" width=\"502\" height=\"339\" border=\"0\" alt=\"A picture named spicer.png\"></a></p>\n",
|
||||
"pubDate": "Sat, 24 Jun 2017 14:52:32 GMT",
|
||||
"guid": "http://scripting.com/2017/06/24.html#a100632",
|
||||
"source:outline": {
|
||||
"text": "Republican-inspired art",
|
||||
"created": "Sat, 24 Jun 2017 14:52:32 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "<a href=\"http://boingboing.net/2017/06/23/the-white-house-is-having-off.html\"><img src=\"http://scripting.com/images/2017/06/24/spicer.png\" width=\"502\" height=\"339\" border=\"0\" alt=\"A picture named spicer.png\"></a>",
|
||||
"created": "Sat, 24 Jun 2017 14:52:37 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a100637"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/24.html#a100632"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/23.html#a070648",
|
||||
"description": "Thank you <a href=\"https://twitter.com/om/status/878075081204711424\">Om</a> for the tweet-love. ❤️ ",
|
||||
"pubDate": "Fri, 23 Jun 2017 11:50:48 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a070648",
|
||||
"source:outline": {
|
||||
"text": "Thank you <a href=\"https://twitter.com/om/status/878075081204711424\">Om</a> for the tweet-love. :heart: ",
|
||||
"created": "Fri, 23 Jun 2017 11:50:48 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070648"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/23.html#a120634",
|
||||
"description": "<a href=\"https://twitter.com/davewiner/status/878279505453826049\">An idea worth RT'ing</a>: \"A site people could go to, fill in some info about themselves, and find out how much they would lose under the Repub plan.\"",
|
||||
"pubDate": "Fri, 23 Jun 2017 16:02:34 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a120634",
|
||||
"source:outline": {
|
||||
"text": "<a href=\"https://twitter.com/davewiner/status/878279505453826049\">An idea worth RT'ing</a>: \"A site people could go to, fill in some info about themselves, and find out how much they would lose under the Repub plan.\"",
|
||||
"created": "Fri, 23 Jun 2017 16:02:34 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a120634"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/23.html#a090638",
|
||||
"description": "<a href=\"https://github.com/scripting/githubpub/blob/master/README.md\">githubpub</a> is a Node app that serves from GitHub repositories.",
|
||||
"pubDate": "Fri, 23 Jun 2017 13:52:38 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a090638",
|
||||
"source:outline": {
|
||||
"text": "\"githubpub\" is a Node app that serves from GitHub repositories.",
|
||||
"created": "Fri, 23 Jun 2017 13:52:38 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a090638"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/23.html#a100602",
|
||||
"description": "Fix for the Scripting News <a href=\"http://scripting.com/rss.xml\">RSS</a> <a href=\"http://scripting.com/rss.json\">feed</a>: we now process glossary entries and emoji short codes. The net effect is that text shortcuts like <a href=\"http://cyber.law.harvard.edu/rss/rss.html\">RSS</a> will be expanded as well as 👏 emoji 👏, in the feeds. As they say, <i>still diggin!</i>",
|
||||
"pubDate": "Fri, 23 Jun 2017 14:52:02 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a100602",
|
||||
"source:outline": {
|
||||
"text": "Fix for the Scripting News <a href=\"http://scripting.com/rss.xml\">RSS</a> <a href=\"http://scripting.com/rss.json\">feed</a>: we now process glossary entries and emoji short codes. The net effect is that text shortcuts like \"rss\" will be expanded as well as :clap: emoji :clap:, in the feeds. As they say, <i>still diggin!</i>",
|
||||
"created": "Fri, 23 Jun 2017 14:52:02 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100602"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/23.html#a070659",
|
||||
"description": "So many thoughts today begin with -- If the Dems only had their shit together. ",
|
||||
"pubDate": "Fri, 23 Jun 2017 11:52:59 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a070659",
|
||||
"source:outline": {
|
||||
"text": "So many thoughts today begin with -- If the Dems only had their shit together. ",
|
||||
"created": "Fri, 23 Jun 2017 11:52:59 GMT",
|
||||
"type": "tweet",
|
||||
"tweetId": "878220762343133184",
|
||||
"tweetUserName": "davewiner",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070659"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/23.html#a070624",
|
||||
"description": "If the Dems only had their shit together, we would be mobleizable to knock on neighbor's doors this weekend with pre-written talking points. \"Did you know that you <s>could</s> will lose your health insurance if the Republicans have their way?\"",
|
||||
"pubDate": "Fri, 23 Jun 2017 11:53:24 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a070624",
|
||||
"source:outline": {
|
||||
"text": "If the Dems only had their shit together, we would be mobleizable to knock on neighbor's doors this weekend with pre-written talking points. \"Did you know that you <s>could</s> will lose your health insurance if the Republicans have their way?\"",
|
||||
"created": "Fri, 23 Jun 2017 11:53:24 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070624"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/23.html#a070633",
|
||||
"description": "Repubs who consider voting for the repeal of Medicaid and the ACA should fear the <a href=\"https://en.wikipedia.org/wiki/Hell\">hellfire</a> they will face when they run for re-election. <i>This weekend</i> is the time to make your feelings felt. ",
|
||||
"pubDate": "Fri, 23 Jun 2017 11:47:33 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a070633",
|
||||
"source:outline": {
|
||||
"text": "Repubs who consider voting for the repeal of Medicaid and the ACA should fear the <a href=\"https://en.wikipedia.org/wiki/Hell\">hellfire</a> they will face when they run for re-election. <i>This weekend</i> is the time to make your feelings felt. ",
|
||||
"created": "Fri, 23 Jun 2017 11:47:33 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070633"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/23.html#a070641",
|
||||
"description": "BTW, if someone expresses frustration it doesn't follow that they blame you, esp on Twitter which is a length-impaired medium. ",
|
||||
"pubDate": "Fri, 23 Jun 2017 11:45:41 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a070641",
|
||||
"source:outline": {
|
||||
"text": "BTW, if someone expresses frustration it doesn't follow that they blame you, esp on Twitter which is a length-impaired medium. ",
|
||||
"created": "Fri, 23 Jun 2017 11:45:41 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070641"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Thin servers",
|
||||
"link": "http://scripting.com/2017/06/23.html#a100620",
|
||||
"description": "<p>Two friends, <a href=\"https://twitter.com/judell\">Jon Udell</a> and <a href=\"https://twitter.com/holden\">Mike Caulfield</a>, are talking about \"dumb\" servers. I call the same things \"thin.\" Also fractional-horsepower servers. They go by a bunch of names, but the idea and motivation is the same. </p>\n<p>The idea: We move functionality from the server to the edge (desktop, mobile device), repeating until someday there's nothing left on the server. We could go all the way, but it needs a strong operational backend, something a big company is good at, not so much individuals. (With the caveat that some think this problem is distributable, notably the fictional <a href=\"https://www.crunchbase.com/person/richard-hendriks#/entity\">CEO</a> of <a href=\"http://www.piedpiper.com/\">Pied Piper</a>.)</p>\n<p>The key thing is identity. Once you have that solved, it all becomes relatively easy. I've factored out identity into a layer I call <a href=\"https://github.com/scripting/nodeStorage\">nodeStorage</a>. It associates storage with a user's Twitter identity. Twitter is a good service to use, unlike some others, because they have a liberal policy of who gets to <a href=\"https://apps.twitter.com/app/new\">create</a> apps. Faceook has an extensive vetting process. Twitter <a href=\"https://apps.twitter.com/app/new\">is</a> \"let a thousand flowers bloom.\" I know some people have problems with Twitter, but I've learned over many years that all corporate vendors are imperfect. If you're waiting for <a href=\"http://this.how/standards#1497798834000\">perfection</a> you'll wait forever. And you build the software so that if Twitter should again become draconian, a new service can be filled in with as little disruption as possible.</p>\n<p>Another place I've looked is Dropbox. There, with one simple feature, the ability to associate a domain with a folder, they would solve the problem. I know there are external services that provide something \"like\" this, but fundamentally Dropbox doesn't provide enough flexibility in the API to do this in a reasonable way. (Lack of granularity in permissions, an app gets access to one folder or everything.)</p>\n<p>Or Amazon, if their identity system for AWS were simpler for end users, or if their end-user storage system could be accessed through the S3 API. I'm sure they've thought of it. There must be a reason they don't do it. </p>\n<p>And Twitter could completely eliminate the need for nodeStorage, by offering users a few gigabytes of storage attached to their Twitter account, accessible through the API. The first person who described the feature to me was Jack Dorsey, about eight years ago, when we met for coffee in SF. So he understands why this idea is so powerful. I'm not sure what the holdup is. </p>\n<p>In the meantime, <a href=\"https://github.com/scripting/nodeStorage\">nodeStorage</a> works. I build the kind of apps I want. Open the sidebar on Scripting News (left margin) and have a look at the apps. Some even have <a href=\"https://github.com/scripting/macwrite/blob/master/macwrite.js\">source code</a> so you can see for yourself. </p>\n",
|
||||
"pubDate": "Fri, 23 Jun 2017 14:03:20 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a100620",
|
||||
"source:outline": {
|
||||
"text": "Thin servers",
|
||||
"created": "Fri, 23 Jun 2017 14:03:20 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "Two friends, <a href=\"https://twitter.com/judell\">Jon Udell</a> and <a href=\"https://twitter.com/holden\">Mike Caulfield</a>, are talking about \"dumb\" servers. I call the same things \"thin.\" Also fractional-horsepower servers. They go by a bunch of names, but the idea and motivation is the same. ",
|
||||
"created": "Fri, 23 Jun 2017 14:05:30 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100630"
|
||||
},
|
||||
{
|
||||
"text": "The idea: We move functionality from the server to the edge (desktop, mobile device), repeating until someday there's nothing left on the server. We could go all the way, but it needs a strong operational backend, something a big company is good at, not so much individuals. (With the caveat that some think this problem is distributable, notably the fictional <a href=\"https://www.crunchbase.com/person/richard-hendriks#/entity\">CEO</a> of <a href=\"http://www.piedpiper.com/\">Pied Piper</a>.)",
|
||||
"created": "Fri, 23 Jun 2017 14:24:35 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100635"
|
||||
},
|
||||
{
|
||||
"text": "The key thing is identity. Once you have that solved, it all becomes relatively easy. I've factored out identity into a layer I call \"nodeStorage\". It associates storage with a user's Twitter identity. Twitter is a good service to use, unlike some others, because they have a liberal policy of who gets to <a href=\"https://apps.twitter.com/app/new\">create</a> apps. Faceook has an extensive vetting process. Twitter <a href=\"https://apps.twitter.com/app/new\">is</a> \"let a thousand flowers bloom.\" I know some people have problems with Twitter, but I've learned over many years that all corporate vendors are imperfect. If you're waiting for <a href=\"http://this.how/standards#1497798834000\">perfection</a> you'll wait forever. And you build the software so that if Twitter should again become draconian, a new service can be filled in with as little disruption as possible.",
|
||||
"created": "Fri, 23 Jun 2017 14:05:43 GMT",
|
||||
"image": "http://scripting.com/images/2017/06/17/bowling.png",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100643"
|
||||
},
|
||||
{
|
||||
"text": "Another place I've looked is Dropbox. There, with one simple feature, the ability to associate a domain with a folder, they would solve the problem. I know there are external services that provide something \"like\" this, but fundamentally Dropbox doesn't provide enough flexibility in the API to do this in a reasonable way. (Lack of granularity in permissions, an app gets access to one folder or everything.)",
|
||||
"created": "Fri, 23 Jun 2017 14:08:41 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100641"
|
||||
},
|
||||
{
|
||||
"text": "Or Amazon, if their identity system for AWS were simpler for end users, or if their end-user storage system could be accessed through the S3 API. I'm sure they've thought of it. There must be a reason they don't do it. ",
|
||||
"created": "Fri, 23 Jun 2017 14:09:10 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100610"
|
||||
},
|
||||
{
|
||||
"text": "And Twitter could completely eliminate the need for nodeStorage, by offering users a few gigabytes of storage attached to their Twitter account, accessible through the API. The first person who described the feature to me was Jack Dorsey, about eight years ago, when we met for coffee in SF. So he understands why this idea is so powerful. I'm not sure what the holdup is. ",
|
||||
"created": "Fri, 23 Jun 2017 14:09:55 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100655"
|
||||
},
|
||||
{
|
||||
"text": "In the meantime, \"nodeStorage\" works. I build the kind of apps I want. Open the sidebar on Scripting News (left margin) and have a look at the apps. Some even have <a href=\"https://github.com/scripting/macwrite/blob/master/macwrite.js\">source code</a> so you can see for yourself. ",
|
||||
"created": "Fri, 23 Jun 2017 14:33:10 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100610"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a100620"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "RicMac, part II",
|
||||
"link": "http://scripting.com/2017/06/23.html#a070643",
|
||||
"description": "<p><a href=\"https://twitter.com/ricmac\">Richard MacManus</a> keeps on truckin. There's nothing more powerful than a persistent and curious user who's relatively fearless. </p>\n<p>In a follow-up <a href=\"https://richardmacmanus.com/2017/06/22/openness-rivers-indieweb/\">post</a> I learned that there is an IndieWeb-approved feed reader called <a href=\"https://github.com/kylewm/woodwind\">Woodwind</a>. That's good news. <a href=\"http://cyber.law.harvard.edu/rss/rss.html\">RSS</a> and related technolgies, <a href=\"https://github.com/kylewm/woodwind/issues/66\">including</a> <a href=\"https://github.com/kylewm/woodwind/issues/7\">OPML</a> import and export, are essential components of the open web. </p>\n<p>BTW, to Richard, I wrote up my <a href=\"http://this.how/standards\">rules for standards-makers</a>, based on experience re what (imho) is important and what works and doesn't. Another item for your consideration. </p>\n",
|
||||
"pubDate": "Fri, 23 Jun 2017 11:14:43 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a070643",
|
||||
"source:outline": {
|
||||
"text": "RicMac, part II",
|
||||
"created": "Fri, 23 Jun 2017 11:14:43 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "<a href=\"https://twitter.com/ricmac\">Richard MacManus</a> keeps on truckin. There's nothing more powerful than a persistent and curious user who's relatively fearless. ",
|
||||
"created": "Fri, 23 Jun 2017 11:20:38 GMT",
|
||||
"image": "http://scripting.com/images/2017/06/23/mrNatural.png",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070638"
|
||||
},
|
||||
{
|
||||
"text": "In a follow-up <a href=\"https://richardmacmanus.com/2017/06/22/openness-rivers-indieweb/\">post</a> I learned that there is an IndieWeb-approved feed reader called <a href=\"https://github.com/kylewm/woodwind\">Woodwind</a>. That's good news. \"RSS\" and related technolgies, <a href=\"https://github.com/kylewm/woodwind/issues/66\">including</a> <a href=\"https://github.com/kylewm/woodwind/issues/7\">OPML</a> import and export, are essential components of the open web. ",
|
||||
"created": "Fri, 23 Jun 2017 11:15:54 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070654"
|
||||
},
|
||||
{
|
||||
"text": "BTW, to Richard, I wrote up my <a href=\"http://this.how/standards\">rules for standards-makers</a>, based on experience re what (imho) is important and what works and doesn't. Another item for your consideration. ",
|
||||
"created": "Fri, 23 Jun 2017 11:19:38 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070638"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a070643"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Test post",
|
||||
"link": "http://scripting.com/2017/06/23.html#a030621",
|
||||
"description": "<p>Here's a list with four items</p>\n<ul>\n\t<li>one</li>\n\t<li>two</li>\n\t<li>three</li>\n\t<li>four</li>\n\t</ul>\n",
|
||||
"pubDate": "Fri, 23 Jun 2017 19:48:21 GMT",
|
||||
"guid": "http://scripting.com/2017/06/23.html#a030621",
|
||||
"source:outline": {
|
||||
"text": "Test post",
|
||||
"created": "Fri, 23 Jun 2017 19:48:21 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "Here's a list with four items",
|
||||
"created": "Fri, 23 Jun 2017 19:49:21 GMT",
|
||||
"flNumberedSubs": "true",
|
||||
"subs": [
|
||||
{
|
||||
"text": "one",
|
||||
"created": "Fri, 23 Jun 2017 19:48:43 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a030643"
|
||||
},
|
||||
{
|
||||
"text": "two",
|
||||
"created": "Fri, 23 Jun 2017 19:48:44 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a030644"
|
||||
},
|
||||
{
|
||||
"text": "three",
|
||||
"created": "Fri, 23 Jun 2017 19:48:45 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a030645"
|
||||
},
|
||||
{
|
||||
"text": "four",
|
||||
"created": "Fri, 23 Jun 2017 19:48:46 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a030646"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a030621"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/23.html#a030621"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/22.html#a120633",
|
||||
"description": "I'm working on a new Node web server that serves out of GitHub repos. It's a very sweet very small piece of software. ",
|
||||
"pubDate": "Thu, 22 Jun 2017 16:28:33 GMT",
|
||||
"guid": "http://scripting.com/2017/06/22.html#a120633",
|
||||
"source:outline": {
|
||||
"text": "I'm working on a new Node web server that serves out of GitHub repos. It's a very sweet very small piece of software. ",
|
||||
"created": "Thu, 22 Jun 2017 16:28:33 GMT",
|
||||
"type": "tweet",
|
||||
"tweetId": "877926320822464512",
|
||||
"tweetUserName": "davewiner",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a120633"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/22.html#a090616",
|
||||
"description": "An epiphany. Mark Zuckerberg is his generation's <a href=\"https://en.wikipedia.org/wiki/Ray_Kroc\">Ray Kroc</a>, and Facebook is <a href=\"https://www.mcdonalds.com/us/en-us.html\">McDonald's</a>. I aspire to be <a href=\"https://en.wikipedia.org/wiki/Alice_Waters\">Alice Waters</a>.",
|
||||
"pubDate": "Thu, 22 Jun 2017 13:29:16 GMT",
|
||||
"guid": "http://scripting.com/2017/06/22.html#a090616",
|
||||
"source:outline": {
|
||||
"text": "An epiphany. Mark Zuckerberg is his generation's <a href=\"https://en.wikipedia.org/wiki/Ray_Kroc\">Ray Kroc</a>, and Facebook is <a href=\"https://www.mcdonalds.com/us/en-us.html\">McDonald's</a>. I aspire to be <a href=\"https://en.wikipedia.org/wiki/Alice_Waters\">Alice Waters</a>.",
|
||||
"created": "Thu, 22 Jun 2017 13:29:16 GMT",
|
||||
"type": "tweet",
|
||||
"tweetId": "877881227298000896",
|
||||
"tweetUserName": "davewiner",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a090616"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/22.html#a090644",
|
||||
"description": "Future-of-journalism conferences that ignore blogging are not about the future of journalism.",
|
||||
"pubDate": "Thu, 22 Jun 2017 13:29:44 GMT",
|
||||
"guid": "http://scripting.com/2017/06/22.html#a090644",
|
||||
"source:outline": {
|
||||
"text": "Future-of-journalism conferences that ignore blogging are not about the future of journalism.",
|
||||
"created": "Thu, 22 Jun 2017 13:29:44 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a090644"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/22.html#a120629",
|
||||
"description": "News will be interesting tonight. They've got the Repub health care <a href=\"https://www.axios.com/on-health-care-moderates-quiet-while-conservatives-put-their-foot-down-2446375738.html\">bill</a> to rip apart, and it's also <a href=\"http://nbariver.com/\">NBA Draft</a> night. ",
|
||||
"pubDate": "Thu, 22 Jun 2017 16:27:29 GMT",
|
||||
"guid": "http://scripting.com/2017/06/22.html#a120629",
|
||||
"source:outline": {
|
||||
"text": "News will be interesting tonight. They've got the Repub health care <a href=\"https://www.axios.com/on-health-care-moderates-quiet-while-conservatives-put-their-foot-down-2446375738.html\">bill</a> to rip apart, and it's also <a href=\"http://nbariver.com/\">NBA Draft</a> night. ",
|
||||
"created": "Thu, 22 Jun 2017 16:27:29 GMT",
|
||||
"type": "tweet",
|
||||
"tweetId": "877926057260728320",
|
||||
"tweetUserName": "davewiner",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a120629"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Fargo puzzler",
|
||||
"link": "http://scripting.com/2017/06/22.html#a030610",
|
||||
"description": "<p>The last two episodes of <a href=\"https://en.wikipedia.org/wiki/Fargo_(season_3)\">season 3</a> of Fargo were fantastic. But, the opening scene of episode 1, which takes place in a police office in East Germany during the Cold War, is without explanation. </p>\n<p>All through the season, I was wondering how it was going to be connected up with the story that takes place in Minnesota in 2011, but as far as I know it never was.</p>\n<p>Maybe that was VM Varga as the accused? Or the police guy?</p>\n<p>This is kind of bothering me! :-)</p>\n<p>Okay then...</p>\n<p>Update: In the <a href=\"https://en.wikipedia.org/wiki/Fargo_(season_3)#Episodes\">episode guide</a> on Wikipedia they describe the opening scene as follows: \"In 1988 East Berlin, Jacob Ungerleider is questioned in the death of a woman, which he claims is a case of mistaken identity.\" So it's not VM Varga in the hot seat. Who is Jacob Ungerleider? I have no idea! ;-)</p>\n",
|
||||
"pubDate": "Thu, 22 Jun 2017 19:26:10 GMT",
|
||||
"guid": "http://scripting.com/2017/06/22.html#a030610",
|
||||
"source:outline": {
|
||||
"text": "Fargo puzzler",
|
||||
"created": "Thu, 22 Jun 2017 19:26:10 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "The last two episodes of <a href=\"https://en.wikipedia.org/wiki/Fargo_(season_3)\">season 3</a> of Fargo were fantastic. But, the opening scene of episode 1, which takes place in a police office in East Germany during the Cold War, is without explanation. ",
|
||||
"created": "Thu, 22 Jun 2017 19:26:18 GMT",
|
||||
"image": "http://scripting.com/images/2017/06/22/paulBunyan.png",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a030618"
|
||||
},
|
||||
{
|
||||
"text": "All through the season, I was wondering how it was going to be connected up with the story that takes place in Minnesota in 2011, but as far as I know it never was.",
|
||||
"created": "Thu, 22 Jun 2017 19:27:20 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a030620"
|
||||
},
|
||||
{
|
||||
"text": "Maybe that was VM Varga as the accused? Or the police guy?",
|
||||
"created": "Thu, 22 Jun 2017 19:27:54 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a030654"
|
||||
},
|
||||
{
|
||||
"text": "This is kind of bothering me! :-)",
|
||||
"created": "Thu, 22 Jun 2017 19:28:15 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a030615"
|
||||
},
|
||||
{
|
||||
"text": "Okay then...",
|
||||
"created": "Thu, 22 Jun 2017 19:28:27 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a030627"
|
||||
},
|
||||
{
|
||||
"text": "Update: In the <a href=\"https://en.wikipedia.org/wiki/Fargo_(season_3)#Episodes\">episode guide</a> on Wikipedia they describe the opening scene as follows: \"In 1988 East Berlin, Jacob Ungerleider is questioned in the death of a woman, which he claims is a case of mistaken identity.\" So it's not VM Varga in the hot seat. Who is Jacob Ungerleider? I have no idea! ;-)",
|
||||
"created": "Thu, 22 Jun 2017 19:38:25 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a030625"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/22.html#a030610"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/21.html#a060647",
|
||||
"description": "Good morning summer solstice fans!",
|
||||
"pubDate": "Wed, 21 Jun 2017 10:21:47 GMT",
|
||||
"guid": "http://scripting.com/2017/06/21.html#a060647",
|
||||
"source:outline": {
|
||||
"text": "Good morning summer solstice fans!",
|
||||
"created": "Wed, 21 Jun 2017 10:21:47 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060647"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/21.html#a060611",
|
||||
"description": "It was a boring NBA postseason, for the most. But the excitement of next season is already starting, with the draft tomorrow, and deal season in full swing. The place to find all the news is <a href=\"http://nbariver.com/\">nbariver.com</a>. It's one of many rivers maintained by my <a href=\"https://github.com/scripting/river5\">River5</a> installation. ",
|
||||
"pubDate": "Wed, 21 Jun 2017 10:22:11 GMT",
|
||||
"guid": "http://scripting.com/2017/06/21.html#a060611",
|
||||
"source:outline": {
|
||||
"text": "It was a boring NBA postseason, for the most. But the excitement of next season is already starting, with the draft tomorrow, and deal season in full swing. The place to find all the news is <a href=\"http://nbariver.com/\">nbariver.com</a>. It's one of many rivers maintained by my \"River5\" installation. ",
|
||||
"created": "Wed, 21 Jun 2017 10:22:11 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060611"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/21.html#a060632",
|
||||
"description": "BTW, I hate the term \"eating the dogfood.\" As much as I love dogs, it says that our users are pets, not sentient human beings, our equals. It also says our software is dog food. I think as a kid, as an experiment, a few of us kids actually ate dog food. It's a vague memory, that must have some basis in reality. It makes me nauseous to think about it. And that's what I think about when I hear the term. Please, let's find another way of saying \"My software is good because I use it, and vice versa.\"",
|
||||
"pubDate": "Wed, 21 Jun 2017 10:57:32 GMT",
|
||||
"guid": "http://scripting.com/2017/06/21.html#a060632",
|
||||
"source:outline": {
|
||||
"text": "BTW, I hate the term \"eating the dogfood.\" As much as I love dogs, it says that our users are pets, not sentient human beings, our equals. It also says our software is dog food. I think as a kid, as an experiment, a few of us kids actually ate dog food. It's a vague memory, that must have some basis in reality. It makes me nauseous to think about it. And that's what I think about when I hear the term. Please, let's find another way of saying \"My software is good because I use it, and vice versa.\"",
|
||||
"created": "Wed, 21 Jun 2017 10:57:32 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060632"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/21.html#a070633",
|
||||
"description": "Yesterday I posted a <a href=\"http://scripting.com/images/2017/06/21/frozenBlogPost.png\">screen shot</a> of one of my posts on Facebook, to accolades from friends on Facebook. I deleted the post. I won't be doing it again. Facebook is not a place for blog posts. Not as long as they disable linking, styles, titles and podcasts. If you want to help Facebook destroy the open web, go for it. But I will not participate in that awful adventure.",
|
||||
"pubDate": "Wed, 21 Jun 2017 11:01:33 GMT",
|
||||
"guid": "http://scripting.com/2017/06/21.html#a070633",
|
||||
"source:outline": {
|
||||
"text": "Yesterday I posted a <a href=\"http://scripting.com/images/2017/06/21/frozenBlogPost.png\">screen shot</a> of one of my posts on Facebook, to accolades from friends on Facebook. I deleted the post. I won't be doing it again. Facebook is not a place for blog posts. Not as long as they disable linking, styles, titles and podcasts. If you want to help Facebook destroy the open web, go for it. But I will not participate in that awful adventure.",
|
||||
"created": "Wed, 21 Jun 2017 11:01:33 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a070633"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/21.html#a060600",
|
||||
"description": "On Facebook you are who the algorithm says you are. ",
|
||||
"pubDate": "Wed, 21 Jun 2017 10:53:00 GMT",
|
||||
"guid": "http://scripting.com/2017/06/21.html#a060600",
|
||||
"source:outline": {
|
||||
"text": "On Facebook you are who the algorithm says you are. ",
|
||||
"created": "Wed, 21 Jun 2017 10:53:00 GMT",
|
||||
"type": "outline",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060600"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "GitHub API example app",
|
||||
"link": "http://scripting.com/2017/06/21.html#a110614",
|
||||
"description": "<p>A simple <a href=\"http://scripting.com/misc/code/githubapi/directory.html\">web app</a> that travels through the <a href=\"https://github.com/scripting/river5\">River5 repository</a> in my GitHub account, producing a directory that reflects the structure of the repo.</p>\n<p>I couldn't <a href=\"https://duckduckgo.com/?q=github+api+example+app+directory+repository&t=h_&ia=web\">find</a> sample code that does this simple thing. Now I won't have to hunt for it, and neither will you. ;-) </p>\n<p>Here's the <a href=\"https://gist.github.com/scripting/f5e5b3a175265f47fda098cb5bddca2f\">source code</a>. </p>\n",
|
||||
"pubDate": "Wed, 21 Jun 2017 15:18:14 GMT",
|
||||
"guid": "http://scripting.com/2017/06/21.html#a110614",
|
||||
"source:outline": {
|
||||
"text": "GitHub API example app",
|
||||
"created": "Wed, 21 Jun 2017 15:18:14 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "A simple <a href=\"http://scripting.com/misc/code/githubapi/directory.html\">web app</a> that travels through the <a href=\"https://github.com/scripting/river5\">River5 repository</a> in my GitHub account, producing a directory that reflects the structure of the repo.",
|
||||
"created": "Wed, 21 Jun 2017 15:18:24 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a110624"
|
||||
},
|
||||
{
|
||||
"text": "I couldn't <a href=\"https://duckduckgo.com/?q=github+api+example+app+directory+repository&t=h_&ia=web\">find</a> sample code that does this simple thing. Now I won't have to hunt for it, and neither will you. ;-) ",
|
||||
"created": "Wed, 21 Jun 2017 15:18:36 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a110636"
|
||||
},
|
||||
{
|
||||
"text": "Here's the <a href=\"https://gist.github.com/scripting/f5e5b3a175265f47fda098cb5bddca2f\">source code</a>. ",
|
||||
"created": "Wed, 21 Jun 2017 15:19:39 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a110639"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a110614"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Bike video from two years ago",
|
||||
"link": "http://scripting.com/2017/06/21.html#a020637",
|
||||
"description": "<p><iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/iWvdXdW7GiI\" frameborder=\"0\" allowfullscreen></iframe></p>\n",
|
||||
"pubDate": "Wed, 21 Jun 2017 18:47:37 GMT",
|
||||
"guid": "http://scripting.com/2017/06/21.html#a020637",
|
||||
"source:outline": {
|
||||
"text": "Bike video from two years ago",
|
||||
"created": "Wed, 21 Jun 2017 18:47:37 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/iWvdXdW7GiI\" frameborder=\"0\" allowfullscreen></iframe>",
|
||||
"created": "Wed, 21 Jun 2017 18:47:50 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a020650"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a020637"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "An old friend: Richard MacManus",
|
||||
"link": "http://scripting.com/2017/06/21.html#a060651",
|
||||
"description": "<p>Richard is one of the old school bloggers. He started <a href=\"https://web.archive.org/web/20031014183603/http://readwriteweb.com\">ReadWriteWeb</a> in 2003. It started as a Radio UserLand project and grew into a leading tech publication, something which I'm personally proud of. </p>\n<p>He has a <a href=\"http://altplatform.org/2017/06/20/building-a-blogroll-in-2017/\">new blog</a> up and running. I've added it to my <a href=\"http://scripting.com/river.html\">personal river</a> here on Scripting News. He asks about where the blogrolls have gone, a topic I <a href=\"http://scripting.com/2017/06/18.html#a110612\">wrote about</a> a couple of days ago. Richard would certainly be in my blogroll.</p>\n<p>Maybe the subscription list <a href=\"http://scripting.com/2016/09/23/otherBlogsLikeScriptingNews.html\">for</a> my <a href=\"http://bloggers.scripting.com/\">blogger's river</a> would make a good start for my blogroll, or vice versa? Something we didn't do in the first iteration is make our rivers public. Nowadays I'm doing that routinely. A few examples are in the left sidebar here on <a href=\"http://scripting.com/\">Scripting News</a>. </p>\n<p>Richard has turned to <a href=\"https://indieweb.org/\">IndieWeb</a> for the latest on open web tech. That's fine, but you have to look elsewhere too, because as he's discovered, they only embrace part of the open web. It's too bad they chose such an inclusive name, but have an exclusive approach. For example, they have avoided <a href=\"http://cyber.law.harvard.edu/rss/rss.html\">RSS</a>, for reasons I'm sure I don't understand (I've listened, so no need to repeat the reasoning). We need all the advantages we can get because there are <a href=\"http://scripting.com/2017/06/18.html#a090658\">serious</a> <a href=\"http://scripting.com/2016/10/03/turnsOutLessFacebookIsOk.html\">headwinds</a> these days for blogging. RSS is serious open web technology. To not build on it is unthinkable, for me at least. </p>\n<p>Re integration between writing and reading, another topic of interest to Richard, all my rivers <a href=\"http://scripting.com/images/2017/06/21/postingFromARiver.png\">hook</a> into <a href=\"http://radio3.io/\">Radio3</a>, which is my latest <a href=\"http://scripting.com/links.html\">linkblogging</a> tool. For reading, I encouraged Richard to look at <a href=\"http://this.how/electricRiver\">Electric River</a>, it's the closest to what Radio UserLand did with aggregation in 2002. It runs on your Mac desktop, as the original did. When he wants to go all-in with rivers, nothing can take the place of <a href=\"https://github.com/scripting/river5\">River5</a>, which is getting both modular and deep. I'm doing more work on that. Rivers have not finished evolving as far as I'm concerned. </p>\n",
|
||||
"pubDate": "Wed, 21 Jun 2017 10:29:51 GMT",
|
||||
"guid": "http://scripting.com/2017/06/21.html#a060651",
|
||||
"source:outline": {
|
||||
"text": "An old friend: Richard MacManus",
|
||||
"created": "Wed, 21 Jun 2017 10:29:51 GMT",
|
||||
"type": "outline",
|
||||
"subs": [
|
||||
{
|
||||
"text": "Richard is one of the old school bloggers. He started <a href=\"https://web.archive.org/web/20031014183603/http://readwriteweb.com\">ReadWriteWeb</a> in 2003. It started as a Radio UserLand project and grew into a leading tech publication, something which I'm personally proud of. ",
|
||||
"created": "Wed, 21 Jun 2017 10:33:59 GMT",
|
||||
"image": "http://scripting.com/2016/03/10/clown.png",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060659"
|
||||
},
|
||||
{
|
||||
"text": "He has a <a href=\"http://altplatform.org/2017/06/20/building-a-blogroll-in-2017/\">new blog</a> up and running. I've added it to my <a href=\"http://scripting.com/river.html\">personal river</a> here on Scripting News. He asks about where the blogrolls have gone, a topic I <a href=\"http://scripting.com/2017/06/18.html#a110612\">wrote about</a> a couple of days ago. Richard would certainly be in my blogroll.",
|
||||
"created": "Wed, 21 Jun 2017 10:25:16 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060616"
|
||||
},
|
||||
{
|
||||
"text": "Maybe the subscription list <a href=\"http://scripting.com/2016/09/23/otherBlogsLikeScriptingNews.html\">for</a> my <a href=\"http://bloggers.scripting.com/\">blogger's river</a> would make a good start for my blogroll, or vice versa? Something we didn't do in the first iteration is make our rivers public. Nowadays I'm doing that routinely. A few examples are in the left sidebar here on \"Scripting News\". ",
|
||||
"created": "Wed, 21 Jun 2017 10:40:31 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060631"
|
||||
},
|
||||
{
|
||||
"text": "Richard has turned to <a href=\"https://indieweb.org/\">IndieWeb</a> for the latest on open web tech. That's fine, but you have to look elsewhere too, because as he's discovered, they only embrace part of the open web. It's too bad they chose such an inclusive name, but have an exclusive approach. For example, they have avoided \"RSS\", for reasons I'm sure I don't understand (I've listened, so no need to repeat the reasoning). We need all the advantages we can get because there are <a href=\"http://scripting.com/2017/06/18.html#a090658\">serious</a> <a href=\"http://scripting.com/2016/10/03/turnsOutLessFacebookIsOk.html\">headwinds</a> these days for blogging. RSS is serious open web technology. To not build on it is unthinkable, for me at least. ",
|
||||
"created": "Wed, 21 Jun 2017 10:30:33 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060633"
|
||||
},
|
||||
{
|
||||
"text": "Re integration between writing and reading, another topic of interest to Richard, all my rivers <a href=\"http://scripting.com/images/2017/06/21/postingFromARiver.png\">hook</a> into \"Radio3\", which is my latest <a href=\"http://scripting.com/links.html\">linkblogging</a> tool. For reading, I encouraged Richard to look at \"Electric River\", it's the closest to what Radio UserLand did with aggregation in 2002. It runs on your Mac desktop, as the original did. When he wants to go all-in with rivers, nothing can take the place of \"River5\", which is getting both modular and deep. I'm doing more work on that. Rivers have not finished evolving as far as I'm concerned. ",
|
||||
"created": "Wed, 21 Jun 2017 10:32:27 GMT",
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060627"
|
||||
}
|
||||
],
|
||||
"permalink": "http://scripting.com/2017/06/21.html#a060651"
|
||||
}
|
||||
},
|
||||
{
|
||||
"link": "http://scripting.com/2017/06/20.html#a100647",
|
||||
"description": "Anyone want to blog-debate about XML vs JSON? I've spent years using both, I think I have an objective view of the strengths of each. Imho, they are almost the same thing. XML has attributes and values, and that does make it more complex. Slightly. But you don't have to use the extra features. Look at <a href=\"http://dev.opml.org/spec2.html\">OPML</a> for an idea of a simple very JSON-like application of XML. Beyond that, there's really no difference. If you disagree, write a post, link to this and send me the link. I will read what you wrote, and respond, on <a href=\"http://scripting.com/\">my blog</a>, if I have something to say. There's been so much bullshit flying around. I'd like to cut through that. ",
|
||||
"pubDate": "Wed, 21 Jun 2017 02:56:47 GMT",
|
||||
"guid": "http://scripting.com/2017/06/20.html#a100647",
|
||||
"source:outline": {
|
||||
"text": "Anyone want to blog-debate about XML vs JSON? I've spent years using both, I think I have an objective view of the strengths of each. Imho, they are almost the same thing. XML has attributes and values, and that does make it more complex. Slightly. But you don't have to use the extra features. Look at \"OPML\" for an idea of a simple very JSON-like application of XML. Beyond that, there's really no difference. If you disagree, write a post, link to this and send me the link. I will read what you wrote, and respond, on <a href=\"http://scripting.com/\">my blog</a>, if I have something to say. There's been so much bullshit flying around. I'd like to cut through that. ",
|
||||
"created": "Wed, 21 Jun 2017 02:56:47 GMT",
|
||||
"type": "outline",
|
||||
"image": "http://scripting.com/images/2017/06/20/penny.png",
|
||||
"permalink": "http://scripting.com/2017/06/20.html#a100647"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
223
Parser/Tests/ParserTests/Resources/Subs.opml
Executable file
223
Parser/Tests/ParserTests/Resources/Subs.opml
Executable file
@ -0,0 +1,223 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<opml version="1.1">
|
||||
<head>
|
||||
<title>Subs</title>
|
||||
</head>
|
||||
<body>
|
||||
<outline text="Daring Fireball" title="Daring Fireball" description="" type="rss" version="RSS" htmlUrl="http://daringfireball.net/" xmlUrl="http://daringfireball.net/feeds/main"/>
|
||||
<outline text="Swift Blog - Apple Developer" title="Swift Blog - Apple Developer" description="" type="rss" version="RSS" htmlUrl="https://developer.apple.com/swift/blog/" xmlUrl="https://developer.apple.com/swift/blog/news.rss"/>
|
||||
<outline text="Julia Evans" title="Julia Evans" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://jvns.ca/atom.xml"/>
|
||||
<outline text="Erica Sadun" title="Erica Sadun" description="" type="rss" version="RSS" htmlUrl="http://ericasadun.com" xmlUrl="http://ericasadun.com/feed/"/>
|
||||
<outline text="chat & code" title="chat & code" description="" type="rss" version="RSS" htmlUrl="http://corinnekrych.blogspot.com/" xmlUrl="http://corinnekrych.blogspot.com/feeds/posts/default"/>
|
||||
<outline text="never a straight line" title="never a straight line" description="" type="rss" version="RSS" htmlUrl="http://www.blog.juliaferraioli.com/" xmlUrl="http://www.blog.juliaferraioli.com/feeds/posts/default"/>
|
||||
<outline text="jaimeejaimee" title="jaimeejaimee" description="" type="rss" version="RSS" htmlUrl="https://medium.com/@jaimeejaimee?source=rss-11d5cc4494a2------2" xmlUrl="https://medium.com/feed/@jaimeejaimee"/>
|
||||
<outline text="Accidentally in Code" title="Accidentally in Code" description="" type="rss" version="RSS" htmlUrl="https://cate.blog" xmlUrl="http://www.catehuston.com/blog/feed/"/>
|
||||
<outline text="Feral Scrutiny" title="Feral Scrutiny" description="" type="rss" version="RSS" htmlUrl="http://feralscrutiny.co" xmlUrl="http://feralscrutiny.co/feed/"/>
|
||||
<outline text="Doctor Who" title="Doctor Who" description="" type="rss" version="RSS" htmlUrl="http://www.bbc.co.uk/blogs/doctorwho" xmlUrl="http://www.bbc.co.uk/blogs/doctorwho/rss"/>
|
||||
<outline text="jessysaurusrex" title="jessysaurusrex" description="" type="rss" version="RSS" htmlUrl="https://jessysaurusrex.com" xmlUrl="http://jessysaurusrex.com/feed/"/>
|
||||
<outline text="One Foot Tsunami" title="One Foot Tsunami" description="" type="rss" version="RSS" htmlUrl="http://onefoottsunami.com" xmlUrl="http://onefoottsunami.com/feed/atom/"/>
|
||||
<outline text="Loop Insight" title="Loop Insight" description="" type="rss" version="RSS" htmlUrl="http://www.loopinsight.com" xmlUrl="http://www.loopinsight.com/feed/"/>
|
||||
<outline text="The World is not a desktop" title="The World is not a desktop" description="" type="rss" version="RSS" htmlUrl="http://caseorganic.com" xmlUrl="http://caseorganic.com/feed/"/>
|
||||
<outline text="Pointers Gone Wild" title="Pointers Gone Wild" description="" type="rss" version="RSS" htmlUrl="https://pointersgonewild.com" xmlUrl="http://pointersgonewild.com/feed/"/>
|
||||
<outline text="iMore" title="iMore" description="" type="rss" version="RSS" htmlUrl="http://www.imore.com/" xmlUrl="http://www.imore.com/rss.xml"/>
|
||||
<outline text="The Incrementalist." title="The Incrementalist." description="" type="rss" version="RSS" htmlUrl="https://incrementalistblog.wordpress.com" xmlUrl="https://incrementalistblog.wordpress.com/feed/"/>
|
||||
<outline text="Virginia Roberts" title="Virginia Roberts" description="" type="rss" version="RSS" htmlUrl="http://www.virginiaroberts.com" xmlUrl="http://www.virginiaroberts.com/feed/"/>
|
||||
<outline text="Inessential" title="Inessential" description="" type="rss" version="RSS" htmlUrl="http://inessential.com/" xmlUrl="http://inessential.com/xml/rss.xml"/>
|
||||
<outline text="scattered thoughts" title="scattered thoughts" description="" type="rss" version="RSS" htmlUrl="http://blog.nicoleblee.com" xmlUrl="http://blog.nicoleblee.com/feed/"/>
|
||||
<outline text="Rebecca Miller-Webster" title="Rebecca Miller-Webster" description="" type="rss" version="RSS" htmlUrl="http://www.rebeccamiller-webster.com" xmlUrl="http://www.rebeccamiller-webster.com/feed/"/>
|
||||
<outline text="Ellen's Blog" title="Ellen's Blog" description="" type="rss" version="RSS" htmlUrl="https://blog.ellenchisa.com?source=rss----da542b929da2---4" xmlUrl="http://blog.ellenchisa.com/feed/"/>
|
||||
<outline text="Backup Brain" title="Backup Brain" description="" type="rss" version="RSS" htmlUrl="http://www.backupbrain.com" xmlUrl="http://www.backupbrain.com/feed/"/>
|
||||
<outline text="Katie Floyd" title="Katie Floyd" description="" type="rss" version="RSS" htmlUrl="http://www.katiefloyd.com" xmlUrl="http://feed.katiefloyd.com"/>
|
||||
<outline text="The Shape of Everything" title="The Shape of Everything" description="" type="rss" version="RSS" htmlUrl="http://shapeof.com/" xmlUrl="http://shapeof.com/rss.xml"/>
|
||||
<outline text="Sasha Laundy" title="Sasha Laundy" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://blog.sashalaundy.com/atom.xml"/>
|
||||
<outline text="Feed: Thoughtbrain Bloggers" title="Feed: Thoughtbrain Bloggers" description="" type="rss" version="RSS" htmlUrl="http://blog.thoughtbrain.com" xmlUrl="http://blog.thoughtbrain.com/feed/"/>
|
||||
<outline text="MarcySutton.com" title="MarcySutton.com" description="" type="rss" version="RSS" htmlUrl="https://marcysutton.com" xmlUrl="http://marcysutton.com/feed/"/>
|
||||
<outline text="Aleen Mean" title="Aleen Mean" description="" type="rss" version="RSS" htmlUrl="https://aleenmean.com/" xmlUrl="http://www.aleenmean.com/feed.xml"/>
|
||||
<outline text="Michele Titolo's Blog" title="Michele Titolo's Blog" description="" type="rss" version="RSS" htmlUrl="http://michele.io/feed" xmlUrl="http://www.michele.io//feed"/>
|
||||
<outline text="Ashley Nelson-Hornstein" title="Ashley Nelson-Hornstein" description="" type="rss" version="RSS" htmlUrl="http://ashleynh.me:80/" xmlUrl="http://blog.ashleynh.me/rss/"/>
|
||||
<outline text="Veronica Ray on Medium" title="Veronica Ray on Medium" description="" type="rss" version="RSS" htmlUrl="https://medium.com/@nerdonica?source=rss-eaf18ccd367f------2" xmlUrl="https://medium.com/feed/@nerdonica"/>
|
||||
<outline text="Blog - App Camp For Girls" title="Blog - App Camp For Girls" description="" type="rss" version="RSS" htmlUrl="http://appcamp4girls.com/blog/" xmlUrl="http://appcamp4girls.com/blog?format=RSS"/>
|
||||
<outline text="The Future Is Now" title="The Future Is Now" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.mistys-internet.website/blog/atom.xml"/>
|
||||
<outline text="Learn Swift ↯" title="Learn Swift ↯" description="" type="rss" version="RSS" htmlUrl="http://swift.ayaka.me/" xmlUrl="http://swift.ayaka.me/posts?format=RSS"/>
|
||||
<outline text="Everything in Context" title="Everything in Context" description="" type="rss" version="RSS" htmlUrl="http://lambdamaphone.blogspot.com/" xmlUrl="http://lambdamaphone.blogspot.com/feeds/posts/default"/>
|
||||
<outline text="Natasha the Robot" title="Natasha the Robot" description="" type="rss" version="RSS" htmlUrl="https://www.natashatherobot.com" xmlUrl="https://www.natashatherobot.com/feed/"/>
|
||||
<outline text="Katie Floyd" title="Katie Floyd" description="" type="rss" version="RSS" htmlUrl="http://www.katiefloyd.com" xmlUrl="http://feed.katiefloyd.com/"/>
|
||||
<outline text="Meagan Waller" title="Meagan Waller" description="" type="rss" version="RSS" htmlUrl="http://meaganwaller.com" xmlUrl="http://meaganwaller.com/index.php/feed/"/>
|
||||
<outline text="Susan × Blog" title="Susan × Blog" description="" type="rss" version="RSS" htmlUrl="http://sketch.bysusanlin.com/" xmlUrl="http://sketch.bysusanlin.com/rss"/>
|
||||
<outline text="go ahead, mac my day" title="go ahead, mac my day" description="" type="rss" version="RSS" htmlUrl="http://www.nadynerichmond.com/blog" xmlUrl="http://www.nadynerichmond.com/blog/feed/"/>
|
||||
<outline text="don't panic" title="don't panic" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://timekl.com/atom.xml"/>
|
||||
<outline text="MechanicalGirl" title="MechanicalGirl" description="" type="rss" version="RSS" htmlUrl="http://www.MechanicalGirl.com/" xmlUrl="http://www.mechanicalgirl.com/feeds/all/"/>
|
||||
<outline text="BAD YEWEX" title="BAD YEWEX" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://feeds.feedburner.com/BadYewex"/>
|
||||
<outline text="Becky Hansmeyer" title="Becky Hansmeyer" description="" type="rss" version="RSS" htmlUrl="http://beckyhansmeyer.com" xmlUrl="http://beckyhansmeyer.com/feed/"/>
|
||||
<outline text="KateHeddleston.com Blog Posts" title="KateHeddleston.com Blog Posts" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="https://kateheddleston.com/blog/feed.atom"/>
|
||||
<outline text="The Record" title="The Record" description="" type="rss" version="RSS" htmlUrl="http://therecord.co/" xmlUrl="http://therecord.co/xml/rss.xml"/>
|
||||
<outline text="Grok Swift" title="Grok Swift" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="https://grokswift.com/feed/index.xml"/>
|
||||
<outline text="Inspired Mouse" title="Inspired Mouse" description="" type="rss" version="RSS" htmlUrl="https://inspiredmouse.com" xmlUrl="https://inspiredmouse.com/feed/"/>
|
||||
<outline text="Tess Rinearson on Medium" title="Tess Rinearson on Medium" description="" type="rss" version="RSS" htmlUrl="https://medium.com/@tessr?source=rss-c16152863954------2" xmlUrl="https://medium.com/feed/@tessr"/>
|
||||
<outline text="Liz Marley on Medium" title="Liz Marley on Medium" description="" type="rss" version="RSS" htmlUrl="https://medium.com/@emarley?source=rss-b4981c59ffa5------2" xmlUrl="https://medium.com/feed/@emarley"/>
|
||||
<outline text="All The Flow" title="All The Flow" description="" type="rss" version="RSS" htmlUrl="http://blog.alltheflow.com/" xmlUrl="https://blog.alltheflow.com/rss/"/>
|
||||
<outline text="kt zine — Medium" title="kt zine — Medium" description="" type="rss" version="RSS" htmlUrl="https://ktzine.com?source=rss----7097752c9303---4" xmlUrl="https://ktzine.com/feed"/>
|
||||
<outline text="Daring Fireball" title="Daring Fireball" description="" type="rss" version="RSS" htmlUrl="http://daringfireball.net/" xmlUrl="http://daringfireball.net/index.xml"/>
|
||||
<outline text="Blog Posts About Stuff" title="Blog Posts About Stuff" description="" type="rss" version="RSS" htmlUrl="http://nothe.purplellamas.net/index.xml" xmlUrl="http://nothe.purplellamas.net/index.xml"/>
|
||||
<outline text="Six Colors" title="Six Colors" description="" type="rss" version="RSS" htmlUrl="https://www.sixcolors.com/" xmlUrl="http://feedpress.me/sixcolors"/>
|
||||
<outline text="mostgood" title="mostgood" description="" type="rss" version="RSS" htmlUrl="http://www.mostgood.net/" xmlUrl="http://www.mostgood.net/blog?format=RSS"/>
|
||||
<outline text="cocoa by the fire" title="cocoa by the fire" description="" type="rss" version="RSS" htmlUrl="http://blog.cocoabythefire.com/" xmlUrl="http://blog.cocoabythefire.com/rss"/>
|
||||
<outline text="ranchero.com" title="ranchero.com" description="" type="rss" version="RSS" htmlUrl="http://ranchero.com/" xmlUrl="http://ranchero.com/xml/rss.xml"/>
|
||||
<outline text="Linda Dong" title="Linda Dong" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.lindadong.com/blog?format=RSS"/>
|
||||
<outline text="Eryn Wells" title="Eryn Wells" description="" type="rss" version="RSS" htmlUrl="http://blog.erynwells.me/" xmlUrl="http://blog.erynwells.me/rss"/>
|
||||
<outline text="The Red Queen Coder" title="The Red Queen Coder" description="" type="rss" version="RSS" htmlUrl="http://redqueencoder.com" xmlUrl="http://redqueencoder.com/feed/"/>
|
||||
<outline text="nataliepo (posts on 'nataliepo' (rss 2.0))" title="nataliepo (posts on 'nataliepo' (rss 2.0))" description="" type="rss" version="RSS" htmlUrl="http://nataliepo.typepad.com/nataliepo/" xmlUrl="http://nataliepo.typepad.com/nataliepo/rss.xml"/>
|
||||
<outline text="Scripting News" title="Scripting News" description="" type="rss" version="RSS" htmlUrl="http://scripting.com/" xmlUrl="http://scripting.com/rss.xml"/>
|
||||
<outline text="Designated Nerd" title="Designated Nerd" description="" type="rss" version="RSS" htmlUrl="http://designatednerd.com" xmlUrl="http://designatednerd.com/feed/"/>
|
||||
<outline text="kristinathai.com" title="kristinathai.com" description="" type="rss" version="RSS" htmlUrl="https://kristina.io" xmlUrl="http://www.kristinathai.com/feed/"/>
|
||||
<outline text="Natasha The Robot" title="Natasha The Robot" description="" type="rss" version="RSS" htmlUrl="https://www.natashatherobot.com" xmlUrl="http://natashatherobot.com/feed/"/>
|
||||
<outline text="Samantha Marshall's Blog" title="Samantha Marshall's Blog" description="" type="rss" version="RSS" htmlUrl="http://pewpewthespells.com/" xmlUrl="http://pewpewthespells.com/feed.xml"/>
|
||||
<outline text="Ballard" title="Ballard" description="" type="rss" version="RSS" htmlUrl="http://www.myballard.com" xmlUrl="http://www.myballard.com/feed/"/>
|
||||
<outline text="Programming" title="Programming">
|
||||
<outline text="iOS Unit Testing" title="iOS Unit Testing" description="" type="rss" version="RSS" htmlUrl="http://iosunittesting.com" xmlUrl="http://iosunittesting.com/feed/"/>
|
||||
<outline text="A List Apart: The Full Feed" title="A List Apart: The Full Feed" description="" type="rss" version="RSS" htmlUrl="http://alistapart.com" xmlUrl="http://feeds.feedburner.com/alistapart/main"/>
|
||||
<outline text="Swift Programming — Medium" title="Swift Programming — Medium" description="" type="rss" version="RSS" htmlUrl="https://medium.com/swift-programming?source=rss----5396e0e8bc29---4" xmlUrl="https://medium.com/feed/swift-programming"/>
|
||||
<outline text="The Confusatory" title="The Confusatory" description="" type="rss" version="RSS" htmlUrl="http://confusatory.org/" xmlUrl="http://confusatory.org/rss"/>
|
||||
<outline text="Debuggers" title="Debuggers" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://debuggers.co/atom.xml"/>
|
||||
<outline text="We ❤ Swift" title="We ❤ Swift" description="" type="rss" version="RSS" htmlUrl="https://www.weheartswift.com" xmlUrl="http://www.weheartswift.com/feed/"/>
|
||||
<outline text="Airspeed Velocity" title="Airspeed Velocity" description="" type="rss" version="RSS" htmlUrl="https://airspeedvelocity.net" xmlUrl="http://airspeedvelocity.net/feed/"/>
|
||||
<outline text="The blog of Tony Arnold" title="The blog of Tony Arnold" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://tonyarnold.com/atom.xml"/>
|
||||
<outline text="Code by Kevin" title="Code by Kevin" description="" type="rss" version="RSS" htmlUrl="http://www.codebykevin.com/blosxom.cgi" xmlUrl="http://www.codebykevin.com/blosxom.cgi/index.rss"/>
|
||||
<outline text="Programming in the 21st Century" title="Programming in the 21st Century" description="" type="rss" version="RSS" htmlUrl="http://prog21.dadgum.com/" xmlUrl="http://prog21.dadgum.com/atom.xml"/>
|
||||
<outline text="What Amy Did" title="What Amy Did" description="" type="rss" version="RSS" htmlUrl="http://blog.amyworrall.com/" xmlUrl="http://blog.amyworrall.com/rss"/>
|
||||
<outline text="Russ Bishop (atom)" title="Russ Bishop (atom)" description="" type="rss" version="RSS" htmlUrl="http://www.russbishop.net" xmlUrl="http://www.russbishop.net/feed"/>
|
||||
<outline text="The Mental Blog" title="The Mental Blog" description="" type="rss" version="RSS" htmlUrl="http://mentalfaculty.tumblr.com/" xmlUrl="http://mentalfaculty.tumblr.com/rss"/>
|
||||
<outline text="Swift Studies" title="Swift Studies" description="" type="rss" version="RSS" htmlUrl="http://www.swift-studies.com/" xmlUrl="http://www.swift-studies.com/blog?format=RSS"/>
|
||||
<outline text="owensd.io - thoughts in and out - Articles" title="owensd.io - thoughts in and out - Articles" description="" type="rss" version="RSS" htmlUrl="https://owensd.io" xmlUrl="http://owensd.io/rss.xml"/>
|
||||
<outline text="Swift Yeti" title="Swift Yeti" description="" type="rss" version="RSS" htmlUrl="http://swiftyeti.com/" xmlUrl="http://swiftyeti.com/rss/"/>
|
||||
<outline text="Cocoaphony" title="Cocoaphony" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://robnapier.net/atom.xml"/>
|
||||
<outline text="Damien DeVille" title="Damien DeVille" description="" type="rss" version="RSS" htmlUrl="http://ddeville.me" xmlUrl="http://ddeville.me/feed.xml"/>
|
||||
<outline text="Cocoa Manifest" title="Cocoa Manifest" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://cocoamanifest.net/feeds/index.xml"/>
|
||||
<outline text="Indie Stack" title="Indie Stack" description="" type="rss" version="RSS" htmlUrl="http://indiestack.com" xmlUrl="http://indiestack.com/feed/"/>
|
||||
<outline text="[macoscope blog]" title="[macoscope blog]" description="" type="rss" version="RSS" htmlUrl="http://macoscope.com/blog" xmlUrl="http://macoscope.com/blog/feed/"/>
|
||||
<outline text="Ole Begemann: iOS Development" title="Ole Begemann: iOS Development" description="" type="rss" version="RSS" htmlUrl="https://oleb.net/blog/" xmlUrl="http://oleb.net/blog/atom.xml"/>
|
||||
<outline text="David J Peacock - iOS Blog" title="David J Peacock - iOS Blog" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://davidjpeacock.ca/atom.xml"/>
|
||||
<outline text="Ray Wenderlich" title="Ray Wenderlich" description="" type="rss" version="RSS" htmlUrl="https://www.raywenderlich.com" xmlUrl="http://www.raywenderlich.com/feed"/>
|
||||
<outline text="Peter Steinberger" title="Peter Steinberger" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://petersteinberger.com/atom.xml"/>
|
||||
<outline text="Cocoa Is My Girlfriend" title="Cocoa Is My Girlfriend" description="" type="rss" version="RSS" htmlUrl="http://www.cimgf.com" xmlUrl="http://www.cimgf.com/feed/"/>
|
||||
<outline text="Subjective-C" title="Subjective-C" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://subjc.com/atom.xml"/>
|
||||
<outline text="the Joy of Code" title="the Joy of Code" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://feeds.feedburner.com/thejoyofcode/"/>
|
||||
<outline text="New Yankee Codeshop" title="New Yankee Codeshop" description="" type="rss" version="RSS" htmlUrl="http://newyankeecodeshop.tumblr.com/" xmlUrl="http://newyankeecodeshop.tumblr.com/rss"/>
|
||||
<outline text="Borkware Miniblog" title="Borkware Miniblog" description="" type="rss" version="RSS" htmlUrl="https://borkwarellc.wordpress.com" xmlUrl="http://borkware.com/miniblog/rss/rss.xml"/>
|
||||
<outline text="NSHipster" title="NSHipster" description="" type="rss" version="RSS" htmlUrl="http://nshipster.com" xmlUrl="http://nshipster.com/feed.xml"/>
|
||||
<outline text="Pilky.me" title="Pilky.me" description="" type="rss" version="RSS" htmlUrl="http://pilky.me/" xmlUrl="http://feeds.feedburner.com/pilkyme"/>
|
||||
<outline text="Big Nerd Ranch Blog" title="Big Nerd Ranch Blog" description="" type="rss" version="RSS" htmlUrl="https://www.bignerdranch.com/" xmlUrl="http://blog.bignerdranch.com/feed/"/>
|
||||
</outline>
|
||||
<outline text="Macintosh" title="Macintosh">
|
||||
<outline text="Macalope" title="Macalope" description="" type="rss" version="RSS" htmlUrl="http://www.macalope.com" xmlUrl="http://www.macalope.com/feed/"/>
|
||||
<outline text="9to5Mac" title="9to5Mac" description="" type="rss" version="RSS" htmlUrl="https://9to5mac.com" xmlUrl="http://9to5mac.com/feed/"/>
|
||||
<outline text="Macdrifter" title="Macdrifter" description="" type="rss" version="RSS" htmlUrl="http://www.macdrifter.com/" xmlUrl="http://www.macdrifter.com/feeds/all.atom.xml"/>
|
||||
<outline text="TidBITS: Apple News for the Rest of Us" title="TidBITS: Apple News for the Rest of Us" description="" type="rss" version="RSS" htmlUrl="http://tidbits.com/" xmlUrl="http://tidbits.com/feeds/tidbits_blurb.rss"/>
|
||||
<outline text="The Flying Meat Weblog" title="The Flying Meat Weblog" description="" type="rss" version="RSS" htmlUrl="http://flyingmeat.com/blog/" xmlUrl="http://flyingmeat.com/blog/atom.xml"/>
|
||||
</outline>
|
||||
<outline text="Weblogs" title="Weblogs">
|
||||
<outline text="Clark's Tech Blog" title="Clark's Tech Blog" description="" type="rss" version="RSS" htmlUrl="http://www.libertypages.com/clarktech" xmlUrl="http://www.libertypages.com/clarktech/?feed=rss2"/>
|
||||
<outline text="NSBlog" title="NSBlog" description="" type="rss" version="RSS" htmlUrl="http://www.mikeash.com/pyblog/" xmlUrl="http://www.mikeash.com/pyblog/rss.py"/>
|
||||
<outline text="metablog" title="metablog" description="" type="rss" version="RSS" htmlUrl="http://blog.metaobject.com/" xmlUrl="http://blog.metaobject.com/feeds/posts/default"/>
|
||||
<outline text="Better Elevation" title="Better Elevation" description="" type="rss" version="RSS" htmlUrl="http://betterelevation.com" xmlUrl="http://betterelevation.com/feed/"/>
|
||||
<outline text="Allen Pike" title="Allen Pike" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.allenpike.com/feed/"/>
|
||||
<outline text="Secure Mac Programming" title="Secure Mac Programming" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://blog.securemacprogramming.com/feed/"/>
|
||||
<outline text="iPhone Developer News" title="iPhone Developer News" description="" type="rss" version="RSS" htmlUrl="https://developer.apple.com/news/" xmlUrl="https://developer.apple.com/news/rss/news.rss"/>
|
||||
<outline text="David Smith" title="David Smith" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://david-smith.org/atom.xml"/>
|
||||
<outline text="Liss is More" title="Liss is More" description="" type="rss" version="RSS" htmlUrl="https://www.caseyliss.com" xmlUrl="http://www.caseyliss.com/rss"/>
|
||||
<outline text="Codeplease" title="Codeplease" description="" type="rss" version="RSS" htmlUrl="http://codeplease.io/" xmlUrl="http://codeplease.io/rss/"/>
|
||||
<outline text="furbo.org" title="furbo.org" description="" type="rss" version="RSS" htmlUrl="http://furbo.org" xmlUrl="http://furbo.org/feed/"/>
|
||||
<outline text="Very Web. Such Blog. Wow." title="Very Web. Such Blog. Wow." description="" type="rss" version="RSS" htmlUrl="http://brian-webster.tumblr.com/" xmlUrl="http://brian-webster.tumblr.com/rss"/>
|
||||
<outline text="bryan i/o" title="bryan i/o" description="" type="rss" version="RSS" htmlUrl="http://bryan.io/" xmlUrl="http://bryan.io/rss"/>
|
||||
<outline text="Nick Bradbury" title="Nick Bradbury" description="" type="rss" version="RSS" htmlUrl="https://nickbradbury.com" xmlUrl="http://nickbradbury.com/feed/"/>
|
||||
<outline text="Gordon Meyer (posts on 'gordon meyer' (atom))" title="Gordon Meyer (posts on 'gordon meyer' (atom))" description="" type="rss" version="RSS" htmlUrl="http://www.gordonmeyer.com/" xmlUrl="http://www.gordonmeyer.com/atom.xml"/>
|
||||
<outline text="Rhonabwy" title="Rhonabwy" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.rhonabwy.com/wp/feed/"/>
|
||||
<outline text="Typeset In The Future" title="Typeset In The Future" description="" type="rss" version="RSS" htmlUrl="https://typesetinthefuture.com" xmlUrl="http://typesetinthefuture.com/feed/"/>
|
||||
<outline text="Neglected Potential" title="Neglected Potential" description="" type="rss" version="RSS" htmlUrl="http://www.neglectedpotential.com" xmlUrl="http://www.neglectedpotential.com/feed/"/>
|
||||
<outline text="Informal Protocol" title="Informal Protocol" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://informalprotocol.com/atom.xml"/>
|
||||
<outline text="NSHipster" title="NSHipster" description="" type="rss" version="RSS" htmlUrl="http://nshipster.com" xmlUrl="http://feeds.feedburner.com/NSHipster"/>
|
||||
<outline text="Daniel Jalkut" title="Daniel Jalkut" description="" type="rss" version="RSS" htmlUrl="http://bitsplitting.org" xmlUrl="http://bitsplitting.org/feed/"/>
|
||||
<outline text="Ash Furrow" title="Ash Furrow" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://ashfurrow.com/blog?format=rss"/>
|
||||
<outline text="Jared Sinclair" title="Jared Sinclair" description="" type="rss" version="RSS" htmlUrl="http://blog.jaredsinclair.com/" xmlUrl="http://blog.jaredsinclair.com/rss?1"/>
|
||||
<outline text="Doug Russell" title="Doug Russell" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.takingnotes.co/atom.xml"/>
|
||||
<outline text="JakeSavin.com" title="JakeSavin.com" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.jakesavin.com/xml/rss.xml"/>
|
||||
<outline text="Apple Outsider" title="Apple Outsider" description="" type="rss" version="RSS" htmlUrl="http://www.appleoutsider.com" xmlUrl="http://www.appleoutsider.com/feed/"/>
|
||||
<outline text="Nackblog" title="Nackblog" description="" type="rss" version="RSS" htmlUrl="http://jnack.com/blog" xmlUrl="http://jnack.com/blog/?feed=rss2"/>
|
||||
<outline text="ignorethecode.net" title="ignorethecode.net" description="" type="rss" version="RSS" htmlUrl="http://ignorethecode.net" xmlUrl="http://ignorethecode.net/blog/rss/"/>
|
||||
<outline text="Sheila's Weblog" title="Sheila's Weblog" description="" type="rss" version="RSS" htmlUrl="https://sheilasweblog.wordpress.com" xmlUrl="http://sheilasweblog.wordpress.com/feed/"/>
|
||||
<outline text="The Fine Edge" title="The Fine Edge" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://nicemohawk.com/atom.xml"/>
|
||||
<outline text="Rands In Repose" title="Rands In Repose" description="" type="rss" version="RSS" htmlUrl="http://randsinrepose.com" xmlUrl="http://www.randsinrepose.com/index.xml"/>
|
||||
<outline text="John Nack on Adobe (rss (feedburner))" title="John Nack on Adobe (rss (feedburner))" description="" type="rss" version="RSS" htmlUrl="http://blogs.adobe.com/jnack" xmlUrl="http://feeds2.feedburner.com/adobe/jnack"/>
|
||||
<outline text="Dan Gillmor" title="Dan Gillmor" description="" type="rss" version="RSS" htmlUrl="http://dangillmor.com" xmlUrl="http://dangillmor.com/feed/"/>
|
||||
<outline text="Corporation Unknown" title="Corporation Unknown" description="" type="rss" version="RSS" htmlUrl="http://corporationunknown.com/blog" xmlUrl="http://corporationunknown.com/blog/feed/"/>
|
||||
<outline text="Dalton Caldwell" title="Dalton Caldwell" description="" type="rss" version="RSS" htmlUrl="http://daltoncaldwell.com" xmlUrl="http://daltoncaldwell.com/feed"/>
|
||||
<outline text="level of indirection" title="level of indirection" description="" type="rss" version="RSS" htmlUrl="http://www.levelofindirection.com/journal/" xmlUrl="http://www.levelofindirection.com/journal/rss.xml"/>
|
||||
<outline text="I Am Simme" title="I Am Simme" description="" type="rss" version="RSS" htmlUrl="http://iamsim.me" xmlUrl="http://iamsim.me/rss/"/>
|
||||
<outline text="Collin Donnell (collin donnell » feed)" title="Collin Donnell (collin donnell » feed)" description="" type="rss" version="RSS" htmlUrl="http://collindonnell.com" xmlUrl="http://feedpress.me/collindonnell"/>
|
||||
<outline text="Sci-Fi Hi-Fi: Weblog" title="Sci-Fi Hi-Fi: Weblog" description="" type="rss" version="RSS" htmlUrl="http://log.scifihifi.com/" xmlUrl="http://log.scifihifi.com/rss"/>
|
||||
<outline text="Adventures in Newfield" title="Adventures in Newfield" description="" type="rss" version="RSS" htmlUrl="http://adventuresinnewfield.blogspot.com/" xmlUrl="http://adventuresinnewfield.blogspot.com/feeds/posts/default"/>
|
||||
<outline text="The Desolation of Blog" title="The Desolation of Blog" description="" type="rss" version="RSS" htmlUrl="http://lapcatsoftware.com/articles/index.html" xmlUrl="http://lapcatsoftware.com/articles/atom.xml"/>
|
||||
<outline text="Jesper" title="Jesper" description="" type="rss" version="RSS" htmlUrl="http://stmts.net" xmlUrl="http://stmts.net/feed/"/>
|
||||
<outline text="RatHole" title="RatHole" description="" type="rss" version="RSS" htmlUrl="http://rathole.tumblr.com/" xmlUrl="http://rathole.tumblr.com/rss"/>
|
||||
<outline text="Jeff McLeman" title="Jeff McLeman" description="" type="rss" version="RSS" htmlUrl="http://www.jeffmcleman.com/blog" xmlUrl="http://www.jeffmcleman.com/blog/feed/"/>
|
||||
<outline text="frozendevil" title="frozendevil" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://frozendevil.com/atom.xml"/>
|
||||
<outline text="Zathras.de - Uli's most useless blog in the World" title="Zathras.de - Uli's most useless blog in the World" description="" type="rss" version="RSS" htmlUrl="http://orangejuiceliberationfront.com" xmlUrl="http://www.zathras.de/angelweb/BlogRSSFeed.rss"/>
|
||||
<outline text="Monday Note" title="Monday Note" description="" type="rss" version="RSS" htmlUrl="https://mondaynote.com?source=rss----c537d80ed0a---4" xmlUrl="http://www.mondaynote.com/feed/"/>
|
||||
<outline text="Michael Tsai" title="Michael Tsai" description="" type="rss" version="RSS" htmlUrl="http://mjtsai.com/blog" xmlUrl="http://mjtsai.com/blog/feed/"/>
|
||||
<outline text="James Dempsey" title="James Dempsey" description="" type="rss" version="RSS" htmlUrl="http://jamesdempsey.net" xmlUrl="http://jamesdempsey.net/feed/"/>
|
||||
<outline text="Red Sweater" title="Red Sweater" description="" type="rss" version="RSS" htmlUrl="https://red-sweater.com/blog" xmlUrl="http://www.red-sweater.com/blog/feed"/>
|
||||
<outline text="Peter Hosey" title="Peter Hosey" description="" type="rss" version="RSS" htmlUrl="http://boredzo.org/blog" xmlUrl="http://feeds.feedburner.com/domainofthebored"/>
|
||||
<outline text="Use Your Loaf" title="Use Your Loaf" description="" type="rss" version="RSS" htmlUrl="http://useyourloaf.com/blog/" xmlUrl="http://useyourloaf.com/blog/rss.xml"/>
|
||||
<outline text="The Main Thread" title="The Main Thread" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://themainthread.com/feed.xml"/>
|
||||
<outline text="Awkward Hare" title="Awkward Hare" description="" type="rss" version="RSS" htmlUrl="http://awkwardhare.com/" xmlUrl="http://awkwardhare.com/rss"/>
|
||||
<outline text="Journal (atom)" title="Journal (atom)" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.curtclifton.net/journal/atom.xml"/>
|
||||
<outline text="Blog - Jury.me" title="Blog - Jury.me" description="" type="rss" version="RSS" htmlUrl="http://jury.me/blog/" xmlUrl="http://jury.me/blog?format=rss"/>
|
||||
<outline text="Call Me Fishmeal." title="Call Me Fishmeal." description="" type="rss" version="RSS" htmlUrl="http://blog.wilshipley.com/" xmlUrl="http://blog.wilshipley.com/rss.xml"/>
|
||||
<outline text="Hal Mueller's Blog" title="Hal Mueller's Blog" description="" type="rss" version="RSS" htmlUrl="https://halmueller.wordpress.com" xmlUrl="http://halmueller.wordpress.com/feed/"/>
|
||||
<outline text="And now it’s all this" title="And now it’s all this" description="" type="rss" version="RSS" htmlUrl="http://leancrew.com/all-this" xmlUrl="http://www.leancrew.com/all-this/feed/"/>
|
||||
<outline text="OneThirtySeven" title="OneThirtySeven" description="" type="rss" version="RSS" htmlUrl="http://one37.net/blog/" xmlUrl="http://one37.net/blog?format=rss"/>
|
||||
<outline text="rentzsch.tumblr.com" title="rentzsch.tumblr.com" description="" type="rss" version="RSS" htmlUrl="http://rentzsch.tumblr.com/" xmlUrl="http://rentzsch.tumblr.com/rss"/>
|
||||
<outline text="⌥⇧K" title="⌥⇧K" description="" type="rss" version="RSS" htmlUrl="http://optshiftk.com" xmlUrl="http://optshiftk.com/feed/"/>
|
||||
<outline text="Sam Ruby" title="Sam Ruby" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://intertwingly.net/blog/index.atom"/>
|
||||
<outline text="literator.me" title="literator.me" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://literator.me/rss"/>
|
||||
<outline text="upbeat.it" title="upbeat.it" description="" type="rss" version="RSS" htmlUrl="http://www.upbeat.it/" xmlUrl="http://www.upbeat.it/atom.xml"/>
|
||||
<outline text="Minutes to Midnight" title="Minutes to Midnight" description="" type="rss" version="RSS" htmlUrl="http://minutestomidnight.net/" xmlUrl="http://minutestomidnight.net/blog?format=RSS"/>
|
||||
<outline text="John Moltz's Very Nice Web Site" title="John Moltz's Very Nice Web Site" description="" type="rss" version="RSS" htmlUrl="http://verynicewebsite.net" xmlUrl="http://verynicewebsite.net/feed/atom/"/>
|
||||
<outline text="Matt Mullenweg" title="Matt Mullenweg" description="" type="rss" version="RSS" htmlUrl="https://ma.tt" xmlUrl="http://ma.tt/feed/"/>
|
||||
<outline text="BrettTerpstra.com" title="BrettTerpstra.com" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://brettterpstra.com/atom.xml"/>
|
||||
<outline text="jwz" title="jwz" description="" type="rss" version="RSS" htmlUrl="https://www.jwz.org/blog/" xmlUrl="http://www.jwz.org/blog/feed/"/>
|
||||
<outline text="Maniacal Rage" title="Maniacal Rage" description="" type="rss" version="RSS" htmlUrl="http://log.maniacalrage.net/" xmlUrl="http://log.maniacalrage.net/rss"/>
|
||||
<outline text="Shawn Blanc" title="Shawn Blanc" description="" type="rss" version="RSS" htmlUrl="http://shawnblanc.net" xmlUrl="http://shawnblanc.net/feed/"/>
|
||||
<outline text="carpeaqua" title="carpeaqua" description="" type="rss" version="RSS" htmlUrl="https://carpeaqua.com" xmlUrl="http://feeds.feedburner.com/carpeaqua"/>
|
||||
<outline text="Doug Russell" title="Doug Russell" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.getitdownonpaper.com/atom.xml"/>
|
||||
<outline text="bbum's weblog-o-mat" title="bbum's weblog-o-mat" description="" type="rss" version="RSS" htmlUrl="http://www.friday.com/bbum" xmlUrl="http://www.friday.com/bbum/feed/"/>
|
||||
<outline text="Fraser Speirs" title="Fraser Speirs" description="" type="rss" version="RSS" htmlUrl="http://www.speirs.org/" xmlUrl="http://speirs.org/feed/"/>
|
||||
<outline text="ridiculous_fish" title="ridiculous_fish" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://pammon.webfactional.com/blog/feed/"/>
|
||||
<outline text="Hypercritical" title="Hypercritical" description="" type="rss" version="RSS" htmlUrl="http://hypercritical.co/" xmlUrl="http://hypercritical.co/feeds/main"/>
|
||||
<outline text="Manton Reece" title="Manton Reece" description="" type="rss" version="RSS" htmlUrl="http://www.manton.org" xmlUrl="http://manton.org/rss.xml"/>
|
||||
<outline text="Gus's weblog" title="Gus's weblog" description="" type="rss" version="RSS" htmlUrl="http://shapeof.com/" xmlUrl="http://gusmueller.com/blog/atom.xml"/>
|
||||
<outline text="Dustin Curtis" title="Dustin Curtis" description="" type="rss" version="RSS" htmlUrl="https://dcurt.is" xmlUrl="http://feeds.feedburner.com/dcurtis"/>
|
||||
<outline text="Waffle" title="Waffle" description="" type="rss" version="RSS" htmlUrl="http://waffle.wootest.net" xmlUrl="http://waffle.wootest.net/feed/atom/"/>
|
||||
<outline text="Mistitled" title="Mistitled" description="" type="rss" version="RSS" htmlUrl="http://rms2.tumblr.com/" xmlUrl="http://rms2.tumblr.com/rss"/>
|
||||
<outline text="Don Melton" title="Don Melton" description="" type="rss" version="RSS" htmlUrl="https://donmelton.com" xmlUrl="http://donmelton.com/rss.xml"/>
|
||||
<outline text="Anil Dash" title="Anil Dash" description="" type="rss" version="RSS" htmlUrl="http://anildash.com/" xmlUrl="http://feeds.dashes.com/AnilDash"/>
|
||||
<outline text="Neven Mrgan's tumbl" title="Neven Mrgan's tumbl" description="" type="rss" version="RSS" htmlUrl="http://mrgan.tumblr.com/" xmlUrl="http://mrgan.tumblr.com/rss"/>
|
||||
<outline text="alexking.org: Blog" title="alexking.org: Blog" description="" type="rss" version="RSS" htmlUrl="http://alexking.org" xmlUrl="http://alexking.org/blog/feed"/>
|
||||
<outline text="ParisLemon" title="ParisLemon" description="" type="rss" version="RSS" htmlUrl="http://parislemon.com/" xmlUrl="http://parislemon.com/rss"/>
|
||||
<outline text="Black Pixel" title="Black Pixel" description="" type="rss" version="RSS" htmlUrl="https://blackpixel.com/writing/" xmlUrl="http://blackpixel.com/blog/atom.xml"/>
|
||||
<outline text="Marco.org" title="Marco.org" description="" type="rss" version="RSS" htmlUrl="https://marco.org/" xmlUrl="http://www.marco.org/rss"/>
|
||||
<outline text="The Guinea Pig in the Cocoa Mine" title="The Guinea Pig in the Cocoa Mine" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://cocoamine.net/atom.xml"/>
|
||||
<outline text="Blog | Mike Abdullah" title="Blog | Mike Abdullah" description="" type="rss" version="RSS" htmlUrl="http://mikeabdullah.net/" xmlUrl="http://www.mikeabdullah.net/index.xml"/>
|
||||
<outline text="Kickingbear Blog" title="Kickingbear Blog" description="" type="rss" version="RSS" htmlUrl="http://kickingbear.com/blog" xmlUrl="http://kickingbear.com/blog/feed"/>
|
||||
<outline text="Stuart Hall" title="Stuart Hall" description="" type="rss" version="RSS" htmlUrl="http://stuartkhall.com/" xmlUrl="http://feeds.feedburner.com/stuartkhall"/>
|
||||
<outline text="ongoing" title="ongoing" description="" type="rss" version="RSS" htmlUrl="" xmlUrl="http://www.tbray.org/ongoing/ongoing.atom"/>
|
||||
<outline text="Stratēchery" title="Stratēchery" description="" type="rss" version="RSS" htmlUrl="https://stratechery.com" xmlUrl="http://stratechery.com/feed/"/>
|
||||
<outline text="Mark Bernstein" title="Mark Bernstein" description="" type="rss" version="RSS" htmlUrl="http://markbernstein.org/" xmlUrl="http://www.markbernstein.org/news.rss"/>
|
||||
</outline>
|
||||
<outline text="Writers" title="Writers">
|
||||
<outline text="Whatever" title="Whatever" description="" type="rss" version="RSS" htmlUrl="http://whatever.scalzi.com" xmlUrl="http://whatever.scalzi.com/feed/"/>
|
||||
<outline text="Charlie's Diary" title="Charlie's Diary" description="" type="rss" version="RSS" htmlUrl="http://www.antipope.org/charlie/blog-static/" xmlUrl="http://www.antipope.org/charlie/blog-static/atom.xml"/>
|
||||
<outline text="Gerrold" title="Gerrold" description="" type="rss" version="RSS" htmlUrl="http://www.gerrold.com" xmlUrl="http://www.gerrold.com/feed/"/>
|
||||
</outline>
|
||||
</body>
|
||||
</opml>
|
237
Parser/Tests/ParserTests/Resources/SubsNoTitleAttributes.opml
Executable file
237
Parser/Tests/ParserTests/Resources/SubsNoTitleAttributes.opml
Executable file
@ -0,0 +1,237 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<opml version="2.0">
|
||||
<head><!-- <editor>
|
||||
<sidebar/>
|
||||
<column name="text" width="150"/>
|
||||
<column name="description" width="150"/>
|
||||
<column name="type" width="150"/>
|
||||
<column name="version" width="150"/>
|
||||
<column name="htmlUrl" width="150"/>
|
||||
<column name="xmlUrl" width="150"/>
|
||||
</editor> -->
|
||||
<title>Subs</title>
|
||||
<expansionState></expansionState>
|
||||
<vertScrollState>0</vertScrollState>
|
||||
<windowTop>472</windowTop>
|
||||
<windowLeft>502</windowLeft>
|
||||
<windowRight>1182</windowRight>
|
||||
<windowBottom>1252</windowBottom>
|
||||
</head>
|
||||
<body>
|
||||
<outline text="Daring Fireball" type="rss" version="RSS" htmlUrl="http://daringfireball.net/" xmlUrl="http://daringfireball.net/feeds/main"/>
|
||||
<outline text="Swift Blog - Apple Developer" type="rss" version="RSS" htmlUrl="https://developer.apple.com/swift/blog/" xmlUrl="https://developer.apple.com/swift/blog/news.rss"/>
|
||||
<outline text="Julia Evans" type="rss" version="RSS" xmlUrl="http://jvns.ca/atom.xml"/>
|
||||
<outline text="Erica Sadun" type="rss" version="RSS" htmlUrl="http://ericasadun.com" xmlUrl="http://ericasadun.com/feed/"/>
|
||||
<outline text="chat & code" type="rss" version="RSS" htmlUrl="http://corinnekrych.blogspot.com/" xmlUrl="http://corinnekrych.blogspot.com/feeds/posts/default"/>
|
||||
<outline text="never a straight line" type="rss" version="RSS" htmlUrl="http://www.blog.juliaferraioli.com/" xmlUrl="http://www.blog.juliaferraioli.com/feeds/posts/default"/>
|
||||
<outline text="jaimeejaimee" type="rss" version="RSS" htmlUrl="https://medium.com/@jaimeejaimee?source=rss-11d5cc4494a2------2" xmlUrl="https://medium.com/feed/@jaimeejaimee"/>
|
||||
<outline text="Accidentally in Code" type="rss" version="RSS" htmlUrl="https://cate.blog" xmlUrl="http://www.catehuston.com/blog/feed/"/>
|
||||
<outline text="Feral Scrutiny" type="rss" version="RSS" htmlUrl="http://feralscrutiny.co" xmlUrl="http://feralscrutiny.co/feed/"/>
|
||||
<outline text="Doctor Who" type="rss" version="RSS" htmlUrl="http://www.bbc.co.uk/blogs/doctorwho" xmlUrl="http://www.bbc.co.uk/blogs/doctorwho/rss"/>
|
||||
<outline text="jessysaurusrex" type="rss" version="RSS" htmlUrl="https://jessysaurusrex.com" xmlUrl="http://jessysaurusrex.com/feed/"/>
|
||||
<outline text="One Foot Tsunami" type="rss" version="RSS" htmlUrl="http://onefoottsunami.com" xmlUrl="http://onefoottsunami.com/feed/atom/"/>
|
||||
<outline text="Loop Insight" type="rss" version="RSS" htmlUrl="http://www.loopinsight.com" xmlUrl="http://www.loopinsight.com/feed/"/>
|
||||
<outline text="The World is not a desktop" type="rss" version="RSS" htmlUrl="http://caseorganic.com" xmlUrl="http://caseorganic.com/feed/"/>
|
||||
<outline text="Pointers Gone Wild" type="rss" version="RSS" htmlUrl="https://pointersgonewild.com" xmlUrl="http://pointersgonewild.com/feed/"/>
|
||||
<outline text="iMore" type="rss" version="RSS" htmlUrl="http://www.imore.com/" xmlUrl="http://www.imore.com/rss.xml"/>
|
||||
<outline text="The Incrementalist." type="rss" version="RSS" htmlUrl="https://incrementalistblog.wordpress.com" xmlUrl="https://incrementalistblog.wordpress.com/feed/"/>
|
||||
<outline text="Virginia Roberts" type="rss" version="RSS" htmlUrl="http://www.virginiaroberts.com" xmlUrl="http://www.virginiaroberts.com/feed/"/>
|
||||
<outline text="Inessential" type="rss" version="RSS" htmlUrl="http://inessential.com/" xmlUrl="http://inessential.com/xml/rss.xml"/>
|
||||
<outline text="scattered thoughts" type="rss" version="RSS" htmlUrl="http://blog.nicoleblee.com" xmlUrl="http://blog.nicoleblee.com/feed/"/>
|
||||
<outline text="Rebecca Miller-Webster" type="rss" version="RSS" htmlUrl="http://www.rebeccamiller-webster.com" xmlUrl="http://www.rebeccamiller-webster.com/feed/"/>
|
||||
<outline text="Ellen's Blog" type="rss" version="RSS" htmlUrl="https://blog.ellenchisa.com?source=rss----da542b929da2---4" xmlUrl="http://blog.ellenchisa.com/feed/"/>
|
||||
<outline text="Backup Brain" type="rss" version="RSS" htmlUrl="http://www.backupbrain.com" xmlUrl="http://www.backupbrain.com/feed/"/>
|
||||
<outline text="Katie Floyd" type="rss" version="RSS" htmlUrl="http://www.katiefloyd.com" xmlUrl="http://feed.katiefloyd.com"/>
|
||||
<outline text="The Shape of Everything" type="rss" version="RSS" htmlUrl="http://shapeof.com/" xmlUrl="http://shapeof.com/rss.xml"/>
|
||||
<outline text="Sasha Laundy" type="rss" version="RSS" xmlUrl="http://blog.sashalaundy.com/atom.xml"/>
|
||||
<outline text="Feed: Thoughtbrain Bloggers" type="rss" version="RSS" htmlUrl="http://blog.thoughtbrain.com" xmlUrl="http://blog.thoughtbrain.com/feed/"/>
|
||||
<outline text="MarcySutton.com" type="rss" version="RSS" htmlUrl="https://marcysutton.com" xmlUrl="http://marcysutton.com/feed/"/>
|
||||
<outline text="Aleen Mean" type="rss" version="RSS" htmlUrl="https://aleenmean.com/" xmlUrl="http://www.aleenmean.com/feed.xml"/>
|
||||
<outline text="Michele Titolo's Blog" type="rss" version="RSS" htmlUrl="http://michele.io/feed" xmlUrl="http://www.michele.io//feed"/>
|
||||
<outline text="Ashley Nelson-Hornstein" type="rss" version="RSS" htmlUrl="http://ashleynh.me:80/" xmlUrl="http://blog.ashleynh.me/rss/"/>
|
||||
<outline text="Veronica Ray on Medium" type="rss" version="RSS" htmlUrl="https://medium.com/@nerdonica?source=rss-eaf18ccd367f------2" xmlUrl="https://medium.com/feed/@nerdonica"/>
|
||||
<outline text="Blog - App Camp For Girls" type="rss" version="RSS" htmlUrl="http://appcamp4girls.com/blog/" xmlUrl="http://appcamp4girls.com/blog?format=RSS"/>
|
||||
<outline text="The Future Is Now" type="rss" version="RSS" xmlUrl="http://www.mistys-internet.website/blog/atom.xml"/>
|
||||
<outline text="Learn Swift ↯" type="rss" version="RSS" htmlUrl="http://swift.ayaka.me/" xmlUrl="http://swift.ayaka.me/posts?format=RSS"/>
|
||||
<outline text="Everything in Context" type="rss" version="RSS" htmlUrl="http://lambdamaphone.blogspot.com/" xmlUrl="http://lambdamaphone.blogspot.com/feeds/posts/default"/>
|
||||
<outline text="Natasha the Robot" type="rss" version="RSS" htmlUrl="https://www.natashatherobot.com" xmlUrl="https://www.natashatherobot.com/feed/"/>
|
||||
<outline text="Katie Floyd" type="rss" version="RSS" htmlUrl="http://www.katiefloyd.com" xmlUrl="http://feed.katiefloyd.com/"/>
|
||||
<outline text="Meagan Waller" type="rss" version="RSS" htmlUrl="http://meaganwaller.com" xmlUrl="http://meaganwaller.com/index.php/feed/"/>
|
||||
<outline text="Susan × Blog" type="rss" version="RSS" htmlUrl="http://sketch.bysusanlin.com/" xmlUrl="http://sketch.bysusanlin.com/rss"/>
|
||||
<outline text="go ahead, mac my day" type="rss" version="RSS" htmlUrl="http://www.nadynerichmond.com/blog" xmlUrl="http://www.nadynerichmond.com/blog/feed/"/>
|
||||
<outline text="don't panic" type="rss" version="RSS" xmlUrl="http://timekl.com/atom.xml"/>
|
||||
<outline text="MechanicalGirl" type="rss" version="RSS" htmlUrl="http://www.MechanicalGirl.com/" xmlUrl="http://www.mechanicalgirl.com/feeds/all/"/>
|
||||
<outline text="BAD YEWEX" type="rss" version="RSS" xmlUrl="http://feeds.feedburner.com/BadYewex"/>
|
||||
<outline text="Becky Hansmeyer" type="rss" version="RSS" htmlUrl="http://beckyhansmeyer.com" xmlUrl="http://beckyhansmeyer.com/feed/"/>
|
||||
<outline text="KateHeddleston.com Blog Posts" type="rss" version="RSS" xmlUrl="https://kateheddleston.com/blog/feed.atom"/>
|
||||
<outline text="The Record" type="rss" version="RSS" htmlUrl="http://therecord.co/" xmlUrl="http://therecord.co/xml/rss.xml"/>
|
||||
<outline text="Grok Swift" type="rss" version="RSS" xmlUrl="https://grokswift.com/feed/index.xml"/>
|
||||
<outline text="Inspired Mouse" type="rss" version="RSS" htmlUrl="https://inspiredmouse.com" xmlUrl="https://inspiredmouse.com/feed/"/>
|
||||
<outline text="Tess Rinearson on Medium" type="rss" version="RSS" htmlUrl="https://medium.com/@tessr?source=rss-c16152863954------2" xmlUrl="https://medium.com/feed/@tessr"/>
|
||||
<outline text="Liz Marley on Medium" type="rss" version="RSS" htmlUrl="https://medium.com/@emarley?source=rss-b4981c59ffa5------2" xmlUrl="https://medium.com/feed/@emarley"/>
|
||||
<outline text="All The Flow" type="rss" version="RSS" htmlUrl="http://blog.alltheflow.com/" xmlUrl="https://blog.alltheflow.com/rss/"/>
|
||||
<outline text="kt zine — Medium" type="rss" version="RSS" htmlUrl="https://ktzine.com?source=rss----7097752c9303---4" xmlUrl="https://ktzine.com/feed"/>
|
||||
<outline text="Daring Fireball" type="rss" version="RSS" htmlUrl="http://daringfireball.net/" xmlUrl="http://daringfireball.net/index.xml"/>
|
||||
<outline text="Blog Posts About Stuff" type="rss" version="RSS" htmlUrl="http://nothe.purplellamas.net/index.xml" xmlUrl="http://nothe.purplellamas.net/index.xml"/>
|
||||
<outline text="Six Colors" type="rss" version="RSS" htmlUrl="https://www.sixcolors.com/" xmlUrl="http://feedpress.me/sixcolors"/>
|
||||
<outline text="mostgood" type="rss" version="RSS" htmlUrl="http://www.mostgood.net/" xmlUrl="http://www.mostgood.net/blog?format=RSS"/>
|
||||
<outline text="cocoa by the fire" type="rss" version="RSS" htmlUrl="http://blog.cocoabythefire.com/" xmlUrl="http://blog.cocoabythefire.com/rss"/>
|
||||
<outline text="ranchero.com" type="rss" version="RSS" htmlUrl="http://ranchero.com/" xmlUrl="http://ranchero.com/xml/rss.xml"/>
|
||||
<outline text="Linda Dong" type="rss" version="RSS" xmlUrl="http://www.lindadong.com/blog?format=RSS"/>
|
||||
<outline text="Eryn Wells" type="rss" version="RSS" htmlUrl="http://blog.erynwells.me/" xmlUrl="http://blog.erynwells.me/rss"/>
|
||||
<outline text="The Red Queen Coder" type="rss" version="RSS" htmlUrl="http://redqueencoder.com" xmlUrl="http://redqueencoder.com/feed/"/>
|
||||
<outline text="nataliepo (posts on 'nataliepo' (rss 2.0))" type="rss" version="RSS" htmlUrl="http://nataliepo.typepad.com/nataliepo/" xmlUrl="http://nataliepo.typepad.com/nataliepo/rss.xml"/>
|
||||
<outline text="Scripting News" type="rss" version="RSS" htmlUrl="http://scripting.com/" xmlUrl="http://scripting.com/rss.xml"/>
|
||||
<outline text="Designated Nerd" type="rss" version="RSS" htmlUrl="http://designatednerd.com" xmlUrl="http://designatednerd.com/feed/"/>
|
||||
<outline text="kristinathai.com" type="rss" version="RSS" htmlUrl="https://kristina.io" xmlUrl="http://www.kristinathai.com/feed/"/>
|
||||
<outline text="Natasha The Robot" type="rss" version="RSS" htmlUrl="https://www.natashatherobot.com" xmlUrl="http://natashatherobot.com/feed/"/>
|
||||
<outline text="Samantha Marshall's Blog" type="rss" version="RSS" htmlUrl="http://pewpewthespells.com/" xmlUrl="http://pewpewthespells.com/feed.xml"/>
|
||||
<outline text="Ballard" type="rss" version="RSS" htmlUrl="http://www.myballard.com" xmlUrl="http://www.myballard.com/feed/"/>
|
||||
<outline text="Programming">
|
||||
<outline text="iOS Unit Testing" type="rss" version="RSS" htmlUrl="http://iosunittesting.com" xmlUrl="http://iosunittesting.com/feed/"/>
|
||||
<outline text="A List Apart: The Full Feed" type="rss" version="RSS" htmlUrl="http://alistapart.com" xmlUrl="http://feeds.feedburner.com/alistapart/main"/>
|
||||
<outline text="Swift Programming — Medium" type="rss" version="RSS" htmlUrl="https://medium.com/swift-programming?source=rss----5396e0e8bc29---4" xmlUrl="https://medium.com/feed/swift-programming"/>
|
||||
<outline text="The Confusatory" type="rss" version="RSS" htmlUrl="http://confusatory.org/" xmlUrl="http://confusatory.org/rss"/>
|
||||
<outline text="Debuggers" type="rss" version="RSS" xmlUrl="http://debuggers.co/atom.xml"/>
|
||||
<outline text="We ❤ Swift" type="rss" version="RSS" htmlUrl="https://www.weheartswift.com" xmlUrl="http://www.weheartswift.com/feed/"/>
|
||||
<outline text="Airspeed Velocity" type="rss" version="RSS" htmlUrl="https://airspeedvelocity.net" xmlUrl="http://airspeedvelocity.net/feed/"/>
|
||||
<outline text="The blog of Tony Arnold" type="rss" version="RSS" xmlUrl="http://tonyarnold.com/atom.xml"/>
|
||||
<outline text="Code by Kevin" type="rss" version="RSS" htmlUrl="http://www.codebykevin.com/blosxom.cgi" xmlUrl="http://www.codebykevin.com/blosxom.cgi/index.rss"/>
|
||||
<outline text="Programming in the 21st Century" type="rss" version="RSS" htmlUrl="http://prog21.dadgum.com/" xmlUrl="http://prog21.dadgum.com/atom.xml"/>
|
||||
<outline text="What Amy Did" type="rss" version="RSS" htmlUrl="http://blog.amyworrall.com/" xmlUrl="http://blog.amyworrall.com/rss"/>
|
||||
<outline text="Russ Bishop (atom)" type="rss" version="RSS" htmlUrl="http://www.russbishop.net" xmlUrl="http://www.russbishop.net/feed"/>
|
||||
<outline text="The Mental Blog" type="rss" version="RSS" htmlUrl="http://mentalfaculty.tumblr.com/" xmlUrl="http://mentalfaculty.tumblr.com/rss"/>
|
||||
<outline text="Swift Studies" type="rss" version="RSS" htmlUrl="http://www.swift-studies.com/" xmlUrl="http://www.swift-studies.com/blog?format=RSS"/>
|
||||
<outline text="owensd.io - thoughts in and out - Articles" type="rss" version="RSS" htmlUrl="https://owensd.io" xmlUrl="http://owensd.io/rss.xml"/>
|
||||
<outline text="Swift Yeti" type="rss" version="RSS" htmlUrl="http://swiftyeti.com/" xmlUrl="http://swiftyeti.com/rss/"/>
|
||||
<outline text="Cocoaphony" type="rss" version="RSS" xmlUrl="http://robnapier.net/atom.xml"/>
|
||||
<outline text="Damien DeVille" type="rss" version="RSS" htmlUrl="http://ddeville.me" xmlUrl="http://ddeville.me/feed.xml"/>
|
||||
<outline text="Cocoa Manifest" type="rss" version="RSS" xmlUrl="http://cocoamanifest.net/feeds/index.xml"/>
|
||||
<outline text="Indie Stack" type="rss" version="RSS" htmlUrl="http://indiestack.com" xmlUrl="http://indiestack.com/feed/"/>
|
||||
<outline text="[macoscope blog]" type="rss" version="RSS" htmlUrl="http://macoscope.com/blog" xmlUrl="http://macoscope.com/blog/feed/"/>
|
||||
<outline text="Ole Begemann: iOS Development" type="rss" version="RSS" htmlUrl="https://oleb.net/blog/" xmlUrl="http://oleb.net/blog/atom.xml"/>
|
||||
<outline text="David J Peacock - iOS Blog" type="rss" version="RSS" xmlUrl="http://davidjpeacock.ca/atom.xml"/>
|
||||
<outline text="Ray Wenderlich" type="rss" version="RSS" htmlUrl="https://www.raywenderlich.com" xmlUrl="http://www.raywenderlich.com/feed"/>
|
||||
<outline text="Peter Steinberger" type="rss" version="RSS" xmlUrl="http://petersteinberger.com/atom.xml"/>
|
||||
<outline text="Cocoa Is My Girlfriend" type="rss" version="RSS" htmlUrl="http://www.cimgf.com" xmlUrl="http://www.cimgf.com/feed/"/>
|
||||
<outline text="Subjective-C" type="rss" version="RSS" xmlUrl="http://subjc.com/atom.xml"/>
|
||||
<outline text="the Joy of Code" type="rss" version="RSS" xmlUrl="http://feeds.feedburner.com/thejoyofcode/"/>
|
||||
<outline text="New Yankee Codeshop" type="rss" version="RSS" htmlUrl="http://newyankeecodeshop.tumblr.com/" xmlUrl="http://newyankeecodeshop.tumblr.com/rss"/>
|
||||
<outline text="Borkware Miniblog" type="rss" version="RSS" htmlUrl="https://borkwarellc.wordpress.com" xmlUrl="http://borkware.com/miniblog/rss/rss.xml"/>
|
||||
<outline text="NSHipster" type="rss" version="RSS" htmlUrl="http://nshipster.com" xmlUrl="http://nshipster.com/feed.xml"/>
|
||||
<outline text="Pilky.me" type="rss" version="RSS" htmlUrl="http://pilky.me/" xmlUrl="http://feeds.feedburner.com/pilkyme"/>
|
||||
<outline text="Big Nerd Ranch Blog" type="rss" version="RSS" htmlUrl="https://www.bignerdranch.com/" xmlUrl="http://blog.bignerdranch.com/feed/"/>
|
||||
</outline>
|
||||
<outline text="Macintosh">
|
||||
<outline text="Macalope" type="rss" version="RSS" htmlUrl="http://www.macalope.com" xmlUrl="http://www.macalope.com/feed/"/>
|
||||
<outline text="9to5Mac" type="rss" version="RSS" htmlUrl="https://9to5mac.com" xmlUrl="http://9to5mac.com/feed/"/>
|
||||
<outline text="Macdrifter" type="rss" version="RSS" htmlUrl="http://www.macdrifter.com/" xmlUrl="http://www.macdrifter.com/feeds/all.atom.xml"/>
|
||||
<outline text="TidBITS: Apple News for the Rest of Us" type="rss" version="RSS" htmlUrl="http://tidbits.com/" xmlUrl="http://tidbits.com/feeds/tidbits_blurb.rss"/>
|
||||
<outline text="The Flying Meat Weblog" type="rss" version="RSS" htmlUrl="http://flyingmeat.com/blog/" xmlUrl="http://flyingmeat.com/blog/atom.xml"/>
|
||||
</outline>
|
||||
<outline text="Weblogs">
|
||||
<outline text="Clark's Tech Blog" type="rss" version="RSS" htmlUrl="http://www.libertypages.com/clarktech" xmlUrl="http://www.libertypages.com/clarktech/?feed=rss2"/>
|
||||
<outline text="NSBlog" type="rss" version="RSS" htmlUrl="http://www.mikeash.com/pyblog/" xmlUrl="http://www.mikeash.com/pyblog/rss.py"/>
|
||||
<outline text="metablog" type="rss" version="RSS" htmlUrl="http://blog.metaobject.com/" xmlUrl="http://blog.metaobject.com/feeds/posts/default"/>
|
||||
<outline text="Better Elevation" type="rss" version="RSS" htmlUrl="http://betterelevation.com" xmlUrl="http://betterelevation.com/feed/"/>
|
||||
<outline text="Allen Pike" type="rss" version="RSS" xmlUrl="http://www.allenpike.com/feed/"/>
|
||||
<outline text="Secure Mac Programming" type="rss" version="RSS" xmlUrl="http://blog.securemacprogramming.com/feed/"/>
|
||||
<outline text="iPhone Developer News" type="rss" version="RSS" htmlUrl="https://developer.apple.com/news/" xmlUrl="https://developer.apple.com/news/rss/news.rss"/>
|
||||
<outline text="David Smith" type="rss" version="RSS" xmlUrl="http://david-smith.org/atom.xml"/>
|
||||
<outline text="Liss is More" type="rss" version="RSS" htmlUrl="https://www.caseyliss.com" xmlUrl="http://www.caseyliss.com/rss"/>
|
||||
<outline text="Codeplease" type="rss" version="RSS" htmlUrl="http://codeplease.io/" xmlUrl="http://codeplease.io/rss/"/>
|
||||
<outline text="furbo.org" type="rss" version="RSS" htmlUrl="http://furbo.org" xmlUrl="http://furbo.org/feed/"/>
|
||||
<outline text="Very Web. Such Blog. Wow." type="rss" version="RSS" htmlUrl="http://brian-webster.tumblr.com/" xmlUrl="http://brian-webster.tumblr.com/rss"/>
|
||||
<outline text="bryan i/o" type="rss" version="RSS" htmlUrl="http://bryan.io/" xmlUrl="http://bryan.io/rss"/>
|
||||
<outline text="Nick Bradbury" type="rss" version="RSS" htmlUrl="https://nickbradbury.com" xmlUrl="http://nickbradbury.com/feed/"/>
|
||||
<outline text="Gordon Meyer (posts on 'gordon meyer' (atom))" type="rss" version="RSS" htmlUrl="http://www.gordonmeyer.com/" xmlUrl="http://www.gordonmeyer.com/atom.xml"/>
|
||||
<outline text="Rhonabwy" type="rss" version="RSS" xmlUrl="http://www.rhonabwy.com/wp/feed/"/>
|
||||
<outline text="Typeset In The Future" type="rss" version="RSS" htmlUrl="https://typesetinthefuture.com" xmlUrl="http://typesetinthefuture.com/feed/"/>
|
||||
<outline text="Neglected Potential" type="rss" version="RSS" htmlUrl="http://www.neglectedpotential.com" xmlUrl="http://www.neglectedpotential.com/feed/"/>
|
||||
<outline text="Informal Protocol" type="rss" version="RSS" xmlUrl="http://informalprotocol.com/atom.xml"/>
|
||||
<outline text="NSHipster" type="rss" version="RSS" htmlUrl="http://nshipster.com" xmlUrl="http://feeds.feedburner.com/NSHipster"/>
|
||||
<outline text="Daniel Jalkut" type="rss" version="RSS" htmlUrl="http://bitsplitting.org" xmlUrl="http://bitsplitting.org/feed/"/>
|
||||
<outline text="Ash Furrow" type="rss" version="RSS" xmlUrl="http://ashfurrow.com/blog?format=rss"/>
|
||||
<outline text="Jared Sinclair" type="rss" version="RSS" htmlUrl="http://blog.jaredsinclair.com/" xmlUrl="http://blog.jaredsinclair.com/rss?1"/>
|
||||
<outline text="Doug Russell" type="rss" version="RSS" xmlUrl="http://www.takingnotes.co/atom.xml"/>
|
||||
<outline text="JakeSavin.com" type="rss" version="RSS" xmlUrl="http://www.jakesavin.com/xml/rss.xml"/>
|
||||
<outline text="Apple Outsider" type="rss" version="RSS" htmlUrl="http://www.appleoutsider.com" xmlUrl="http://www.appleoutsider.com/feed/"/>
|
||||
<outline text="Nackblog" type="rss" version="RSS" htmlUrl="http://jnack.com/blog" xmlUrl="http://jnack.com/blog/?feed=rss2"/>
|
||||
<outline text="ignorethecode.net" type="rss" version="RSS" htmlUrl="http://ignorethecode.net" xmlUrl="http://ignorethecode.net/blog/rss/"/>
|
||||
<outline text="Sheila's Weblog" type="rss" version="RSS" htmlUrl="https://sheilasweblog.wordpress.com" xmlUrl="http://sheilasweblog.wordpress.com/feed/"/>
|
||||
<outline text="The Fine Edge" type="rss" version="RSS" xmlUrl="http://nicemohawk.com/atom.xml"/>
|
||||
<outline text="Rands In Repose" type="rss" version="RSS" htmlUrl="http://randsinrepose.com" xmlUrl="http://www.randsinrepose.com/index.xml"/>
|
||||
<outline text="John Nack on Adobe (rss (feedburner))" type="rss" version="RSS" htmlUrl="http://blogs.adobe.com/jnack" xmlUrl="http://feeds2.feedburner.com/adobe/jnack"/>
|
||||
<outline text="Dan Gillmor" type="rss" version="RSS" htmlUrl="http://dangillmor.com" xmlUrl="http://dangillmor.com/feed/"/>
|
||||
<outline text="Corporation Unknown" type="rss" version="RSS" htmlUrl="http://corporationunknown.com/blog" xmlUrl="http://corporationunknown.com/blog/feed/"/>
|
||||
<outline text="Dalton Caldwell" type="rss" version="RSS" htmlUrl="http://daltoncaldwell.com" xmlUrl="http://daltoncaldwell.com/feed"/>
|
||||
<outline text="level of indirection" type="rss" version="RSS" htmlUrl="http://www.levelofindirection.com/journal/" xmlUrl="http://www.levelofindirection.com/journal/rss.xml"/>
|
||||
<outline text="I Am Simme" type="rss" version="RSS" htmlUrl="http://iamsim.me" xmlUrl="http://iamsim.me/rss/"/>
|
||||
<outline text="Collin Donnell (collin donnell » feed)" type="rss" version="RSS" htmlUrl="http://collindonnell.com" xmlUrl="http://feedpress.me/collindonnell"/>
|
||||
<outline text="Sci-Fi Hi-Fi: Weblog" type="rss" version="RSS" htmlUrl="http://log.scifihifi.com/" xmlUrl="http://log.scifihifi.com/rss"/>
|
||||
<outline text="Adventures in Newfield" type="rss" version="RSS" htmlUrl="http://adventuresinnewfield.blogspot.com/" xmlUrl="http://adventuresinnewfield.blogspot.com/feeds/posts/default"/>
|
||||
<outline text="The Desolation of Blog" type="rss" version="RSS" htmlUrl="http://lapcatsoftware.com/articles/index.html" xmlUrl="http://lapcatsoftware.com/articles/atom.xml"/>
|
||||
<outline text="Jesper" type="rss" version="RSS" htmlUrl="http://stmts.net" xmlUrl="http://stmts.net/feed/"/>
|
||||
<outline text="RatHole" type="rss" version="RSS" htmlUrl="http://rathole.tumblr.com/" xmlUrl="http://rathole.tumblr.com/rss"/>
|
||||
<outline text="Jeff McLeman" type="rss" version="RSS" htmlUrl="http://www.jeffmcleman.com/blog" xmlUrl="http://www.jeffmcleman.com/blog/feed/"/>
|
||||
<outline text="frozendevil" type="rss" version="RSS" xmlUrl="http://frozendevil.com/atom.xml"/>
|
||||
<outline text="Zathras.de - Uli's most useless blog in the World" type="rss" version="RSS" htmlUrl="http://orangejuiceliberationfront.com" xmlUrl="http://www.zathras.de/angelweb/BlogRSSFeed.rss"/>
|
||||
<outline text="Monday Note" type="rss" version="RSS" htmlUrl="https://mondaynote.com?source=rss----c537d80ed0a---4" xmlUrl="http://www.mondaynote.com/feed/"/>
|
||||
<outline text="Michael Tsai" type="rss" version="RSS" htmlUrl="http://mjtsai.com/blog" xmlUrl="http://mjtsai.com/blog/feed/"/>
|
||||
<outline text="James Dempsey" type="rss" version="RSS" htmlUrl="http://jamesdempsey.net" xmlUrl="http://jamesdempsey.net/feed/"/>
|
||||
<outline text="Red Sweater" type="rss" version="RSS" htmlUrl="https://red-sweater.com/blog" xmlUrl="http://www.red-sweater.com/blog/feed"/>
|
||||
<outline text="Peter Hosey" type="rss" version="RSS" htmlUrl="http://boredzo.org/blog" xmlUrl="http://feeds.feedburner.com/domainofthebored"/>
|
||||
<outline text="Use Your Loaf" type="rss" version="RSS" htmlUrl="http://useyourloaf.com/blog/" xmlUrl="http://useyourloaf.com/blog/rss.xml"/>
|
||||
<outline text="The Main Thread" type="rss" version="RSS" xmlUrl="http://themainthread.com/feed.xml"/>
|
||||
<outline text="Awkward Hare" type="rss" version="RSS" htmlUrl="http://awkwardhare.com/" xmlUrl="http://awkwardhare.com/rss"/>
|
||||
<outline text="Journal (atom)" type="rss" version="RSS" xmlUrl="http://www.curtclifton.net/journal/atom.xml"/>
|
||||
<outline text="Blog - Jury.me" type="rss" version="RSS" htmlUrl="http://jury.me/blog/" xmlUrl="http://jury.me/blog?format=rss"/>
|
||||
<outline text="Call Me Fishmeal." type="rss" version="RSS" htmlUrl="http://blog.wilshipley.com/" xmlUrl="http://blog.wilshipley.com/rss.xml"/>
|
||||
<outline text="Hal Mueller's Blog" type="rss" version="RSS" htmlUrl="https://halmueller.wordpress.com" xmlUrl="http://halmueller.wordpress.com/feed/"/>
|
||||
<outline text="And now it’s all this" type="rss" version="RSS" htmlUrl="http://leancrew.com/all-this" xmlUrl="http://www.leancrew.com/all-this/feed/"/>
|
||||
<outline text="OneThirtySeven" type="rss" version="RSS" htmlUrl="http://one37.net/blog/" xmlUrl="http://one37.net/blog?format=rss"/>
|
||||
<outline text="rentzsch.tumblr.com" type="rss" version="RSS" htmlUrl="http://rentzsch.tumblr.com/" xmlUrl="http://rentzsch.tumblr.com/rss"/>
|
||||
<outline text="⌥⇧K" type="rss" version="RSS" htmlUrl="http://optshiftk.com" xmlUrl="http://optshiftk.com/feed/"/>
|
||||
<outline text="Sam Ruby" type="rss" version="RSS" xmlUrl="http://intertwingly.net/blog/index.atom"/>
|
||||
<outline text="literator.me" type="rss" version="RSS" xmlUrl="http://literator.me/rss"/>
|
||||
<outline text="upbeat.it" type="rss" version="RSS" htmlUrl="http://www.upbeat.it/" xmlUrl="http://www.upbeat.it/atom.xml"/>
|
||||
<outline text="Minutes to Midnight" type="rss" version="RSS" htmlUrl="http://minutestomidnight.net/" xmlUrl="http://minutestomidnight.net/blog?format=RSS"/>
|
||||
<outline text="John Moltz's Very Nice Web Site" type="rss" version="RSS" htmlUrl="http://verynicewebsite.net" xmlUrl="http://verynicewebsite.net/feed/atom/"/>
|
||||
<outline text="Matt Mullenweg" type="rss" version="RSS" htmlUrl="https://ma.tt" xmlUrl="http://ma.tt/feed/"/>
|
||||
<outline text="BrettTerpstra.com" type="rss" version="RSS" xmlUrl="http://brettterpstra.com/atom.xml"/>
|
||||
<outline text="jwz" type="rss" version="RSS" htmlUrl="https://www.jwz.org/blog/" xmlUrl="http://www.jwz.org/blog/feed/"/>
|
||||
<outline text="Maniacal Rage" type="rss" version="RSS" htmlUrl="http://log.maniacalrage.net/" xmlUrl="http://log.maniacalrage.net/rss"/>
|
||||
<outline text="Shawn Blanc" type="rss" version="RSS" htmlUrl="http://shawnblanc.net" xmlUrl="http://shawnblanc.net/feed/"/>
|
||||
<outline text="carpeaqua" type="rss" version="RSS" htmlUrl="https://carpeaqua.com" xmlUrl="http://feeds.feedburner.com/carpeaqua"/>
|
||||
<outline text="Doug Russell" type="rss" version="RSS" xmlUrl="http://www.getitdownonpaper.com/atom.xml"/>
|
||||
<outline text="bbum's weblog-o-mat" type="rss" version="RSS" htmlUrl="http://www.friday.com/bbum" xmlUrl="http://www.friday.com/bbum/feed/"/>
|
||||
<outline text="Fraser Speirs" type="rss" version="RSS" htmlUrl="http://www.speirs.org/" xmlUrl="http://speirs.org/feed/"/>
|
||||
<outline text="ridiculous_fish" type="rss" version="RSS" xmlUrl="http://pammon.webfactional.com/blog/feed/"/>
|
||||
<outline text="Hypercritical" type="rss" version="RSS" htmlUrl="http://hypercritical.co/" xmlUrl="http://hypercritical.co/feeds/main"/>
|
||||
<outline text="Manton Reece" type="rss" version="RSS" htmlUrl="http://www.manton.org" xmlUrl="http://manton.org/rss.xml"/>
|
||||
<outline text="Gus's weblog" type="rss" version="RSS" htmlUrl="http://shapeof.com/" xmlUrl="http://gusmueller.com/blog/atom.xml"/>
|
||||
<outline text="Dustin Curtis" type="rss" version="RSS" htmlUrl="https://dcurt.is" xmlUrl="http://feeds.feedburner.com/dcurtis"/>
|
||||
<outline text="Waffle" type="rss" version="RSS" htmlUrl="http://waffle.wootest.net" xmlUrl="http://waffle.wootest.net/feed/atom/"/>
|
||||
<outline text="Mistitled" type="rss" version="RSS" htmlUrl="http://rms2.tumblr.com/" xmlUrl="http://rms2.tumblr.com/rss"/>
|
||||
<outline text="Don Melton" type="rss" version="RSS" htmlUrl="https://donmelton.com" xmlUrl="http://donmelton.com/rss.xml"/>
|
||||
<outline text="Anil Dash" type="rss" version="RSS" htmlUrl="http://anildash.com/" xmlUrl="http://feeds.dashes.com/AnilDash"/>
|
||||
<outline text="Neven Mrgan's tumbl" type="rss" version="RSS" htmlUrl="http://mrgan.tumblr.com/" xmlUrl="http://mrgan.tumblr.com/rss"/>
|
||||
<outline text="alexking.org: Blog" type="rss" version="RSS" htmlUrl="http://alexking.org" xmlUrl="http://alexking.org/blog/feed"/>
|
||||
<outline text="ParisLemon" type="rss" version="RSS" htmlUrl="http://parislemon.com/" xmlUrl="http://parislemon.com/rss"/>
|
||||
<outline text="Black Pixel" type="rss" version="RSS" htmlUrl="https://blackpixel.com/writing/" xmlUrl="http://blackpixel.com/blog/atom.xml"/>
|
||||
<outline text="Marco.org" type="rss" version="RSS" htmlUrl="https://marco.org/" xmlUrl="http://www.marco.org/rss"/>
|
||||
<outline text="The Guinea Pig in the Cocoa Mine" type="rss" version="RSS" xmlUrl="http://cocoamine.net/atom.xml"/>
|
||||
<outline text="Blog | Mike Abdullah" type="rss" version="RSS" htmlUrl="http://mikeabdullah.net/" xmlUrl="http://www.mikeabdullah.net/index.xml"/>
|
||||
<outline text="Kickingbear Blog" type="rss" version="RSS" htmlUrl="http://kickingbear.com/blog" xmlUrl="http://kickingbear.com/blog/feed"/>
|
||||
<outline text="Stuart Hall" type="rss" version="RSS" htmlUrl="http://stuartkhall.com/" xmlUrl="http://feeds.feedburner.com/stuartkhall"/>
|
||||
<outline text="ongoing" type="rss" version="RSS" xmlUrl="http://www.tbray.org/ongoing/ongoing.atom"/>
|
||||
<outline text="Stratēchery" type="rss" version="RSS" htmlUrl="https://stratechery.com" xmlUrl="http://stratechery.com/feed/"/>
|
||||
<outline text="Mark Bernstein" type="rss" version="RSS" htmlUrl="http://markbernstein.org/" xmlUrl="http://www.markbernstein.org/news.rss"/>
|
||||
</outline>
|
||||
<outline text="Writers">
|
||||
<outline text="Whatever" type="rss" version="RSS" htmlUrl="http://whatever.scalzi.com" xmlUrl="http://whatever.scalzi.com/feed/"/>
|
||||
<outline text="Charlie's Diary" type="rss" version="RSS" htmlUrl="http://www.antipope.org/charlie/blog-static/" xmlUrl="http://www.antipope.org/charlie/blog-static/atom.xml"/>
|
||||
<outline text="Gerrold" type="rss" version="RSS" htmlUrl="http://www.gerrold.com" xmlUrl="http://www.gerrold.com/feed/"/>
|
||||
</outline>
|
||||
</body>
|
||||
</opml>
|
24
Parser/Tests/ParserTests/Resources/YouTubeTheVolvoRocks.html
Normal file
24
Parser/Tests/ParserTests/Resources/YouTubeTheVolvoRocks.html
Normal file
File diff suppressed because one or more lines are too long
710
Parser/Tests/ParserTests/Resources/aktuality.rss
Normal file
710
Parser/Tests/ParserTests/Resources/aktuality.rss
Normal file
@ -0,0 +1,710 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
|
||||
>
|
||||
<channel>
|
||||
<title>Aktuality.sk - aktuálne spravodajstvo</title>
|
||||
<link>https://www.aktuality.sk</link>
|
||||
<pubDate>Sun, 17 Jan 2021 23:46:22 +0100</pubDate>
|
||||
<description>Denne aktualizované spravodajstvo z domova i zo sveta. Správy z oblasti ekonomiky,
|
||||
kultúry a spoločenského života. Predpoveď počasia, horoskopy, TV program a kurzový lístok.</description>
|
||||
<dc:language>sk</dc:language>
|
||||
<dc:creator>Ringier Axel Springer SK</dc:creator>
|
||||
<sy:updatePeriod>hourly</sy:updatePeriod>
|
||||
<sy:updateFrequency>1</sy:updateFrequency>
|
||||
|
||||
<item>
|
||||
<title>Analytici Goldman Sachs zvýšili odhad rastu ekonomiky USA</title>
|
||||
<link>https://www.aktuality.sk/clanok/856577/analytici-goldman-sachs-zvysili-odhad-rastu-ekonomiky-usa/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/vUw5YHzvQKDCkNw6PtXtAg.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=OcgUSVfIJOH9QPOW-nqQxQ&e=2145916800&v=3</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Analytici Goldman Sachs zvýšili odhad rastu ekonomiky USA
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856577/analytici-goldman-sachs-zvysili-odhad-rastu-ekonomiky-usa/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/vUw5YHzvQKDCkNw6PtXtAg.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=OcgUSVfIJOH9QPOW-nqQxQ&e=2145916800&v=3" />
|
||||
<br/>
|
||||
Očakávajú totiž, že plán fiškálnych stimulov novozvoleného prezidenta Joea Bidena urýchli zotavovanie hospodárstva po pandémii ochorenia COVID-19.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 19:27:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Koronavírus: Cielené testovanie zachytilo v Banskej Bystrici desiatky pozitívnych</title>
|
||||
<link>https://www.aktuality.sk/clanok/856600/koronavirus-cielene-testovanie-zachytilo-v-banskej-bystrici-desiatky-pozitivnych/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/uimnlYXjRubxkNz9qHHvKw.320~Pr-prava-odberov-v-Banskej-Bystrici.jpg?t=LzE1OHgxMDYvc21hcnQ&h=o5WAHblMYtS3LhTEGJqP5w&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Koronavírus: Cielené testovanie zachytilo v Banskej Bystrici desiatky pozitívnych
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856600/koronavirus-cielene-testovanie-zachytilo-v-banskej-bystrici-desiatky-pozitivnych/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/uimnlYXjRubxkNz9qHHvKw.320~Pr-prava-odberov-v-Banskej-Bystrici.jpg?t=LzE1OHgxMDYvc21hcnQ&h=o5WAHblMYtS3LhTEGJqP5w&e=2145916800&v=2" />
|
||||
<br/>
|
||||
V krajskom meste otestovali viac ako 8-tisíc ľudí, ktorí nemôžu pracovať z domu.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 19:25:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Koronavírus: Slovensko čaká 9-dňové plošné testovanie, predĺžil sa zákaz vychádzania</title>
|
||||
<link>https://www.aktuality.sk/clanok/856582/koronavirus-slovensko-caka-9-dnove-plosne-testovanie-predlzi-sa-aj-zakaz-vychadzania/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/fXiDsLeeR2uP3wTcEQMcxg.320~Igor-Matovi-hovor-o-novom-pl-ne-pre-Slovensko-v-s-vislosti-s-koronav-rusom.jpg?t=LzE1OHgxMDYvc21hcnQ&h=UitS_uvS5fVTyL9_MCD3vw&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Koronavírus: Slovensko čaká 9-dňové plošné testovanie, predĺžil sa zákaz vychádzania
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856582/koronavirus-slovensko-caka-9-dnove-plosne-testovanie-predlzi-sa-aj-zakaz-vychadzania/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/fXiDsLeeR2uP3wTcEQMcxg.320~Igor-Matovi-hovor-o-novom-pl-ne-pre-Slovensko-v-s-vislosti-s-koronav-rusom.jpg?t=LzE1OHgxMDYvc21hcnQ&h=UitS_uvS5fVTyL9_MCD3vw&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Celoslovenské plošné testovanie začne od zajtra a potrvá do 26. januára. Povinnosť mať certifikát o negatívnom teste na Covid-19 začne platiť od 27 januára. Otestovať sa budete môcť v mobilných odberných miestach.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 19:06:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Komentár Ľubomíra Jaška: Pellegrini prerazil dno, ale Matovič mu v tom výrazne pomohol</title>
|
||||
<link>https://www.aktuality.sk/clanok/856603/komentar-lubomira-jaska-pellegrini-prerazil-dno-ale-matovic-mu-v-tom-vyrazne-pomohol/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/vjCQaQs5S-XhbgVKbM6O_A.320~Predseda-Hlasu-SD-Peter-Pellegrini.jpg?t=LzE1OHgxMDYvc21hcnQ&h=HbiWYr3kBM3gZZFg-66E8Q&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Komentár Ľubomíra Jaška: Pellegrini prerazil dno, ale Matovič mu v tom výrazne pomohol
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856603/komentar-lubomira-jaska-pellegrini-prerazil-dno-ale-matovic-mu-v-tom-vyrazne-pomohol/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/vjCQaQs5S-XhbgVKbM6O_A.320~Predseda-Hlasu-SD-Peter-Pellegrini.jpg?t=LzE1OHgxMDYvc21hcnQ&h=HbiWYr3kBM3gZZFg-66E8Q&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Atómová bomba plošného testovania môže Pellegriniho zásluhou vybuchnúť úplne inak, ako si predstavuje najväčší fanúšik tyčiniek v nose.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 19:00:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Británia vyzvala Čínu, aby umožnila inšpektorom z OSN navštíviť ujgurskú oblasť</title>
|
||||
<link>https://www.aktuality.sk/clanok/856579/britania-vyzvala-cinu-aby-umoznila-inspektorom-z-osn-navstivit-ujgursku-oblast/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/o7lvPB9UQa7nc2DDLRcIkg.320~Brit-nia-sa-dostala-na-zoznam-zelen-ch-kraj-n-Slov-kov-m-e-od-britskej-karant-ny-oslobodi-testovanie-na-letisku.jpg?t=LzE1OHgxMDYvc21hcnQ&h=AvL1vkAMoW9iJUmagwaVDg&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Británia vyzvala Čínu, aby umožnila inšpektorom z OSN navštíviť ujgurskú oblasť
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856579/britania-vyzvala-cinu-aby-umoznila-inspektorom-z-osn-navstivit-ujgursku-oblast/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/o7lvPB9UQa7nc2DDLRcIkg.320~Brit-nia-sa-dostala-na-zoznam-zelen-ch-kraj-n-Slov-kov-m-e-od-britskej-karant-ny-oslobodi-testovanie-na-letisku.jpg?t=LzE1OHgxMDYvc21hcnQ&h=AvL1vkAMoW9iJUmagwaVDg&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Británia obvinila v utorok Čínu z porušovania ľudských práv ujgurskej menšiny a zaviedla obmedzenia pre dovoz tovaru, pri ktorom existuje podozrenie, že bol výsledkom nútenej práce.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 18:47:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Guatemalská polícia použila slzotvorný plyn proti migrantom smerujúcim do USA</title>
|
||||
<link>https://www.aktuality.sk/clanok/856584/guatemalska-policia-pouzila-slzotvorny-plyn-proti-migrantom-smerujucim-do-usa/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/BWTlMtRQR9O3m3h1AVzYAQ.320~Guatemalsk-pol-cia-pou-ila-v-nede-u-slzotvorn-plyn-pri-pokuse-o-rozpt-lenie-tis-cov-migrantov-ktor-smeruj-do-Spojen-ch-t-tov.jpg?t=LzE1OHgxMDYvc21hcnQ&h=fgEYpogX5r_3yr9PP83DaQ&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Guatemalská polícia použila slzotvorný plyn proti migrantom smerujúcim do USA
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856584/guatemalska-policia-pouzila-slzotvorny-plyn-proti-migrantom-smerujucim-do-usa/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/BWTlMtRQR9O3m3h1AVzYAQ.320~Guatemalsk-pol-cia-pou-ila-v-nede-u-slzotvorn-plyn-pri-pokuse-o-rozpt-lenie-tis-cov-migrantov-ktor-smeruj-do-Spojen-ch-t-tov.jpg?t=LzE1OHgxMDYvc21hcnQ&h=fgEYpogX5r_3yr9PP83DaQ&e=2145916800&v=2" />
|
||||
<br/>
|
||||
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 18:16:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Lietadlo s Navaľným na palube pristálo v Moskve. Zadržali ho pri pasovej kontrole</title>
|
||||
<link>https://www.aktuality.sk/clanok/856578/v-rade-cislo-13-a-v-obkluceni-novinarov-lietadlo-s-navalnym-na-palube-pristalo-v-moskve/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/rQR7fDWdSUCrhoDxG5E-4w.320~Nava-nyj-v-obk-en-novin-rov-na-palube-lietadla-ruskej-spolo-nosti-Pobeda.jpg?t=LzE1OHgxMDYvc21hcnQ&h=ltlql5GozBU4I7ojtvlq_Q&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Lietadlo s Navaľným na palube pristálo v Moskve. Zadržali ho pri pasovej kontrole
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856578/v-rade-cislo-13-a-v-obkluceni-novinarov-lietadlo-s-navalnym-na-palube-pristalo-v-moskve/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/rQR7fDWdSUCrhoDxG5E-4w.320~Nava-nyj-v-obk-en-novin-rov-na-palube-lietadla-ruskej-spolo-nosti-Pobeda.jpg?t=LzE1OHgxMDYvc21hcnQ&h=ltlql5GozBU4I7ojtvlq_Q&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Na moskovskom letisku Vnukovo, kde malo pristáť lietadlo s Navaľným na palube, poriadkové sily zadržiavali ľudí. Lietadlo nakoniec odklonili na letisko Šeremetievo. Ruského opozičníka zadržali pri pasovej kontrole.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 18:09:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Zima ako u Mrázika. Na ruskú zimu neplatí ani vodka</title>
|
||||
<link>https://www.aktuality.sk/clanok/850383/zima-ako-u-mrazika-na-rusku-zimu-neplati-ani-vodka/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/ycvFEc7sRqDRN4gwgHe7RQ.320~Vyhod-vriacu-vodu-a-dopad-sneh-Ojmiakon-Rusko.jpg?t=LzE1OHgxMDYvc21hcnQ&h=EgmzER7E9H4nRn45dFNKMg&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Zima ako u Mrázika. Na ruskú zimu neplatí ani vodka
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/850383/zima-ako-u-mrazika-na-rusku-zimu-neplati-ani-vodka/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/ycvFEc7sRqDRN4gwgHe7RQ.320~Vyhod-vriacu-vodu-a-dopad-sneh-Ojmiakon-Rusko.jpg?t=LzE1OHgxMDYvc21hcnQ&h=EgmzER7E9H4nRn45dFNKMg&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Vedeli ste, že ani Rusi nezaháňajú zimu vodkou? Cestovateľ Martin Navrátil bol v meste Ojmiakon, kde bola nameraná najnižšia teplota na svete mimo Antarktídy a vypočul si to priamo od domácich.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 18:00:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Bielorusko: V niekoľkých mestách pokračovali protesty proti Lukašenkovi</title>
|
||||
<link>https://www.aktuality.sk/clanok/856576/bielorusko-v-niekolkych-mestach-pokracovali-protesty-proti-lukasenkovi/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/0CMFQVwiS3-kV3J90Uv1gg.320~D-chodcovia-protestuj-proti-Alexandarovi-Luka-enkovi.jpg?t=LzB4MzozMjB4MTgzLzE1OHgxMDYvc21hcnQ&h=prxFwHNADbX61294ZAVPPg&e=2145916800&v=3</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Bielorusko: V niekoľkých mestách pokračovali protesty proti Lukašenkovi
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856576/bielorusko-v-niekolkych-mestach-pokracovali-protesty-proti-lukasenkovi/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/0CMFQVwiS3-kV3J90Uv1gg.320~D-chodcovia-protestuj-proti-Alexandarovi-Luka-enkovi.jpg?t=LzB4MzozMjB4MTgzLzE1OHgxMDYvc21hcnQ&h=prxFwHNADbX61294ZAVPPg&e=2145916800&v=3" />
|
||||
<br/>
|
||||
Denné demonštrácie sa konajú v Bielorusku od prezidentských volieb zo začiatku vlaňajšieho augusta.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 17:49:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>K Slovensku sa približuje arktický vzduch</title>
|
||||
<link>https://www.aktuality.sk/clanok/856587/k-slovensku-sa-priblizuje-arkticky-vzduch/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/p2tnVTC8Qxa26IHd5-AhPg.320~Ilustra-n-foto-J-no-kov-diery.jpg?t=LzE1OHgxMDYvc21hcnQ&h=7caByfMIXHR9QDBy1Lqk2g&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
K Slovensku sa približuje arktický vzduch
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856587/k-slovensku-sa-priblizuje-arkticky-vzduch/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/p2tnVTC8Qxa26IHd5-AhPg.320~Ilustra-n-foto-J-no-kov-diery.jpg?t=LzE1OHgxMDYvc21hcnQ&h=7caByfMIXHR9QDBy1Lqk2g&e=2145916800&v=2" />
|
||||
<br/>
|
||||
SHMÚ vydal na nedeľnú noc aj výstrahy pred nízkymi teplotami. Pri výstrahe prvého stupňa môžu teploty klesnúť až do mínus 17 stupňov Celzia.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 17:22:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Vodička skončila so Škodou Felicia v Kunovskej priehrade</title>
|
||||
<link>https://www.aktuality.sk/clanok/856575/policia-vodicka-skoncila-so-skodou-felicia-v-kunovskej-priehrade/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/mEabrdgHQ5H-YvTISg66wg.320~Vodi-ka-skon-ila-so-kodou-Felicia-v-Kunovskej-priehrade.jpg?t=LzE1OHgxMDYvc21hcnQ&h=s85yNFKC_KFi3hT26TWlCw&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Vodička skončila so Škodou Felicia v Kunovskej priehrade
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856575/policia-vodicka-skoncila-so-skodou-felicia-v-kunovskej-priehrade/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/mEabrdgHQ5H-YvTISg66wg.320~Vodi-ka-skon-ila-so-kodou-Felicia-v-Kunovskej-priehrade.jpg?t=LzE1OHgxMDYvc21hcnQ&h=s85yNFKC_KFi3hT26TWlCw&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Asi po dvoch hodinách sa jej podarilo z auta dostať a požiadala o pomoc okoloidúce auto.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 17:13:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>V čínskej bani, v ktorej zavalilo 22 baníkov, zrejme objavili známky života</title>
|
||||
<link>https://www.aktuality.sk/clanok/856556/cina-v-bani-v-ktorej-zavalilo-22-banikov-zrejme-objavili-znamky-zivota/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/P2BOOeGhSDO7oW2gT7Q_4g.320~V-buch-v-bani-v-meste-chi-sia-vo-v-chodo-nskej-provincii-an-tung.jpg?t=LzE1OHgxMDYvc21hcnQ&h=KmrXmPCrbOr5vJo0SSZa0w&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
V čínskej bani, v ktorej zavalilo 22 baníkov, zrejme objavili známky života
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856556/cina-v-bani-v-ktorej-zavalilo-22-banikov-zrejme-objavili-znamky-zivota/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/P2BOOeGhSDO7oW2gT7Q_4g.320~V-buch-v-bani-v-meste-chi-sia-vo-v-chodo-nskej-provincii-an-tung.jpg?t=LzE1OHgxMDYvc21hcnQ&h=KmrXmPCrbOr5vJo0SSZa0w&e=2145916800&v=2" />
|
||||
<br/>
|
||||
V bani v čase nehody prebiehali stavebné práce.
|
||||
|
||||
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 16:49:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Pri pobreží Turecka sa potopila nákladná loď s Ukrajincami a Rusmi, hlásia obete</title>
|
||||
<link>https://www.aktuality.sk/clanok/856570/pri-pobrezi-turecka-sa-potopila-nakladna-lod-s-ukrajincami-a-rusmi-hlasia-obete/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/H795qTJHRZaBIQjZoBwNyw.320~ilustra-n-foto.jpg?t=LzB4MjozMjB4MTgyLzE1OHgxMDYvc21hcnQ&h=evSZWlnugRGixpDhm7f-9A&e=2145916800&v=1</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Pri pobreží Turecka sa potopila nákladná loď s Ukrajincami a Rusmi, hlásia obete
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856570/pri-pobrezi-turecka-sa-potopila-nakladna-lod-s-ukrajincami-a-rusmi-hlasia-obete/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/H795qTJHRZaBIQjZoBwNyw.320~ilustra-n-foto.jpg?t=LzB4MjozMjB4MTgyLzE1OHgxMDYvc21hcnQ&h=evSZWlnugRGixpDhm7f-9A&e=2145916800&v=1" />
|
||||
<br/>
|
||||
Na palube bolo 12 členov posádky - dvaja Rusi a desať Ukrajincov, vyplýva z aktualizovaných údajov tureckého ministerstva dopravy.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 16:12:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>OĽANO: Z poslancov hnutia sa prednostne očkovať nebude nikto</title>
|
||||
<link>https://www.aktuality.sk/clanok/856546/olano-z-poslancov-hnutia-sa-prednostne-ockovat-nebude-nikto/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/Iir7W_-ZRHuRYKjFfGvQNw.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=tTKDjpcr-WCck0ii_K7hPw&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
OĽANO: Z poslancov hnutia sa prednostne očkovať nebude nikto
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856546/olano-z-poslancov-hnutia-sa-prednostne-ockovat-nebude-nikto/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/Iir7W_-ZRHuRYKjFfGvQNw.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=tTKDjpcr-WCck0ii_K7hPw&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Ponuku predsedu NR SR Borisa Kollára na očkovanie organizované NR SR nevyužijú ani poslanci z klubu OĽANO nad 65 rokov či poslanci s chronickým ochorením, uviedlo hnutie.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 15:45:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Ministerka Kolíková pripustila možné rozloženie plošného testovania na viac dní</title>
|
||||
<link>https://www.aktuality.sk/clanok/856564/monitor-m-kolikova-pripustila-mozne-rozlozenie-plosneho-testovania-na-viac-dni/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/oiH-LtzgSfKgzwjIo3P-xg.320~M-ria-Kol-kov.jpg?t=LzE1OHgxMDYvc21hcnQ&h=EmcC1VWiBAcwU58K_vHI4w&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Ministerka Kolíková pripustila možné rozloženie plošného testovania na viac dní
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856564/monitor-m-kolikova-pripustila-mozne-rozlozenie-plosneho-testovania-na-viac-dni/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/oiH-LtzgSfKgzwjIo3P-xg.320~M-ria-Kol-kov.jpg?t=LzE1OHgxMDYvc21hcnQ&h=EmcC1VWiBAcwU58K_vHI4w&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Diskusia vo vláde sa podľa Kolíkovej týka aj toho, či treba viac sprísniť opatrenia a čo by to pre Slovensko znamenalo.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 15:13:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Pellegrini pripúšťa referendovú kampaň pri testovaní, Kolíková hovorí o zneužívaní situácie</title>
|
||||
<link>https://www.aktuality.sk/clanok/856555/monitor-pellegrini-pripusta-referendovu-kampan-pri-testovani-kolikova-namieta/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/DFeuPDb7TKnoEHGyUmPxog.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=EwBwCDY2OSSoh2F18-MowQ&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Pellegrini pripúšťa referendovú kampaň pri testovaní, Kolíková hovorí o zneužívaní situácie
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856555/monitor-pellegrini-pripusta-referendovu-kampan-pri-testovani-kolikova-namieta/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/DFeuPDb7TKnoEHGyUmPxog.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=EwBwCDY2OSSoh2F18-MowQ&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Ministerka spravodlivosti Mária Kolíková (Za ľudí) reagovala, že ak to Pellegrini ako expremiér urobí, bude to hrubé podkopávanie dôvery v štát, čo v tejto situácii nikomu nepomôže
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 14:46:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Infektológ Krčméry: Viem si predstaviť, že by boli seniori zaočkovaní do Veľkej noci</title>
|
||||
<link>https://www.aktuality.sk/clanok/856557/infektolog-krcmery-viem-si-predstavit-ze-by-boli-seniori-zaockovani-do-velkej-noci/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/-bkXS45NRwvx3oDMN4lryw.320.jpg?t=LzB4MTU6MzIweDE5NS8xNTh4MTA2L3NtYXJ0&h=wQoXY1pwdk-Qnto_a4zB4Q&e=2145916800&v=5</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Infektológ Krčméry: Viem si predstaviť, že by boli seniori zaočkovaní do Veľkej noci
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856557/infektolog-krcmery-viem-si-predstavit-ze-by-boli-seniori-zaockovani-do-velkej-noci/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/-bkXS45NRwvx3oDMN4lryw.320.jpg?t=LzB4MTU6MzIweDE5NS8xNTh4MTA2L3NtYXJ0&h=wQoXY1pwdk-Qnto_a4zB4Q&e=2145916800&v=5" />
|
||||
<br/>
|
||||
Na margo toho, prečo sprísnenie opatrení neprišlo ešte pred Vianocami, Krčméry uviedol, že ústredný krízový štáb sa nevedel dohodnúť, či bude skôr plošné testovanie a potom lockdown, alebo naopak.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 14:36:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Maroš Šefčovič: Vakcín bude dostatok na zaočkovanie vyše 80 % Európanov</title>
|
||||
<link>https://www.aktuality.sk/clanok/856544/maros-sefcovic-vakcin-bude-dostatok-na-zaockovanie-vyse-80-europanov/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/JxrtwXnyTF20fvkTGgU86g.320~Maro-ef-ovi.jpg?t=LzB4MzozMjB4MTgzLzE1OHgxMDYvc21hcnQ&h=0w-3LT932CbyFOGMzXGXDQ&e=2145916800&v=3</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Maroš Šefčovič: Vakcín bude dostatok na zaočkovanie vyše 80 % Európanov
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856544/maros-sefcovic-vakcin-bude-dostatok-na-zaockovanie-vyse-80-europanov/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/JxrtwXnyTF20fvkTGgU86g.320~Maro-ef-ovi.jpg?t=LzB4MzozMjB4MTgzLzE1OHgxMDYvc21hcnQ&h=0w-3LT932CbyFOGMzXGXDQ&e=2145916800&v=3" />
|
||||
<br/>
|
||||
Na pôde ministerskej rady by sa malo v pondelok debatovať aj o tom, že by bolo dobré, keby všetky krajiny Únie minimálne dvakrát týždenne zverejňovali údaje o tom, koľko dostala ktorá krajina vakcín a koľko je v danom štáte zaočkovaných ľudí.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 14:12:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Boris Kollár: Vakcinovať sa budú len poslanci v rizikovom veku a skupine</title>
|
||||
<link>https://www.aktuality.sk/clanok/856533/monitor-b-kollar-vakcinovat-sa-budu-len-poslanci-v-rizikovom-veku-a-skupine/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/Y824Wf-fRgHcRzr7rdsKkg.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=Ie6ihB3wClBg2c0UvZ-_-A&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Boris Kollár: Vakcinovať sa budú len poslanci v rizikovom veku a skupine
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856533/monitor-b-kollar-vakcinovat-sa-budu-len-poslanci-v-rizikovom-veku-a-skupine/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/Y824Wf-fRgHcRzr7rdsKkg.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=Ie6ihB3wClBg2c0UvZ-_-A&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Zároveň uviedol, že hoci dostal výzvu na očkovanie poslancov, požiadal poslanecké kluby, aby možnosť využili poslanci v rizikovom veku či rizikovej skupine.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 13:43:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Prieskum: Voľby by v januári vyhrala strana Hlas-SD, druhé je OĽANO</title>
|
||||
<link>https://www.aktuality.sk/clanok/856542/prieskum-volby-by-v-januari-vyhrala-strana-hlas-sd-druhe-je-olano/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/YBZLlaA5RUiWH1Xey7vgTw.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=_fZRRxFKwp3tQj23o_PX-w&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Prieskum: Voľby by v januári vyhrala strana Hlas-SD, druhé je OĽANO
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856542/prieskum-volby-by-v-januari-vyhrala-strana-hlas-sd-druhe-je-olano/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/YBZLlaA5RUiWH1Xey7vgTw.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=_fZRRxFKwp3tQj23o_PX-w&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Podľa prieskumu by voliť na začiatku januára prišlo 49 percent ľudí, možnosť, že by skôr prišli voliť, označilo 18 percent opýtaných.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 13:14:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>V sudánskom regióne Dárfúr sa opäť rozhoreli násilnosti, hlásia desiatky obetí</title>
|
||||
<link>https://www.aktuality.sk/clanok/856523/v-sudanskom-regione-darfur-sa-opat-rozhoreli-nasilnosti-hlasia-desiatky-obeti/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/uA_I-efVQYGQoErTU3JSsA.320~Nepokoje-v-Sud-ne.jpg?t=LzE1OHgxMDYvc21hcnQ&h=uT7a6qvCQm9EkLuNU13bMw&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
V sudánskom regióne Dárfúr sa opäť rozhoreli násilnosti, hlásia desiatky obetí
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856523/v-sudanskom-regione-darfur-sa-opat-rozhoreli-nasilnosti-hlasia-desiatky-obeti/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/uA_I-efVQYGQoErTU3JSsA.320~Nepokoje-v-Sud-ne.jpg?t=LzE1OHgxMDYvc21hcnQ&h=uT7a6qvCQm9EkLuNU13bMw&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Krvavé udalosti, ktoré prebiehajú od soboty rána, zanechali aj 97 zranených.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 12:49:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Prezidentka Čaputová o Matovičovom riadení krízy: Nevidím posun k lepšiemu</title>
|
||||
<link>https://www.aktuality.sk/clanok/856538/prezidentka-caputova-o-matovicovom-riadeni-krizy-nevidim-posun-k-lepsiemu/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/Er6pQuhATBTjzXx08cJn1Q.320~Prezidentka-Zuzana-ap-tov.jpg?t=LzE1OHgxMDYvc21hcnQ&h=I-C1vf5dqVT9MFPZjeJWXw&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Prezidentka Čaputová o Matovičovom riadení krízy: Nevidím posun k lepšiemu
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856538/prezidentka-caputova-o-matovicovom-riadeni-krizy-nevidim-posun-k-lepsiemu/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/Er6pQuhATBTjzXx08cJn1Q.320~Prezidentka-Zuzana-ap-tov.jpg?t=LzE1OHgxMDYvc21hcnQ&h=I-C1vf5dqVT9MFPZjeJWXw&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Podľa prezidentky je každý, kto má otázky k plošnému testovaniu, v súčasnosti označovaný ako triedny nepriateľ a sabotér.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 12:45:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Kollár verí, že vláda prijme kroky, Pellegrini hovorí o telenovele</title>
|
||||
<link>https://www.aktuality.sk/clanok/856527/monitor-b-kollar-veri-ze-vlada-prijme-kroky-p-pellegrini-hovori-o-telenovele/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/ivwyvG2YT9HIpfqqYuuvew.320~Boris-Koll-r-o-vy-etrovan-smrti-Lu-ansk-ho-a-d-vere-tefanovi-Hol-mu.jpg?t=LzE1OHgxMDYvc21hcnQ&h=-_6e5uQd2es41cufI1CA5w&e=2145916800&v=5</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Kollár verí, že vláda prijme kroky, Pellegrini hovorí o telenovele
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856527/monitor-b-kollar-veri-ze-vlada-prijme-kroky-p-pellegrini-hovori-o-telenovele/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/ivwyvG2YT9HIpfqqYuuvew.320~Boris-Koll-r-o-vy-etrovan-smrti-Lu-ansk-ho-a-d-vere-tefanovi-Hol-mu.jpg?t=LzE1OHgxMDYvc21hcnQ&h=-_6e5uQd2es41cufI1CA5w&e=2145916800&v=5" />
|
||||
<br/>
|
||||
Oponent v diskusii, nezaradený poslanec a líder strany Hlas Peter Pellegrini pripomenul, že vláda zatiaľ celý týždeň neúspešne rokuje o opatreniach a podobe plošného testovania.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 12:18:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Koronavírus: Spresnili odkiaľ pochádzajú nové prípady</title>
|
||||
<link>https://www.aktuality.sk/clanok/856515/koronavirus-spresnili-odkial-pochadzaju-nove-pripady/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/mEOdTGA1TlWbm6imAFlPEw.320~Ilustra-n-foto.jpg?t=LzE1OHgxMDYvc21hcnQ&h=Hf45ewlfaxiXMQG7Ywv3dg&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Koronavírus: Spresnili odkiaľ pochádzajú nové prípady
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856515/koronavirus-spresnili-odkial-pochadzaju-nove-pripady/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/mEOdTGA1TlWbm6imAFlPEw.320~Ilustra-n-foto.jpg?t=LzE1OHgxMDYvc21hcnQ&h=Hf45ewlfaxiXMQG7Ywv3dg&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Z ochorenia sa vyliečilo ďalších 2360 pacientov.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 12:09:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Parkoviská na bratislavskej Kolibe či Kamzíku sú aj dnes plné</title>
|
||||
<link>https://www.aktuality.sk/clanok/856513/parkoviska-na-bratislavskej-kolibe-ci-kamziku-su-aj-dnes-plne/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/doYZGE76SR7ykX8pn3p9VQ.320~Policajti-reguluj-parkovanie-na-pr-jazdovej-ceste-na-Kolibu.jpg?t=LzE1OHgxMDYvc21hcnQ&h=P2lm47a3Id2SrLXP2GWuMQ&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Parkoviská na bratislavskej Kolibe či Kamzíku sú aj dnes plné
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856513/parkoviska-na-bratislavskej-kolibe-ci-kamziku-su-aj-dnes-plne/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/doYZGE76SR7ykX8pn3p9VQ.320~Policajti-reguluj-parkovanie-na-pr-jazdovej-ceste-na-Kolibu.jpg?t=LzE1OHgxMDYvc21hcnQ&h=P2lm47a3Id2SrLXP2GWuMQ&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Polícia vyzvala ľudí, aby využili na cestovanie mestskú kromadnú dopravu.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 12:04:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Koronavírus: Záujem o testovanie je v Košiciach o polovicu nižší ako minule, výsledok príde aj ako sms</title>
|
||||
<link>https://www.aktuality.sk/clanok/856519/koronavirus-zaujem-o-testovanie-je-v-kosiciach-o-polovicu-nizsi-ako-minule-vysledok-pride-aj-ako-sms/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/RJuke6ltS8fy-BBNv29AxA.320~Otestova-sa-dobrovo-ne-m-u-aj-obyvatelia-Lun-ka-IX.jpg?t=LzE1OHgxMDYvc21hcnQ&h=8-YvSNilwUwKDCk8gM3mng&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Koronavírus: Záujem o testovanie je v Košiciach o polovicu nižší ako minule, výsledok príde aj ako sms
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856519/koronavirus-zaujem-o-testovanie-je-v-kosiciach-o-polovicu-nizsi-ako-minule-vysledok-pride-aj-ako-sms/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/RJuke6ltS8fy-BBNv29AxA.320~Otestova-sa-dobrovo-ne-m-u-aj-obyvatelia-Lun-ka-IX.jpg?t=LzE1OHgxMDYvc21hcnQ&h=8-YvSNilwUwKDCk8gM3mng&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Výsledok z testovania prostredníctvom SMS správy príde Košičanom za necelú polhodinu. Záujem je o päťdesiat percent nižší, ako pred týždňom.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 11:55:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Okresný súd Bratislava I vytýči termín s Liborom J. až v roku 2022</title>
|
||||
<link>https://www.aktuality.sk/clanok/856502/okresny-sud-bratislava-i-vytyci-termin-s-liborom-j-az-v-roku-2022/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/6GfwoHLnQs2J6wPGmLowLA.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=G18EoWTPBkpKPgzUWPTOLA&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Okresný súd Bratislava I vytýči termín s Liborom J. až v roku 2022
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856502/okresny-sud-bratislava-i-vytyci-termin-s-liborom-j-az-v-roku-2022/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/6GfwoHLnQs2J6wPGmLowLA.320.jpg?t=LzE1OHgxMDYvc21hcnQ&h=G18EoWTPBkpKPgzUWPTOLA&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Situáciu skomplikovalo, že doterajší sudca OS Bratislava I Roland Kemény je dlhodobo práceneschopný.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 11:41:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>V najvyšších polohách Tatier a Malej Fatre je zvýšené lavínové nebezpečenstvo</title>
|
||||
<link>https://www.aktuality.sk/clanok/856510/v-najvyssich-polohach-tatier-a-malej-fatre-je-zvysene-lavinove-nebezpecenstvo/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/EExpK-jPSxrOPHsafoJ5TA.320~Ilustra-n-sn-mka.jpg?t=LzE1OHgxMDYvc21hcnQ&h=ATWtH9xK3yzYtDkJzyfc8g&e=2145916800&v=2</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
V najvyšších polohách Tatier a Malej Fatre je zvýšené lavínové nebezpečenstvo
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856510/v-najvyssich-polohach-tatier-a-malej-fatre-je-zvysene-lavinove-nebezpecenstvo/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/EExpK-jPSxrOPHsafoJ5TA.320~Ilustra-n-sn-mka.jpg?t=LzE1OHgxMDYvc21hcnQ&h=ATWtH9xK3yzYtDkJzyfc8g&e=2145916800&v=2" />
|
||||
<br/>
|
||||
Vyššie stupne lavínového nebezpečenstva sú dôsledkom poslednej periódy sneženia, počas ktorej napadlo do 80 centimetrov nového snehu. Ten padal navyše za súčinnosti vetra.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 11:13:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Ako znášate rádioaktívne žiarenie? Odpoveď máte možno v čreve</title>
|
||||
<link>https://www.aktuality.sk/clanok/856293/ako-znasate-radioaktivne-ziarenie-odpoved-mate-mozno-v-creve/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/BgJ1bO2HS2vmxHdvHCDkVQ.320~Ilustra-n-foto.jpg?t=LzB4MDozMjB4MTc5LzE1OHgxMDYvc21hcnQ&h=rRV1VDNSgKQ8ABz2O1APxQ&e=2145916800&v=1</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Ako znášate rádioaktívne žiarenie? Odpoveď máte možno v čreve
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856293/ako-znasate-radioaktivne-ziarenie-odpoved-mate-mozno-v-creve/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/BgJ1bO2HS2vmxHdvHCDkVQ.320~Ilustra-n-foto.jpg?t=LzB4MDozMjB4MTc5LzE1OHgxMDYvc21hcnQ&h=rRV1VDNSgKQ8ABz2O1APxQ&e=2145916800&v=1" />
|
||||
<br/>
|
||||
Niektoré baktérie v čreve nás môžu chrániť aj pred rádioaktívnym žiarením, tvrdia vedci.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 11:00:00 +0100</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<title>Poslanci OĽANO navrhujú individuálne vzdelávanie umožniť i na druhom stupni</title>
|
||||
<link>https://www.aktuality.sk/clanok/856497/nrsr-poslanci-olano-navrhuju-individualne-vzdelavanie-umoznit-i-na-druhom-stupni/</link>
|
||||
<image>
|
||||
<url>https://t.aimg.sk/magaziny/NsNzYd7LSPStEHj2hYMmvA.320~Ilustra-n-foto.jpg?t=LzB4MTA6MzIweDE5MC8xNTh4MTA2L3NtYXJ0&h=MTq5wFYGv2Dv22UBS0-jAg&e=2145916800&v=4</url>
|
||||
<title>
|
||||
<![CDATA[
|
||||
Poslanci OĽANO navrhujú individuálne vzdelávanie umožniť i na druhom stupni
|
||||
]]>
|
||||
</title>
|
||||
<link>https://www.aktuality.sk/clanok/856497/nrsr-poslanci-olano-navrhuju-individualne-vzdelavanie-umoznit-i-na-druhom-stupni/</link>
|
||||
<width>150</width>
|
||||
<height>150</height>
|
||||
</image>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<img src="https://t.aimg.sk/magaziny/NsNzYd7LSPStEHj2hYMmvA.320~Ilustra-n-foto.jpg?t=LzB4MTA6MzIweDE5MC8xNTh4MTA2L3NtYXJ0&h=MTq5wFYGv2Dv22UBS0-jAg&e=2145916800&v=4" />
|
||||
<br/>
|
||||
Cieľom návrhu zákona je vyriešenie doterajšieho stavu, v ktorom nie je umožnené individuálne vzdelávanie žiakov na druhom stupni základných škôl.
|
||||
]]>
|
||||
</description>
|
||||
<pubDate>Sun, 17 Jan 2021 10:45:00 +0100</pubDate>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
1
Parser/Tests/ParserTests/Resources/allthis-partial.json
Normal file
1
Parser/Tests/ParserTests/Resources/allthis-partial.json
Normal file
File diff suppressed because one or more lines are too long
520
Parser/Tests/ParserTests/Resources/allthis.atom
Normal file
520
Parser/Tests/ParserTests/Resources/allthis.atom
Normal file
@ -0,0 +1,520 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
|
||||
xmlns:content="http://purl.org/rss/1.0/modules/content/"
|
||||
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:atom="http://www.w3.org/2005/Atom"
|
||||
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
|
||||
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
|
||||
>
|
||||
|
||||
<channel>
|
||||
<title>And now it’s all this</title>
|
||||
<atom:link href="http://leancrew.com/all-this/feed/" rel="self" type="application/rss+xml" />
|
||||
<link>http://leancrew.com/all-this</link>
|
||||
<description>I just said what I said and it was wrong. Or was taken wrong.</description>
|
||||
<lastBuildDate>Thu, 23 Nov 2017 21:08:29 +0000</lastBuildDate>
|
||||
<language>en-US</language>
|
||||
<sy:updatePeriod>hourly</sy:updatePeriod>
|
||||
<sy:updateFrequency>1</sy:updateFrequency>
|
||||
<generator>http://wordpress.org/?v=4.0</generator>
|
||||
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/>
|
||||
<atom:link rel="hub" href="http://aniat.superfeedr.com"/>
|
||||
|
||||
<item>
|
||||
<title>Last thoughts on modifier keys</title>
|
||||
<link>http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/</link>
|
||||
<pubDate>Thu, 23 Nov 2017 21:08:29 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/</guid>
|
||||
<description>
|
||||
<![CDATA[When I wrote the post about <a href="http://leancrew.com/all-this/2017/11/modifier-key-order/">ordering Mac modifier keys</a> a few days ago, I was thinking primarily about the proper order of the <em>symbols</em> when writing about a keyboard shortcut, like ⌃⌥⌘P.<sup id="fnref:preview"><a href="#fn:preview" rel="footnote">1</a></sup>. I mentioned parenthetically that this order isn’t always observed when people speak about keyboard shortcuts or when they write the names of the keys out fully, as in “Command-Shift-3 takes a screenshot.”]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>When I wrote the post about <a href="http://leancrew.com/all-this/2017/11/modifier-key-order/">ordering Mac modifier keys</a> a few days ago, I was thinking primarily about the proper order of the <em>symbols</em> when writing about a keyboard shortcut, like ⌃⌥⌘P.<sup id="fnref:preview"><a href="#fn:preview" rel="footnote">1</a></sup>. I mentioned parenthetically that this order isn’t always observed when people speak about keyboard shortcuts or when they write the names of the keys out fully, as in “Command-Shift-3 takes a screenshot.”</p>
|
||||
<p>Jason Snell, in both <a href="https://sixcolors.com/link/2017/11/the-order-of-modifier-keys-on-the-mac/">a post at Six Colors</a> and in conversation with John Siracusa on <a href="https://www.relay.fm/upgrade/168">the lastest episode of Upgrade</a>, took a stand against Apple’s ordering:<sup id="fnref:hig"><a href="#fn:hig" rel="footnote">2</a></sup></p>
|
||||
<blockquote>
|
||||
<p>Command is the commander! Command is the monarch of all keys! Command always comes first, in my book.</p>
|
||||
</blockquote>
|
||||
<p>Siracusa agreed, and so do I. The ⌘ key is, and has always been, the key that signals a keyboard shortcut. While other modifier keys are sometimes used without ⌘—in cursor control and text selection, for example—I can’t think of any Apple applications that don’t use ⌘ to signal a keyboard shortcut for a menu item. And that primacy in shortcuts to menu items is, I think, why Apple puts it last rather than first.</p>
|
||||
<p>Keyboard shortcuts are always presented right-justified along the right edge of the menu. The most common shortcuts are just ⌘ and a letter, like ⌘N to start a new document, for example. It’s typically the variations on the basic command that get additional modifier keys, like ⌥⌘N to start a new project. If that were presented in a menu as ⌘⌥N, the menu would look wrong because the ⌘ symbols wouldn’t line up.</p>
|
||||
<p>Here’s the <span class="menu">File</span> menu in Safari:</p>
|
||||
<p><img alt="Safari File menu" class="ss" src="http://leancrew.com/all-this/images2017/20171123-Safari%20File%20menu.png" title="Safari File menu"/></p>
|
||||
<p>There are two different <span class="menu">New</span> commands and three different <span class="menu">Close</span> commands. This, in Apple’s opinion (and mine), wouldn’t be right:</p>
|
||||
<p><img alt="Altered Safari File menu" class="ss" src="http://leancrew.com/all-this/images2017/20171123-Altered%20Safari%20File%20menu.png" title="Altered Safari File menu"/></p>
|
||||
<p>It’s not just having the ⌘ symbols aligned. The additional modifier symbols go in front <em>because</em> ⌘ is king and must sit next to the N or the W. The importance of the modifier decreases as you move away from the letter.</p>
|
||||
<p>It should go without saying—but I’ll say it anyway—that the letter (or number or whatever) key is the most important because <em>nothing</em> happens until it’s pressed.</p>
|
||||
<p>Having said all this, and despite agreeing with Apple’s symbol ordering, my ear for shortcut ordering works just like Jason’s and John’s. The main reason I use keyboard shortcut symbols in my posts instead of words is that I can read ⌥⇧⌘W and not be bothered because I don’t “hear” it as I read the symbols. “Option-Shift-Command-W,” on the other hand, gets sounded out in my head, and it sounds wrong.</p>
|
||||
<p>I suspect that’s why Apple’s own documentation sometimes gets the order wrong when the modifiers get written out as words. In speaking out the keys, “Command” is natural to put first because it announces that what’s coming is a keyboard shortcut.</p>
|
||||
<div class="footnotes">
|
||||
<hr/>
|
||||
<ol>
|
||||
<li id="fn:preview">
|
||||
<p>Which happens to be the shortcut I use for previewing a blog post locally before publishing it. <a href="#fnref:preview" rev="footnote">↩︎</a></p>
|
||||
</li>
|
||||
<li id="fn:hig">
|
||||
<p>In the original post, I said I didn’t know where the order was documented. A few people pointed me to both the <a href="https://developer.apple.com/macos/human-interface-guidelines/user-interaction/keyboard/">Human Interface Guidelines</a> and the <a href="https://itunes.apple.com/us/book/apple-style-guide/id1161855204?mt=11">Style Guide</a>, where Apple gives the proper order explicitly. <a href="#fnref:hig" rev="footnote">↩︎</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/11/last-thoughts-on-modifier-keys/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>My next Mac</title>
|
||||
<link>http://leancrew.com/all-this/2017/11/my-next-mac/</link>
|
||||
<pubDate>Wed, 22 Nov 2017 22:04:57 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/11/my-next-mac/</guid>
|
||||
<description>
|
||||
<![CDATA[Will probably be an iMac. I guess that spoils the suspense, doesn’t it?]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>Will probably be an iMac. I guess that spoils the suspense, doesn’t it?</p>
|
||||
<p>My iMac at work is the <a href="https://support.apple.com/kb/sp667?locale=en_US">27″ Late 2012 model</a>, the one that came out one step before Retina came to the iMac. I don’t regret buying it, as my previous iMac (a <a href="https://everymac.com/systems/apple/imac/specs/imac-core-2-duo-2.16-24-inch-specs.html">2006 model</a>, I think) was absolutely on its last legs—constantly swapping to hard disk and running hot. I hadn’t meant to wait so long to replace it, but there was <a href="https://buyersguide.macrumors.com/">a long delay</a> before that 2012 model came out, and I didn’t want to buy something that would be last year’s model almost as soon as I set it up.</p>
|
||||
<p>My home Mac is the venerable 2010 13″ MacBook Air, the first good Air. In the normal course of things, this would be the Mac I replace next, and I’ve been expecting to do so for a few years now. but…</p>
|
||||
<p>But Apple never came out with a Retina MacBook Air, choosing instead to go with the MacBook, which I find a little too far on the portable side of the portability/power spectrum. A couple of years ago, I had a crisis when <a href="http://leancrew.com/all-this/2015/06/beep-beep-beep/">my Air crapped out on me</a>. It seemed wrong to put money into a five-year-old machine, but I wasn’t enthused about any of the MacBooks in the lineup at the time. I didn’t know the just-released 2015 MacBook Pro would turn out to be the <a href="https://marco.org/2017/11/14/best-laptop-ever">best laptop ever made</a>, I didn’t want to spend MacBook Pro money on my home/travel machine.</p>
|
||||
<p>The $280 logic board upgrade turned out to be a good investment, as I’m now 2½ years into my rejuvenated Air. Yes, it takes a while to wake up when I open the lid. Yes, its 128 GB SSD is tiny. No, it can’t take advantage of many of the iOS integration features that newer Macs can. And no, I don’t think it’s a good idea to install High Sierra on it. But it’s given me 30 months of faithful use, much more than I expected at the time.</p>
|
||||
<p>The announcement of the Touch Bar last year made me certain I’d be getting a MacBook Pro with it. A software-configurable set of controls seemed perfect for someone who’s always ginning up little scripts. But no one seems to like it, possibly because its configurability isn’t especially open to users. Bummer.</p>
|
||||
<p>I’ve delayed the decision on my home Mac for such a long time that now my office Mac is long in the tooth, too. Still working fine for most tasks, but just a Core 2 Duo machine that often <a href="http://leancrew.com/all-this/2015/10/dealing-with-a-recalcitrant-pdf/">makes me wait</a> to scroll through long PDFs of scanned engineering and architectural drawings, something I need to do at work quite often. And no Retina.</p>
|
||||
<p>So it looks like my best bet is to buy a new iMac for work and bring my current office iMac home. This will put the power where I need it the most and will give me extra ooomph here at home. Especially with disk space (3 TB vs. 128 GB) and RAM (24GB vs. 4GB).</p>
|
||||
<p>It will be weird, though, as I haven’t had a desktop computer here at home in a dozen years. Will I enjoy being tethered to one spot in the house? And what about a travel computer?</p>
|
||||
<p>Both of these questions are made less pressing by the device I’m typing this on: a 9.7″ iPad Pro. While I agree with <a href="http://www.macdrifter.com/2017/11/the-mac-still-feels-like-home.html">Gabe</a> that it is by no means a Mac substitute, it can handle a lot of what I do at home and virtually everything I need to do on the road.<sup id="fnref:gabe"><a href="#fn:gabe" rel="footnote">1</a></sup></p>
|
||||
<p>As for which iMac, I think I’ll settle on a middle-of-the-road 27″ configuration with a 3TB Fusion drive. Sort of the 2017 of what I bought in 2012.</p>
|
||||
<div class="footnotes">
|
||||
<hr/>
|
||||
<ol>
|
||||
<li id="fn:gabe">
|
||||
<p>I bought the iPad Pro last year as a sort of experiment to find out how comfortable I’d be working on it. I intend to write a full post about the results of that experiment soon, but in the meantime, you really should read <a href="http://www.macdrifter.com/2017/11/the-mac-still-feels-like-home.html">Gabe’s post over at Macdrifter</a>. I’ll probably use his post as a jumping-off point. And there may be a quiz. <a href="#fnref:gabe" rev="footnote">↩︎</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/11/my-next-mac/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Modifier key order</title>
|
||||
<link>http://leancrew.com/all-this/2017/11/modifier-key-order/</link>
|
||||
<pubDate>Mon, 20 Nov 2017 02:22:59 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/11/modifier-key-order/</guid>
|
||||
<description>
|
||||
<![CDATA[If you write about Mac keyboard shortcuts, as I did yesterday, you should know how to do it right. Just as there’s a <a href="https://dictionary.cambridge.org/grammar/british-grammar/adjectives-order">proper order for adjectives</a> in English, there’s a proper order for listing the modifier keys in a shortcut.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>If you write about Mac keyboard shortcuts, as I did yesterday, you should know how to do it right. Just as there’s a <a href="https://dictionary.cambridge.org/grammar/british-grammar/adjectives-order">proper order for adjectives</a> in English, there’s a proper order for listing the modifier keys in a shortcut.</p>
|
||||
<p>I haven’t found any documentation for this, but Apple’s preferred order is clear in how they show the modifiers in menus and how they’re displayed in the Keyboard Shortcuts Setting.</p>
|
||||
<p><img alt="Canonical Mac modifier key order" class="ss" src="http://leancrew.com/all-this/images2017/20171119-Canonical%20Mac%20modifier%20key%20order.png" title="Canonical Mac modifier key order"/></p>
|
||||
<p>The order is similar to how you see them down at the bottom left of your keyboard.</p>
|
||||
<p><img alt="Modifier keys" class="ss" src="http://leancrew.com/all-this/images2017/20171119-Modifier%20keys.jpg" title="Modifier keys"/></p>
|
||||
<p>Control (⌃), Option (⌥), and Command (⌘) always go in that order. The oddball is the Shift(⇧) key, which sneaks in just in front of Command.</p>
|
||||
<p>Keyboard Maestro recognizes this standard order and presents its “hot key” shortcut the same way.</p>
|
||||
<p><img alt="Keyboard Maestro hot key field" class="ss" src="http://leancrew.com/all-this/images2017/20171119-Keyboard%20Maestro%20hot%20key%20field.png" title="Keyboard Maestro hot key field"/></p>
|
||||
<p>(When people speak about keyboard shortcuts, it’s not uncommon to put Command first, e.g., “Command-Shift-3 takes a screenshot.” I’ve seen it written out that way, too. Apple is usually pretty careful to use the same order when using words as when using symbols. <a href="https://support.apple.com/en-us/HT201361">This page</a>, for example, uses “Shift-Command-3,” to match the ⇧⌘3 you’d see in the Keyboard Shortcut Settings. But even Apple slips up. On <a href="https://support.apple.com/en-us/HT201236">the grand Mac keyboard shortcut page</a>, there are a few instances of “Command-Shift” instead of “Shift-Command.”)</p>
|
||||
<p>The last bit of standard syntax is that the letter key in the shortcut (if there is a letter) is always presented as a capital, even when the Shift key isn’t used. I suspect this was a serious topic of discussion at Bandley 3 back in the early 80s as the Mac was being developed. They got it right. When entering a keyboard shortcut, you’re not typing a letter, you’re pressing a set of physical keys on the keyboard in front of you. The symbols on the letter keys are capitals, so that’s the appropriate way to identify those keys.</p>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/11/modifier-key-order/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Command-E</title>
|
||||
<link>http://leancrew.com/all-this/2017/11/command-e/</link>
|
||||
<pubDate>Sun, 19 Nov 2017 02:02:50 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/11/command-e/</guid>
|
||||
<description>
|
||||
<![CDATA[Earlier this evening, Merlin tweeted out some advice we should all heed:]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>Earlier this evening, Merlin tweeted out some advice we should all heed:</p>
|
||||
<blockquote class="twitter-tweet" data-lang="en"><p dir="ltr" lang="en">Per today's <a href="https://twitter.com/thetalkshow?ref_src=twsrc%5Etfw">@thetalkshow</a>, here's an Apple Support doc that can change your life. I dare you not to find something new here.<a href="https://t.co/37FuPPUbYK">https://t.co/37FuPPUbYK</a></p>— Merlin Mann (@hotdogsladies) <a href="https://twitter.com/hotdogsladies/status/932031370800529408?ref_src=twsrc%5Etfw">November 18, 2017</a></blockquote>
|
||||
<script async="" charset="utf-8" src="https://platform.twitter.com/widgets.js"></script>
|
||||
<p>Even though I’ve been using a Mac for 32 years (albeit with an 8-year Linux hiatus) there were a few keyboard shortcuts in there I didn’t know about. And, just as important, several that I once knew but had fallen out of the habit of using.</p>
|
||||
<p>But one of my favorites—and a big convenience when writing code or prose—isn’t on the <a href="https://support.apple.com/en-ae/HT201236">Mac keyboard shortcut page</a>. I wonder if the folks at Apple have forgotten it.</p>
|
||||
<p>It has to do with finding text in a document. You know about ⌘F to specify a <span class="menu">Find</span>. That usually brings up a window or other control into which you can type (or paste) the text you’re going to be searching for. Often, there are options for controlling how the search will be performed and for replacement text. Here’s the Find window from BBEdit,</p>
|
||||
<p><img alt="BBEdit Find window" class="ss" src="http://leancrew.com/all-this/images2017/20171118-BBEdit%20Find%20window.png" title="BBEdit Find window"/></p>
|
||||
<p>here’s the Find window from Pages,</p>
|
||||
<p><img alt="Pages Find window" class="ss" src="http://leancrew.com/all-this/images2017/20171118-Pages%20Find%20window.png" title="Pages Find window"/></p>
|
||||
<p>and here’s TextEdit, which pops up a little Find section in the main window below the ruler,</p>
|
||||
<p><img alt="TextEdit with Find section" class="ss" src="http://leancrew.com/all-this/images2017/20171118-TextEdit%20with%20Find%20section.png" title="TextEdit with Find section"/></p>
|
||||
<p>You probably also know about ⌘G, which is usually given the menu name <span class="menu">Find Next</span>. It repeats the last <span class="menu">Find</span> command, allowing you to step through all the occurrences of that text in the document. There are usually other ways to cycle through all the found instances—Pages and TextEdit have arrow buttons you can click on—but ⌘G is the traditional way.<sup id="fnref:page"><a href="#fn:page" rel="footnote">1</a></sup></p>
|
||||
<p>A common situation, especially when looking through long documents or source code files, is to see a particular string of text and want to find other instances of it. You may, for example, see the definition of a function and want to search out where that function gets called later in the code. One way to do that would be to select the function name, copy it, ⌘F to bring up the text entry field, paste, and then ⌘G your way through the rest of the document.<sup id="fnref:all"><a href="#fn:all" rel="footnote">2</a></sup> But the copy/⌘F/paste dance is a little clumsy.</p>
|
||||
<p>Which is where ⌘E, which is typically given a menu name like <span class="menu">Use Selection for Find</span>, comes in.</p>
|
||||
<p><img alt="BBEdit Search menu" class="ss" src="http://leancrew.com/all-this/images2017/20171118-BBEdit%20Search%20menu.png" title="BBEdit Search menu"/></p>
|
||||
<p>Select the text you want to search for and hit ⌘E. That turns the selection into the search text, and you can go straight to ⌘G to walk through all the other occurrences.</p>
|
||||
<p>I use ⌘E all the time when searching through code for function and variable names, as in the example above. It also works in Safari, Chrome, Preview, and PDF Expert, so I do similar quick searching on words I come across as I read news articles, blogs, reports, papers, etc.</p>
|
||||
<p>⌘E works in other well-behaved applications, too, so you should give it a try. Note that “well-behaved applications” excludes MS Word. In Word, ⌘E centers the paragraph of text containing the selection. Of course.</p>
|
||||
<div class="update">
|
||||
<p><strong>Update Nov 19, 2017 8:40 AM</strong><br/>
|
||||
A couple of things I forgot to include:</p>
|
||||
<p>First, ⌘E in the Finder has, somewhat ironically, no relationship to finding. It ejects or unmounts the selected disk, assuming there is a selected disk. This shortcut is listed on Apple’s <a href="https://support.apple.com/en-ae/HT201236">Mac shortcut page</a> (which is why I was going to mention it), and a couple of people reminded me of it after the post was published. I guess I forgot about it because it’s a command I never use; I eject by right-clicking and selecting <span class="menu">Eject</span> from the menu that pops up.</p>
|
||||
<p>Second, editors and word processors on iOS have not inherited this useful shortcut from the Mac. There is, of course, no ⌘ key on the software keyboard, but many of the traditional Mac shortcuts that use ⌘, ⌥, and ⇧ for moving the cursor and selecting text do work on iOS when you use an external keyboard. ⌘F and ⌘G (and its backward cousin, ⇧⌘G) often work in iOS, too, but ⌘E didn’t make the cut. Too bad.</p>
|
||||
<p><img alt="Keyboard shortcuts in iOS Notes" class="ss" src="http://leancrew.com/all-this/images2017/20171119-Keyboard%20shortcuts%20in%20iOS%20Notes.jpg" title="Keyboard shortcuts in iOS Notes" width="60%"/></p>
|
||||
</div>
|
||||
<div class="footnotes">
|
||||
<hr/>
|
||||
<ol>
|
||||
<li id="fn:page">
|
||||
<p>I believe ⌘G was originally used as the shortcut for <span class="menu">Go To Page…</span> in MacWrite, but it’s been <span class="menu">Find Next</span> for ages. <a href="#fnref:page" rev="footnote">↩</a></p>
|
||||
</li>
|
||||
<li id="fn:all">
|
||||
<p>Yes, source code editors in general, and BBEdit in particular, have other ways to find all the instances of a string of text, and those ways may be more appropriate in some situations. But stepping your way through each instance in turn is often the best way to see the string in full context. <a href="#fnref:all" rev="footnote">↩</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/11/command-e/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Converting fractions to decimal values</title>
|
||||
<link>http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/</link>
|
||||
<pubDate>Mon, 13 Nov 2017 04:35:39 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/</guid>
|
||||
<description>
|
||||
<![CDATA[Recently, I’ve been getting Excel spreadsheets from clients that contain measurements in inches and/or fractions of an inch. In some cases, the cells with these measurements actually contain numbers but are displayed as fractions using one of the many formatting options Excel has.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>Recently, I’ve been getting Excel spreadsheets from clients that contain measurements in inches and/or fractions of an inch. In some cases, the cells with these measurements actually contain numbers but are displayed as fractions using one of the many formatting options Excel has.</p>
|
||||
<p><img alt="Excel cell formatting options" class="ss" src="http://leancrew.com/all-this/images2017/20171112-Excel%20cell%20formatting%20options.png" title="Excel cell formatting options"/></p>
|
||||
<p>In these cases, it’s easy for me to change the formatting to a standard decimal representation before exporting the sheet as a CSV file.</p>
|
||||
<div class="sidebar">
|
||||
<p><strong>Aside</strong><br/>
|
||||
The only reason I own Excel is to be able to open spreadsheets that other people send me and export them into some other usable format. This wasn’t always the case. I used the hell out of Excel in the late 80s and early 90s, when it was still a proper Mac app. But somewhere there in the early to mid 90s, Microsoft turned Excel into a Windows app that ran on the Mac and ruined its user interface. I stopped using it shortly thereafter. In the quarter-century since, it’s only gotten worse. Note, for example, the absurd layout of the dialog box above.</p>
|
||||
<p>Small Excel spreadsheets usually get moved over to Numbers, a considerably less powerful spreadsheet, but one that works the way a normal Mac app works. Large Excel spreadsheets get converted to CSV (often after a short visit to Numbers, as I’ve had trouble with Excel’s CSV conversion) so I can query and manipulate the data with <a href="http://pandas.pydata.org/">Pandas</a>. Why don’t I just open the Excel file in Pandas <a href="https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_excel.html">directly</a>? Because the spreadsheets I get are so filled with cruft, <code>read_excel</code> can’t be trusted.</p>
|
||||
</div>
|
||||
<p>Often, though, the measurements are entered as text. This is done by prefixing the data with a single quote mark, e.g.,</p>
|
||||
<pre><code>'2 5/16
|
||||
</code></pre>
|
||||
<p>or maybe</p>
|
||||
<pre><code>'2-5/16
|
||||
</code></pre>
|
||||
<p>These need to be converted into a decimal representation before exporting to CSV, so Pandas will recognize them as numbers rather than strings.<sup id="fnref:pandas"><a href="#fn:pandas" rel="footnote">1</a></sup></p>
|
||||
<p>To handle this situation, I created a Keyboard Maestro called Floatize. To use Floatize, I</p>
|
||||
<ul>
|
||||
<li>Select the column of data I want to convert.</li>
|
||||
<li>Copy it to the clipboard.</li>
|
||||
<li>Run Floatize, which converts the clipboard from a fractional representation to decimal.</li>
|
||||
<li>Paste the result back into the column.</li>
|
||||
</ul>
|
||||
<p>Here’s the macro:</p>
|
||||
<p><img alt="Floatize Keyboard Maestro macro" class="ss" src="http://leancrew.com/all-this/images2017/20171112-Floatize%20Keyboard%20Maestro%20macro.png" title="Floatize Keyboard Maestro macro" width="80%"/></p>
|
||||
<p>It takes the clipboard, sends it to a script as standard input, and then puts the output back into the clipboard. Here’s the Python script it runs:</p>
|
||||
<pre><code>python:
|
||||
1: #!/usr/bin/env python
|
||||
2:
|
||||
3: from fractions import Fraction
|
||||
4: import re
|
||||
5: import sys
|
||||
6:
|
||||
7: def floatize(s):
|
||||
8: if s in ['', 'n/a', 'none']:
|
||||
9: return ''
|
||||
10: else:
|
||||
11: try:
|
||||
12: num = float(s)
|
||||
13: except ValueError:
|
||||
14: t = re.sub(r'(.*\d) *- *(\d.*)', r'\1 \2', s)
|
||||
15: if t[0] == '-':
|
||||
16: num = -float(sum(Fraction(x) for x in t[1:].split(None, 1)))
|
||||
17: else:
|
||||
18: num = float(sum(Fraction(x) for x in t.split(None, 1)))
|
||||
19: return "{:f}".format(num)
|
||||
20:
|
||||
21: for line in sys.stdin.read().splitlines():
|
||||
22: item = line.strip().lower()
|
||||
23: print floatize(item)
|
||||
</code></pre>
|
||||
<p>The bulk of the fraction parsing is done by the <a href="https://docs.python.org/2.7/library/fractions.html"><code>fractions</code> module</a> in either Line 16 or Line 18. The rest of the script is an attempt to manipulate the oddball types of data I get into a format that <code>fractions</code> understands. Here’s what it can handle:</p>
|
||||
<ul>
|
||||
<li>Blank, <code>n/a</code> or <code>none</code> entries get turned into an empty string (Lines 8–9). Note that entries are converted to lower case in Line 22, before being passed to the <code>floatize</code> function, so this handles <code>N/A</code> and <code>None</code>, too.</li>
|
||||
<li>Entries already in decimal format get converted to a <code>float</code> (Line 12) and then output in decimal format (Line 19). This is basically a pass-through.</li>
|
||||
<li>Entries with a dash between the whole and fractional parts get the dash removed (Line 14).</li>
|
||||
<li>Negative entries (Line 15) have their whole and fractional parts added together with the sign reversed (Line 16), and then output in decimal format (Line 19).</li>
|
||||
<li>Positive entries have their whole and fractional parts added together (Line 18), and then output in decimal format (Line 19).</li>
|
||||
<li>Entries that don’t look like any of the above cause an error.</li>
|
||||
</ul>
|
||||
<p>The macro works perfectly in Numbers and BBEdit, but not in Excel. For some reason, nothing happens to the clipboard if I run the Floatize macro while Excel is the active application. I can, however, copy the column of fractions, activate another app, run Floatize, switch back to Excel and paste the converted clipboard. Another example of Excel not acting like a proper Mac app and another reason I move data out of Excel as quickly as I can.</p>
|
||||
<p>Despite its inability to work inside Excel, this macro has been very handy over the past couple of weeks. I’ve used it to clean up several data sets that were loaded with fractions.</p>
|
||||
<p>By the way, I will block anyone who tweets that I wouldn’t have this problem if I used the metric system.</p>
|
||||
<div class="update">
|
||||
<p><strong>Update Nov 13, 2017 10:04 PM</strong>
|
||||
The Twitter consensus is my problem in using this macro in Excel stems from Excel’s clipboard dumbassery. It apparently uses a private clipboard instead of the system clipboard until you bring another application to the front. Hence the macro’s inability to convert the clipboard unless I switched away from Excel.</p>
|
||||
<p>Jimmy Hartington suggested this workaround:</p>
|
||||
<div class="bbpBox" id="t929938579085807616">
|
||||
<blockquote>
|
||||
<span class="twContent"><a href="http://twitter.com/drdrang">@drdrang</a> Excel does something weird with the clipboard as you noticed. <br/>Try this. Copy in Excel. Press Escape. Run the macro.<br/>
|
||||
By pressing Escape I think Excel stops messing with the clipboard.
|
||||
<br/><span class="twDecoration">— </span><span class="twRealName">Jimmy Hartington</span><span class="twDecoration"> (</span><a href="http://twitter.com/jimmyhartington"><span class="twScreenName">@jimmyhartington</span></a><span class="twDecoration">) </span><a href="https://twitter.com/jimmyhartington/status/929938579085807616"><span class="twTimeStamp">Nov 12 2017 11:06 PM</span></a><span class="twDecoration"></span></span>
|
||||
</blockquote>
|
||||
</div>
|
||||
<p>This works perfectly and pointed me in the direction to make a better version of the macro, one that does all the copying, converting, and pasting:</p>
|
||||
<p><img alt="New Floatize Keyboard Maestro macro" class="ss" src="http://leancrew.com/all-this/images2017/20171113-New%20Floatize%20Keyboard%20Maestro%20macro.png" title="New Floatize Keyboard Maestro macro" width="80%"/></p>
|
||||
<p>Note the conditional action that simulates the pressing of the Escape key only if Excel is the front application. This is necessary because Escape causes Numbers to deselect the column, which screws up the paste action at the end of the macro.</p>
|
||||
<p>With this macro I can now select the column that needs converting and just press ⌃⌥⌘F to do the conversion in a single step. Thanks, Jimmy!</p>
|
||||
</div>
|
||||
<div class="footnotes">
|
||||
<hr/>
|
||||
<ol>
|
||||
<li id="fn:pandas">
|
||||
<p>There are probably ways to get Pandas to do the conversion as it imports the data, but I feel more comfortable—more in control—if I do it ahead of time. <a href="#fnref:pandas" rev="footnote">↩︎</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/11/converting-fractions-to-decimal-values/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>A modest proposal</title>
|
||||
<link>http://leancrew.com/all-this/2017/11/a-modest-proposal/</link>
|
||||
<pubDate>Tue, 07 Nov 2017 03:27:11 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/11/a-modest-proposal/</guid>
|
||||
<description>
|
||||
<![CDATA[With Apple sales and graphing <a href="http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/">on my mind</a>, I’d like to make a small complaint and suggestion to the folks at MacStories: some of your <a href="https://www.macstories.net/news/apple-q4-2017-results-52-6-billion-revenue-46-7-million-iphones-10-3-million-ipads-sold/">quarterly graphs</a> could use a little scrubbing; they’d be much easier on the eyes if they were cleaned up a bit.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>With Apple sales and graphing <a href="http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/">on my mind</a>, I’d like to make a small complaint and suggestion to the folks at MacStories: some of your <a href="https://www.macstories.net/news/apple-q4-2017-results-52-6-billion-revenue-46-7-million-iphones-10-3-million-ipads-sold/">quarterly graphs</a> could use a little scrubbing; they’d be much easier on the eyes if they were cleaned up a bit.</p>
|
||||
<p>I’m talking about the ones that look like this:</p>
|
||||
<p><img alt="MacStories unit sales graph" class="ss" src="http://leancrew.com/all-this/images2017/20171106-MacStories%20unit%20sales%20graph.jpg" title="MacStories unit sales graph"/></p>
|
||||
<p>My complaint is with the clutter along the x-axis. That uniform gray mass of text detracts from the data above it. And although putting a label at each quarter seems to provide more information to the reader, the reader actually gets less out of it because it’s too hard to read. The axis would be improved if it had just yearly Q1 labels. Maybe even one label every other year. Fewer labels would mean they could be turned back horizontal, which would be easier to read and would give more vertical space for the data.</p>
|
||||
<p>I suspect every data point is labeled because that’s the way Numbers wants to do it, and MacStories’ automated system for generating these graphs follows the Numbers defaults. But if you were making these graphs by hand, you’d never label the x-axis this way. An automated system shouldn’t, either.</p>
|
||||
<p>On the Mac, Numbers has a way to skip labels on the x-axis by selecting Custom Category Intervals from the Category Labels popup menu and then choosing how often to put the labels.</p>
|
||||
<p><img alt="Mac Numbers axis options" class="ss" src="http://leancrew.com/all-this/images2017/20171106-Mac%20Numbers%20axis%20options.png" title="Mac Numbers axis options"/></p>
|
||||
<p>Sadly, I can’t find that option in the iOS version. Maybe it’s tucked away in a less obvious place.</p>
|
||||
<p><img alt="iOS Numbers axis options" class="ss" src="http://leancrew.com/all-this/images2017/20171106-iOS%20Numbers%20axis%20options.jpg" title="iOS Numbers axis options" width="50%"/></p>
|
||||
<p>Personally, I’m not a fan of either version of Numbers when it comes to making graphs because I like way more control than they give. But Numbers (like most graphing programs and libraries) can make nice graphs if you’re willing to break away from its defaults.</p>
|
||||
<p>I don’t really want to pick on MacStories. Their graphs are as good as, if not better than, what you typically find in blogs and news sites. Most graphs, unfortunately, are based on the default settings of the software that made them, and come out looking a little clumsy.</p>
|
||||
<p>I read MacStories because it has stylish writing. I’d like its graphs to be just as stylish.</p>
|
||||
<div class="update">
|
||||
<p><strong>Update Nov 7, 2017 8:55 PM</strong><br/>
|
||||
I was informed on Twitter by two authoritative sources (<a href="https://twitter.com/greyham/status/927795367361441793">this one</a> and <a href="https://twitter.com/viticci/status/927830126481608705">this one</a>) that the MacStories graphs are made with Excel, not Numbers. I haven’t used Excel for graphing since the mid-90s, but even then it had more customization options than Numbers has now. So I’m crossing my fingers for cleaner x-axes in January’s graphs.</p>
|
||||
</div>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/11/a-modest-proposal/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Apple sales graphs and the iPhone 7</title>
|
||||
<link>http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/</link>
|
||||
<pubDate>Mon, 06 Nov 2017 02:53:19 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/</guid>
|
||||
<description>
|
||||
<![CDATA[Last week Apple released its 2017 Q4 (which everyone else’s calendar says is 2017 Q3) <a href="https://www.apple.com/newsroom/2017/11/apple-reports-fourth-quarter-results/">sales and revenue figures</a>, which means a boatload of graphs from all your fave rave Apple-oriented bloggers. I’m a few days late, as usual, despite having long ago written scripts to generate these graphs.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>Last week Apple released its 2017 Q4 (which everyone else’s calendar says is 2017 Q3) <a href="https://www.apple.com/newsroom/2017/11/apple-reports-fourth-quarter-results/">sales and revenue figures</a>, which means a boatload of graphs from all your fave rave Apple-oriented bloggers. I’m a few days late, as usual, despite having long ago written scripts to generate these graphs.</p>
|
||||
<p>I don’t make as many graphs as everyone else, partly because I’m lazy, but mainly because don’t care about revenue. My concern is the popularity of the devices I use, not how expensive they are. I want third-party developers to keep writing apps for them, and that means there has to be a market for them out there.</p>
|
||||
<p>Here’s the summary graph, with the sales figures for the three main Apple products. The dots are the quarterly sales and the lines are the four-quarter moving averages.</p>
|
||||
<p><img alt="Apple sales" class="ss" src="http://leancrew.com/all-this/images2017/20171105-Apple%20sales.png" title="Apple sales"/></p>
|
||||
<p>The iPhone dominates the scale of this graphs, so it’s helpful to also see the iPad and Mac on their own graphs.</p>
|
||||
<p><img alt="iPad sales" class="ss" src="http://leancrew.com/all-this/images2017/20171105-iPad%20sales.png" title="iPad sales"/></p>
|
||||
<p><img alt="Mac sales" class="ss" src="http://leancrew.com/all-this/images2017/20171105-Mac%20sales.png" title="Mac sales"/></p>
|
||||
<p>The good news is the second straight quarter of year-over-year increase in iPad sales after its long, well-documented slide since 2013. Additional good news is the Mac’s slow but consistent rise over the past year—four straight quarters of year-over-year sales increases. Imagine how much better the figures would be if people really liked the Touch Bar.</p>
|
||||
<p>I think its fair to say, though, that the iPhone 7 was something of a dud. It had a good start, but its non-intro quarters had almost exactly the same sales as the 6S had. Look at the final three quarters of each.</p>
|
||||
<p><img alt="Apple sales annotated" class="ss" src="http://leancrew.com/all-this/images2017/20171105-Apple%20sales%20annotated.png" title="Apple sales annotated"/></p>
|
||||
<p><a href="http://leancrew.com/all-this/2016/07/obligatory-apple-sales-post/">As I’ve argued before</a>, the iPhone 6S sales looked lackluster only in comparison to the gangbusters popularity of the iPhone 6. If one ignored the year of the 6, the 6S’s sales were more or less on the same upward trend as the previous three editions. The iPhone 7 was on that same trend for its first quarter but went dead flat after that.</p>
|
||||
<p>Apple’s report in January will be very interesting.</p>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/11/apple-sales-graphs-and-the-iphone-7/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Another one-off Keyboard Maestro macro</title>
|
||||
<link>http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/</link>
|
||||
<pubDate>Fri, 03 Nov 2017 02:02:41 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/</guid>
|
||||
<description>
|
||||
<![CDATA[Do you use Keyboard Maestro (or AppleScript or whatever) for one-time, throwaway macros as often as you should? I know I don’t, but I did put one together a couple of days ago and used a feature I’d never tried before.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>Do you use Keyboard Maestro (or AppleScript or whatever) for one-time, throwaway macros as often as you should? I know I don’t, but I did put one together a couple of days ago and used a feature I’d never tried before.</p>
|
||||
<p>I had one of those <a href="http://leancrew.com/all-this/2015/10/dealing-with-a-recalcitrant-pdf/">recalcitrant PDFs</a> that I often get from clients. This one was 25–30 pages long, each an E-sized floor plan drawing for a building. The drawings were all black-and-white, but the PDF had color annotations added. I needed to add my own annotations to most of the pages, but something about the format of the file made it very cumbersome to work with. I tried Preview, PDF Expert, and PDfpen Pro, and they all were glacially slow when panning, zooming, and switching pages.</p>
|
||||
<p>So I broke the file up into individual pages using <a href="https://www.pdflabs.com/tools/pdftk-server/">PDFtk</a>:</p>
|
||||
<pre><code>pdftk drawings.pdf burst
|
||||
</code></pre>
|
||||
<p>The single-page files didn’t make me wait for the spinning beach ball, so I was able to add my annotations quickly in <a href="https://pdfexpert.com/">PDF Expert</a>. Then came an impasse.</p>
|
||||
<p>I wanted to email the drawings with my annotations back to the client and to some other parties, but they were too big to fit in an email. I could use multiple emails, but that’s a recipe for losing some of the files. I could use a Dropbox link, but I had a sense that one of the other parties wouldn’t understand how that worked. What seemed best was to convert the files to JPEGs at the lowest legible resolution, zip them together, and send the zipped file in a single email.<sup id="fnref:zip"><a href="#fn:zip" rel="footnote">1</a></sup></p>
|
||||
<p>My normal practice would be to use <a href="https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/sips.1.html"><code>sips</code></a> for this, because I can issue a single command to convert any number of files. But I soon learned that <code>sips</code> doesn’t handle annotations properly when converting the format of a file from PDF to JPEG. In my brief testing, I found that neither my annotations or the ones that came from the client were visible in the converted JPEGs.</p>
|
||||
<p>Preview, though, <em>can</em> export a PDF as a JPEG with the annotations intact and visible. Which presumably means that <code>sips</code> and Preview are using different code bases for the conversion. Whatever.</p>
|
||||
<p>The problem with using Preview is I’d have to convert every file by hand. Not the most burdensome job I’ve ever had, but one that’s boring and susceptible to error. Enter Keyboard Maestro.</p>
|
||||
<p>Here’s the macro that exports the current file in Preview to a JPEG and closes it:</p>
|
||||
<p><img alt="Convert to JPEG macro" class="ss" src="http://leancrew.com/all-this/images2017/20171101-Convert%20to%20JPEG%20macro.png" title="Convert to JPEG macro" width="80%"/></p>
|
||||
<p>You’ll note there’s no step for setting the resolution for the exported file. That’s because once it’s set, it doesn’t change from one export to the next.</p>
|
||||
<p>I didn’t try to have the macro open each PDF in turn, because I didn’t trust myself to do that right. I just opened all of them and then ran the macro by pressing ⌃⌥⌘J repeatedly. The macro closed each file after exporting it, leaving the next window ready to be operated on.</p>
|
||||
<p>The new (for me) thing with this macro was the “and drag to” part at the bottom of the third step. That’s what moved the popup menu selection from <span class="menu">PDF</span> up to <span class="menu">JPEG</span>.</p>
|
||||
<p><img alt="Preview export sheet" class="ss" src="http://leancrew.com/all-this/images2017/20171102-Preview%20export%20sheet.png" title="Preview export sheet"/></p>
|
||||
<p>I figured out how much to drag by taking a screenshot like the one above and using a selection in <a href="https://flyingmeat.com/acorn/">Acorn</a> to measure the vertical distance from the center of <span class="menu">PDF</span> to the center of <span class="menu">JPEG</span>. No trial and error.</p>
|
||||
<p>With 25–30 files to convert, it’s possible I did save time with this macro. But the main reason I made it was to avoid the tedium and the likelihood of error on my part. These are not independent—I’m far more prone to make errors when the task is repetitive and doesn’t maintain my attention.</p>
|
||||
<div class="footnotes">
|
||||
<hr/>
|
||||
<ol>
|
||||
<li id="fn:zip">
|
||||
<p>No, a Dropbox link is really no more complicated than a zip file, but zip files are more familiar to more people. And although zipping JPEGs doesn’t make them smaller, it does package them up in a way that naive Windows users are usually comfortable with. It would be lovely if I always worked with people whose computer skills were trustworthy, but that’s not the world I live in. <a href="#fnref:zip" rev="footnote">↩︎</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/11/another-one-off-keyboard-maestro-macro/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Binomial baseball</title>
|
||||
<link>http://leancrew.com/all-this/2017/10/binomial-baseball/</link>
|
||||
<pubDate>Mon, 30 Oct 2017 18:14:47 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/10/binomial-baseball/</guid>
|
||||
<description>
|
||||
<![CDATA[While reading a recap of last night’s World Series games, I saw this statistic: of the 65 Series that have had a sixth game, the team with the 3–2 lead has won the Series 43 times. This was, I think, intended to show us that the Astros have a strong chance to beat out the Dodgers for the title. And they do. But not as strong as you might expect.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>While reading a recap of last night’s World Series games, I saw this statistic: of the 65 Series that have had a sixth game, the team with the 3–2 lead has won the Series 43 times. This was, I think, intended to show us that the Astros have a strong chance to beat out the Dodgers for the title. And they do. But not as strong as you might expect.</p>
|
||||
<p>If the teams were evenly balanced and each game independent of the others, we would expect the team with the 3–2 lead to win 75% of the time. 50% of the time they’d win the sixth game and the Series would be over; 25% of the time (50% of the other 50%) they’d win in the seventh game. So the leading team “should” have won the Series 48 or 49 times out of 65, not 43 times.</p>
|
||||
<p>Is this 5 or 6 game difference meaningful? For that we need to do some calculations using the <a href="https://en.wikipedia.org/wiki/Binomial_distribution">binomial distribution</a>. Python’s SciPy set of libraries has a subsection of statistical modules, including one for <a href="https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.binom.html">binomial distribution calculations</a>. We can import it this way:</p>
|
||||
<pre><code>python:
|
||||
from scipy.stats import binom
|
||||
</code></pre>
|
||||
<p>Let’s start by figuring out the probability that the leading team would win 43 times in 65 trials. With a 75% probability of winning the Series in each trial, the probability of 43 Series wins in 65 chances is calculated through</p>
|
||||
<pre><code>python:
|
||||
binom.pmf(43, 65, .75)
|
||||
</code></pre>
|
||||
<p>where the <code>pmf</code> function gets its name from the standard abbreviation for “<a href="https://en.wikipedia.org/wiki/Probability_mass_function">probability mass function</a>.” The answer is 0.029 or just under 3%. This makes it seem very unlikely that our assumption of 50–50 games would lead to only 43 Series wins for the leading team.</p>
|
||||
<p>But that isn’t the way these sorts of calculations are normally done. If we want to find out if a seemingly out-of-whack result is “statistically significant,” we should look at the probability of results that are at least as far away from our expectations as the actual result was. In our case, that means looking not only at the probability of 43 Series wins out of 65 chances, but also 42 wins, 41 wins, and so on. We then add up all of these “at least as weird” probabilities.</p>
|
||||
<p>The usual terminology for this sort of summation is “cumulative distribution function,” and the <code>binom</code> module has a function for it:</p>
|
||||
<pre><code>python:
|
||||
binom.cdf(43, 65, .75)
|
||||
</code></pre>
|
||||
<p>The result is 0.0695, or about 7%. Another way of looking at this is that if our assumption of 50–50 games were correct, there’s a 93% chance that the leading team would win the Series more than 43 times in 65 chances.</p>
|
||||
<p>In <a href="https://onlinecourses.science.psu.edu/statprogram/node/138">hypothesis testing</a>, the value 0.0695 is called the <em>p-value</em>,
|
||||
and it’s common in many fields to consider a result statistically significant if its p-value is less than 0.05. Using that criterion, we would not take the difference between our “null hypothesis” of 50–50 games and the World Series history as statistically significant.<sup id="fnref:two"><a href="#fn:two" rel="footnote">1</a></sup></p>
|
||||
<p>But it’s something for Dodgers fans to cling to.</p>
|
||||
<div class="footnotes">
|
||||
<hr/>
|
||||
<ol>
|
||||
<li id="fn:two">
|
||||
<p>Yes, I’ve been a little breezy here with my definition of null and alternate hypotheses and one-sided vs. two-sided rejection areas, but it’s just baseball. <a href="#fnref:two" rev="footnote">↩︎</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/10/binomial-baseball/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Judas</title>
|
||||
<link>http://leancrew.com/all-this/2017/10/judas/</link>
|
||||
<pubDate>Sat, 28 Oct 2017 14:33:47 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/10/judas/</guid>
|
||||
<description>
|
||||
<![CDATA[I can’t say I wasn’t warned. The concert’s promotional artwork (especially those fonts) and the previous two albums, <a href="https://geo.itunes.apple.com/us/album/shadows-in-the-night/id945762927?uo=4&at=10l4Fv"><em>Shadows in the Night</em></a> and <a href="https://geo.itunes.apple.com/us/album/fallen-angels/id1099677284?uo=4&at=10l4Fv"><em>Fallen Angels</em></a>, were strong clues that I was going to be seeing a Bob Dylan infected by the Great American Songbook.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>I can’t say I wasn’t warned. The concert’s promotional artwork (especially those fonts) and the previous two albums, <a href="https://geo.itunes.apple.com/us/album/shadows-in-the-night/id945762927?uo=4&at=10l4Fv"><em>Shadows in the Night</em></a> and <a href="https://geo.itunes.apple.com/us/album/fallen-angels/id1099677284?uo=4&at=10l4Fv"><em>Fallen Angels</em></a>, were strong clues that I was going to be seeing a Bob Dylan infected by the Great American Songbook.</p>
|
||||
<p><img alt="Dylan concert promo" class="ss" src="http://leancrew.com/all-this/images2017/20171028-Dylan%20concert%20promo.png" title="Dylan concert promo"/></p>
|
||||
<p>So yes, we got “Melancholy Mood” and “Autumn Leaves” and “The September of My Years” with Bob crooning into a big microphone and holding its stand at a jaunty angle. But that’s not what I disliked about the concert. In fact, those songs were done pretty well, and Dylan’s voice sounded better than any previous concert I’ve been to. It was what he did to his own songs that was frustrating.</p>
|
||||
<p>No one in his right mind goes to a Dylan concert expecting to hear his classics played or sung the way they were done originally. But the treatment they’re given is always interesting and usually fun. Not this time. Apart from “It Ain’t Me, Babe” and “Highway 61 Revisited,” the Dylan songs suffered from arrangements that were, I guess, meant to match the style of the non-Dylan songs. Under these conditions, you can’t expect the songs to rock, but you can expect them to swing. And the band just didn’t swing.</p>
|
||||
<p>There was a sameness to Charlie Sexton’s guitar work throughout the night. Despite changing instruments several times, his sound was constrained and repetitive. But when a band doesn’t swing, it’s mainly because of the rhythm section. These are talented musicians who can play in any style, so if they didn’t find a groove it must have been because Bob didn’t want them to.</p>
|
||||
<p>As a result, he lost the audience. Many left early and those who hung in there stayed glued to their seats until the encores. Can you imagine a Dylan audience not standing to “Tangled Up in Blue”?</p>
|
||||
<p>The optimist in me notes that Dylan never sticks with anything for long, and after two albums of standards he may be ready for the next thing. I hope he comes back to the notion that that it’s his songs, and the blues and country/folk they came out of, that are truly the Great American Songbook and gives them the treatment they deserve.</p>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/10/judas/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Icons</title>
|
||||
<link>http://leancrew.com/all-this/2017/10/icons/</link>
|
||||
<pubDate>Fri, 27 Oct 2017 01:39:06 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/10/icons/</guid>
|
||||
<description>
|
||||
<![CDATA[I have some code sitting around here somewhere for extracting the images (app icons, book covers, album art, etc.) from the various iTunes and App Stores. But I think it’ll be easier to use <a href="http://brettterpstra.com/2017/10/25/itunesicon-2-dot-3/">Brett Terpstra’s iTunesIcon Automator app</a>, which just got an update.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>I have some code sitting around here somewhere for extracting the images (app icons, book covers, album art, etc.) from the various iTunes and App Stores. But I think it’ll be easier to use <a href="http://brettterpstra.com/2017/10/25/itunesicon-2-dot-3/">Brett Terpstra’s iTunesIcon Automator app</a>, which just got an update.</p>
|
||||
<p>Apart from its appeal to my laziness, Brett’s little app has a cool icon that I took an instant liking to.</p>
|
||||
<p><img alt="iTunesIcon icon" class="ss" src="http://leancrew.com/all-this/images2017/20171026-iTunesIcon%20icon.png" title="iTunesIcon icon"/></p>
|
||||
<p>If you work around industrial and construction equipment, Brett’s icon should bring a smile of familiarity. It’s highly reminiscent of the hands in safety signs and decals that are in dire straits.</p>
|
||||
<p><img alt="Safety symbols" class="ss" src="http://leancrew.com/all-this/images2017/20171026-Safety%20symbols.png" title="Safety symbols"/></p>
|
||||
<p class="caption">Images from <a href="http://www.safetysign.com/iso-warning-labels">SafetySign.com</a></p>
|
||||
<p>I’m sure your fingers are safe when using iTunesIcon.</p>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/10/icons/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Feed reading</title>
|
||||
<link>http://leancrew.com/all-this/2017/10/feed-reading/</link>
|
||||
<pubDate>Mon, 23 Oct 2017 00:10:42 +0000</pubDate>
|
||||
<dc:creator>
|
||||
<![CDATA[Dr. Drang]]>
|
||||
</dc:creator>
|
||||
<guid>http://leancrew.com/all-this/2017/10/feed-reading/</guid>
|
||||
<description>
|
||||
<![CDATA[Gabe Weatherhead has <a href="http://www.macdrifter.com/2017/10/feedbin-as-a-multi-device-reader.html">a nice article today</a> on how he uses Feedbin to handle his reading on the web. If you follow Gabe—and if not, why don’t you?—it will not surprise you to learn that he’s formidably organized. I don’t see myself following in his footsteps, but it’s always useful to learn how smart people do things. His article also reminded me that I’ve been meaning to write about <em>my</em> feed reading setup.]]>
|
||||
</description>
|
||||
<content:encoded>
|
||||
<![CDATA[<p>Gabe Weatherhead has <a href="http://www.macdrifter.com/2017/10/feedbin-as-a-multi-device-reader.html">a nice article today</a> on how he uses Feedbin to handle his reading on the web. If you follow Gabe—and if not, why don’t you?—it will not surprise you to learn that he’s formidably organized. I don’t see myself following in his footsteps, but it’s always useful to learn how smart people do things. His article also reminded me that I’ve been meaning to write about <em>my</em> feed reading setup.</p>
|
||||
<p>A couple of years ago, I let my subscription to <a href="https://feedwrangler.net/welcome.html">Feed Wrangler</a> lapse and started using a homemade, web-based RSS reading system. The heart of the system is still the script described in <a href="http://leancrew.com/all-this/2015/11/simpler-syndication/">this post</a>, but with a some changes as I thought of better ways of doing things.</p>
|
||||
<p>The biggest change came in the past few months. Initially, I created a single static web page with all of today’s articles from the feeds I subscribe to. A <a href="https://en.wikipedia.org/wiki/Cron">cron</a> task updated the page a few times an hour throughout the day. In this system, “today” was defined as “from 10:00 pm last night until now,” and the page would grow in size from morning to night.</p>
|
||||
<p>The advantage of this temporal arrangement from a programming point of view was that I didn’t have to write any code to keep track of whether I’d already read an article or not, and there were no external dependencies. If it was published “today,” it got on the page.</p>
|
||||
<p>The disadvantage was from the reading point of view. As I visited the page throughout the day, it became more and more filled with article I’d already read. This wasn’t as terrible as you might think. The articles were arranged in reverse chronological order by publication time, so the ones I’d read were typically at the bottom of the page. I say “typically” because some feeds—<a href="https://xkcd.com/">XKCD</a> comes to mind—are very bad at providing accurate publication times and their articles would sometimes end up at the bottom despite being recently published.</p>
|
||||
<p>Eliminating the reading disadvantage meant keeping track of what I’d read and showing only what I hadn’t—eliminating the programming advantage. I decided to keep track of read articles in an <a href="https://www.sqlite.org/">SQLite</a> database and to add items to that database through a button placed at the bottom of each article on my RSS page.</p>
|
||||
<p><img alt="RSS buttons" class="ss" src="http://leancrew.com/all-this/images2017/20171022-RSS%20buttons.png" title="RSS buttons" width="60%"/></p>
|
||||
<p>This meant</p>
|
||||
<ol>
|
||||
<li>Building a database that would uniquely identify every article. This was pretty simple. Each record has just two fields: the name of the website and the unique article ID (which is often just the article’s URL but is sometimes a long alphanumeric string generated by the site’s blogging software).</li>
|
||||
<li>Altering the existing script that builds the RSS page to filter out feed items that are in the database. Because Python has an <a href="https://docs.python.org/2/library/sqlite3.html">SQLite module</a> as part of its standard library and the syntax of SQL commands is straightforward, this wasn’t as tricky as I thought it would be. In fact, the new code is easier to read than the time-based filtering code I removed.</li>
|
||||
<li>Writing a server script (basically just a CGI script) to add an article to the database when given the blog name and article ID via the <code>POST</code> method. It’s been a while since I last wrote a CGI script, but it was like riding a bicycle.</li>
|
||||
<li>Adding some JavaScript with <a href="https://en.wikipedia.org/wiki/XMLHttpRequest"><code>XMLHttpRequest</code></a> to the RSS page to call the server script when a button is pressed. This took the most time, mainly because everyone in the world (except me) knows how to do AJAX now, and finding references written at an appropriately low level was harder than I expected. I found <a href="https://stackoverflow.com/questions/9713058/send-post-data-using-xmlhttprequest">this Stack Overflow discussion</a> helpful.</li>
|
||||
</ol>
|
||||
<p>So now I usually tap the <span class="menu">Mark as read</span> button when I get to the end of an article. If it’s a <a href="https://stratechery.com/">long article</a> that I want to read later, I don’t mark it as read, and it’ll be there the next time I bring up the RSS page.</p>
|
||||
<p>Fearing I’d forget how to use <code>XMLHttpRequest</code>, I quickly included another form at the end of each article for adding that article to my <a href="https://pinboard.in/api">Pinboard</a> account. I didn’t bother adding labels to the text field, because I’m the only one who uses this and I know the field is for tags. I did, however, include some DOM stuff to make it obvious when I’d marked an article as read or added it to Pinboard.</p>
|
||||
<p><img alt="RSS buttons marked" class="ss" src="http://leancrew.com/all-this/images2017/20171022-RSS%20buttons%20marked.png" title="RSS buttons marked" width="60%"/></p>
|
||||
<p>What I like about this system is how portable and (I hope) future-proof it is. I’ve been reluctant to sign on with Feedbin or Feedly or BazQux or any of the other Google Reader replacements because I worry they’ll <a href="http://observer.com/2017/01/medium-fail-post-literary-genre/">write a Medium post</a> and disappear with my subscription list and whatever organization scheme I’ve created. My system can run on any web server with Python, SQLite, and a <code>cgi-bin</code> directory. I think that’ll mean “any server, anywhere” for a very long time.</p>
|
||||
<br />
|
||||
<p>[If the formatting looks odd in your feed reader, <a href="http://leancrew.com/all-this/2017/10/feed-reading/">visit the original article</a>]</p>]]>
|
||||
</content:encoded>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
</rss>
|
||||
|
1
Parser/Tests/ParserTests/Resources/allthis.json
Normal file
1
Parser/Tests/ParserTests/Resources/allthis.json
Normal file
File diff suppressed because one or more lines are too long
4635
Parser/Tests/ParserTests/Resources/atp.rss
Normal file
4635
Parser/Tests/ParserTests/Resources/atp.rss
Normal file
File diff suppressed because it is too large
Load Diff
28
Parser/Tests/ParserTests/Resources/authors.json
Normal file
28
Parser/Tests/ParserTests/Resources/authors.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": "https://jsonfeed.org/version/1.1",
|
||||
"title": "Author test feed",
|
||||
"authors": [{ "name": "Root Author 1"}, { "name": "Root Author 2" }],
|
||||
"author": { "name": "Legacy Root Author" },
|
||||
"items": [
|
||||
{
|
||||
"id": "Item without authors",
|
||||
"content_html": ""
|
||||
},
|
||||
{
|
||||
"id": "Item with legacy author",
|
||||
"author": { "name": "Legacy Item Author" },
|
||||
"content_html": ""
|
||||
},
|
||||
{
|
||||
"id": "Item with modern authors",
|
||||
"authors": [{ "name": "Item Author 1" }, { "name": "Item Author 2" }],
|
||||
"content_html": ""
|
||||
},
|
||||
{
|
||||
"id": "Item with both",
|
||||
"authors": [{ "name": "Item Author 1" }, { "name": "Item Author 2" }],
|
||||
"author": { "name": "Legacy Item Author" },
|
||||
"content_html": ""
|
||||
}
|
||||
]
|
||||
}
|
663
Parser/Tests/ParserTests/Resources/bio.rdf
Normal file
663
Parser/Tests/ParserTests/Resources/bio.rdf
Normal file
@ -0,0 +1,663 @@
|
||||
<rdf:RDF xmlns:admin="http://webns.net/mvcb/" xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:prism="http://purl.org/rss/1.0/modules/prism/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/">
|
||||
<channel rdf:about="http://biorxiv.org">
|
||||
<admin:errorReportsTo rdf:resource="mailto:biorxiv@cshlpress.edu"/>
|
||||
<title>bioRxiv Subject Collection: Plant Biology</title>
|
||||
<link>http://biorxiv.org</link>
|
||||
<description>
|
||||
This feed contains articles for bioRxiv Subject Collection "Plant Biology"
|
||||
</description>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<items>
|
||||
<rdf:Seq>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/743294v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/746628v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/745653v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/745463v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/744177v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/742742v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/745364v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/744003v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/743898v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/744326v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/742429v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/741314v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/742114v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/740043v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/740126v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/740167v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/740001v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/736363v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/738971v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/738914v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/738070v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/738021v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/736793v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/736439v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/736397v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/734525v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/736199v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/736249v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/733857v1?rss=1"/>
|
||||
<rdf:li rdf:resource="http://biorxiv.org/cgi/content/short/726125v1?rss=1"/>
|
||||
</rdf:Seq>
|
||||
</items>
|
||||
<prism:eIssn/>
|
||||
<prism:publicationName>bioRxiv</prism:publicationName>
|
||||
<prism:issn/>
|
||||
|
||||
<image rdf:resource=""/>
|
||||
</channel>
|
||||
<image rdf:about="">
|
||||
<title>bioRxiv</title>
|
||||
<url/>
|
||||
<link>http://biorxiv.org</link>
|
||||
</image>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/743294v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Wheat inositol pyrophosphate kinase (TaVIH2-3B) interacts with Fasciclin-like arabinogalactan (FLA6) protein and alters the plant cell-wall composition
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/743294v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Inositol pyrophosphates (PPx-InsPs) are of key interest, since they are known to participate in multiple physiological processes from lower eukaryotes to humans. However, limited knowledge is available for their role in plants and especially in crops. In this study, two diphosphoinositol pentakisphosphate kinase PPIP5K wheat homologs, TaVIH1 and TaVIH2 were identified and characterized for their spatio-temporal expression along with their physiological functions. The biochemical assay demonstrated the presence of active VIH-kinase domains as evident from the InsP6 phosphorylation activity. The yeast complementation assays showed differential function, where only TaVIH2-3B was capable of rescuing the growth defects of yeast vip1{Delta} genotype. Reporter assays with TaVIH2-promoter in Arabidopsis displayed strong GUS expression in response to dehydration stress and Pi-starvation with similar observations noted at the transcriptional level. In an attempt to identify VIH2 function, a yeast two hybrid screen of wheat library resulted in the identification of multiple interacting proteins primarily associated with cell-wall. One such interactor of wheat VIH2-3B was identified to be a Fasciclin-like arabinogalactan protein (FLA6) that was confirmed by pull-down assay. Systematic analysis of transgenic Arabidopsis overexpressing TaVIH2-3B protein showed robust growth and enhanced relative water content when compared to controls. Biochemical analysis of their cell-wall components in the shoots resulted in increased accumulation of polysaccharides such as cellulose, arabinogalactan and arabinoxylan, whereas Atvih2-3 mutant showed decrease in some of these components. Overall, our results provide novel insight into the functional role of inositol pyrophosphate kinases that modulate cell-wall components so as to provide tolerance towards the dehydration stress.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Kaur, M., Shukla, A., Kanwar, S., Shukla, V., Kaur, G., Sharma, S., Kumar, A., Aggarwal, S., Bhati, K. K., Pandey, P., Mazumder, K., Rishi, V., Pandey, A. K. ]]></dc:creator>
|
||||
<dc:date>2019-08-27</dc:date>
|
||||
<dc:identifier>doi:10.1101/743294</dc:identifier>
|
||||
<dc:title><![CDATA[Wheat inositol pyrophosphate kinase (TaVIH2-3B) interacts with Fasciclin-like arabinogalactan (FLA6) protein and alters the plant cell-wall composition]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-27</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/746628v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Natural variation in the Arabidopsis AGO2 gene is associated with susceptibility to potato virus X
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/746628v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
RNA silencing functions as an anti-viral defence in plants through the action of DICER-like (DCL) and ARGONAUTE (AGO) proteins. However, there are few known examples of functional variation in RNA silencing components. The AGO2 protein is important for antiviral defense against multiple viruses and has been shown to be a major limiting factor to infection by potato virus X (PVX) of Arabidopsis thaliana but not Nicotiana benthamiana. We show that the AGO2 proteins from these two plants have differential activity against PVX, suggesting that variation in AGO2 is important in plant-virus interactions. Consistent with this, we find that the Arabidopsis thaliana AGO2 gene shows a high incidence of polymorphisms between accessions, with evidence of selective pressure. AGO2 protein variants can be assigned to two groups, in near equal frequency, based on an amino acid change and small deletions in the protein N-terminus. Inoculation of a large number of Arabidopsis accessions shows strong correlation between these alleles and resistance or susceptibility to PVX. These observations were validated using genetic and transgenic complementation analysis, which showed that one type of AGO2 variant is specifically affected in its antiviral activity, without interfering with other AGO2-associated functions such as anti-bacterial resistance or DNA methylation. Our results demonstrate a novel type of genetically-encoded virus resistance and suggest that plant-virus interactions have influenced natural variation in RNA silencing components.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Brosseau, C., Adurogbangba, A., Roussin-Leveillee, C., Zhao, Z., Biga, S., Moffett, P. ]]></dc:creator>
|
||||
<dc:date>2019-08-24</dc:date>
|
||||
<dc:identifier>doi:10.1101/746628</dc:identifier>
|
||||
<dc:title><![CDATA[Natural variation in the Arabidopsis AGO2 gene is associated with susceptibility to potato virus X]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-24</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/745653v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Dynamic architecture and regulatory implications of the miRNA network underlying the response to stress in melon
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/745653v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
miRNAs are small RNAs that regulate mRNAs at both transcriptional and posttranscriptional level. In plants, miRNAs are involved in the regulation of different processes including development and stress-response. Elucidating how stress-responsive miRNAs are regulated is key to understand the global response to stress but also to develop efficient biotechnological tools that could help to cope with stress. Here, we describe a computational approach based on sRNA sequencing, transcript quantification and degradome data to analyze the accumulation, function and structural organization of melon miRNAs reactivated under seven biotic and abiotic stress conditions at two and four days post-treatment. Our pipeline allowed us to identify fourteen stress-responsive miRNAs (including evolutionary conserved such as miR156, miR166, miR172, miR319, miR398, miR399, miR894 and miR408) at both analyzed times. According to our analysis miRNAs were categorized in three groups showing a broad-, intermediate- or narrow- response range. miRNAs reactive to a broad range of environmental cues appear as central components in the stress-response network. The strictly coordinated response of miR398 and miR408 (broad response-range) to the seven stress treatments during the period analyzed here reinforces this notion. Although both, the amplitude and diversity of the miRNA-related response to stress changes during the exposition time, the architecture of the miRNA-network is conserved. This organization of miRNA response to stress is also conserved in rice and soybean supporting the conservation of miRNA-network organization in other crops. Overall, our work sheds light into how miRNA networks in plants organize and function during stress.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Sanz-Carbonell, A., Marques, M. C., Martinez, G., Gomez, G. ]]></dc:creator>
|
||||
<dc:date>2019-08-24</dc:date>
|
||||
<dc:identifier>doi:10.1101/745653</dc:identifier>
|
||||
<dc:title><![CDATA[Dynamic architecture and regulatory implications of the miRNA network underlying the response to stress in melon]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-24</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/745463v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Algal photosynthesis converts nitric oxide into nitrous oxide
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/745463v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Nitrous oxide (N2O), a potent greenhouse gas in the atmosphere, is produced mostly from aquatic ecosystems, to which algae substantially contribute. However, mechanisms of N2O production by photosynthetic organisms are poorly described. Here, we show that the green microalga Chlamydomonas reinhardtii reduces NO into N2O using the photosynthetic electron transport. Through the study of C. reinhardtii mutants deficient in flavodiiron proteins (FLVs) or in a cytochrome p450 (CYP55), we show that FLVs contribute to NO reduction in the light, while CYP55 operates in the dark. Furthermore, NO reduction by both pathways is restricted to Chlorophytes, organisms particularly abundant in ocean N2O-producing hotspots. Our results provide a mechanistic understanding of N2O production in eukaryotic phototrophs and represent an important step toward a comprehensive assessment of greenhouse gas emission by aquatic ecosystems.nnOne sentence summaryGreen microalgae produce N2O using flavodiiron proteins in the light and a cytochrome P450 NO reductase in the dark.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Burlacot, A., Gosset, A., RIchaud, P., Li-Beisson, Y., Peltier, G. ]]></dc:creator>
|
||||
<dc:date>2019-08-24</dc:date>
|
||||
<dc:identifier>doi:10.1101/745463</dc:identifier>
|
||||
<dc:title><![CDATA[Algal photosynthesis converts nitric oxide into nitrous oxide]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-24</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/744177v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Cytokinin fluoroprobe and receptor CRE1/AHK4 localize to both plasma membrane and endoplasmic reticulum
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/744177v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
The plant hormone cytokinin regulates various cell and developmental processes, including cell division and differentiation, embryogenesis, activity of shoot and root apical meristems, formation of shoot and root lateral organs and others 1. Cytokinins are perceived by a subfamily of sensor histidine kinases (HKs), which via a two-component phosphorelay cascade activate transcriptional responses in the nucleus. Based on the subcellular localization of cytokinin receptors in various transient expression systems, such as tobacco leaf epidermal cells, and membrane fractionation experiments of Arabidopsis and maize, the endoplasmic reticulum (ER) membrane has been proposed as a principal hormone perception site 2-4. Intriguingly, recent study of the cytokinin transporter PUP14 has pointed out that the plasma membrane (PM)-mediated signalling might play an important role in establishment of cytokinin response gradients in various plant organs 5. However, localization of cytokinin HK receptors to the PM, although initially suggested 6, remains ambiguous. Here, by monitoring subcellular localizations of the fluorescently labelled natural cytokinin probe iP-NBD 7 and the cytokinin receptor ARABIDOPSIS HISTIDINE KINASE 4 (CRE1/AHK4) fused to GFP reporter, we show that pools of the ER-located cytokinin fluoroprobes and receptors can enter the secretory pathway and reach the PM. We demonstrate that in cells of the root apical meristem, CRE1/AHK4 localizes to the PM and the cell plate of dividing meristematic cells. Brefeldin A (BFA) experiments revealed vesicular recycling of the receptor and its accumulation in BFA compartments. Our results provide a new perspective on cytokinin signalling and the possibility of multiple sites of perception at PM and ER, which may determine specific outputs of cytokinin signalling.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Kubiasova, K., Montesinos, J. C., Samajova, O., Nisler, J., Mik, V., Plihalova, L., Novak, O., Marhavy, P., Zalabak, D., Berka, K., Dolezal, K., Galuszka, P., Samaj, J., Strnad, M., Benkova, E., Plihal, O., Spichal, L. ]]></dc:creator>
|
||||
<dc:date>2019-08-24</dc:date>
|
||||
<dc:identifier>doi:10.1101/744177</dc:identifier>
|
||||
<dc:title><![CDATA[Cytokinin fluoroprobe and receptor CRE1/AHK4 localize to both plasma membrane and endoplasmic reticulum]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-24</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/742742v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Transcriptional Dynamics of the Salicylic Acid Response and its Interplay with the Jasmonic Acid Pathway
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/742742v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
The phytohormone salicylic acid (SA) is a central regulator of plant immunity. Antagonistic and synergistic actions between SA and other defense-associated hormones like jasmonic acid (JA) play key roles in determining the outcome of the plant immune response. To obtain a deeper understanding of SA-mediated transcriptional reprogramming and SA/JA crosstalk, we generated a high-resolution time series of gene expression from Arabidopsis leaves treated with SA alone and a combination of SA and methyl JA (MeJA), sampled at 14 time points over a 16-h period. We found that approximately one-third of the Arabidopsis genome was differentially expressed in response to SA, and temporal changes in gene expression could be partitioned into 45 distinct clusters of process-specific coregulated genes, linked to specific cis-regulatory elements and binding of transcription factors (TFs). Integration of our expression data with information on TF-DNA binding allowed us to generate a dynamic gene regulatory network model of the SA response, recovering known regulators and identifying novel ones. We found that 12% of SA-responsive genes and 69% of the MeJA-responsive genes exhibited antagonistic or synergistic expression levels in the combination treatment. Multi-condition co-clustering of the single- and combined-hormone expression profiles predicted underlying regulatory mechanisms in signal integration. Finally, we identified the TFs ANAC061 and ANAC090 as negative regulators of SA pathway genes and defense against biotrophic pathogens. Collectively, our data provide an unprecedented level of detail about transcriptional changes during the SA response and SA/JA crosstalk, serving as a valuable resource for systems-level network studies and functional plant defense studies.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Hickman, R., Pereira Mendes, M., Van Verk, M. C., Van Dijken, A. J. H., Di Sora, J., Denby, K., Pieterse, C. C. M. J., Van Wees, S. C. M. ]]></dc:creator>
|
||||
<dc:date>2019-08-24</dc:date>
|
||||
<dc:identifier>doi:10.1101/742742</dc:identifier>
|
||||
<dc:title><![CDATA[Transcriptional Dynamics of the Salicylic Acid Response and its Interplay with the Jasmonic Acid Pathway]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-24</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/745364v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Dynamic response of RNA editing to temperature in Grape by RNA deep-sequencing
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/745364v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
RNA editing is a post-transcriptional process of modifying genetic information on RNA molecules, which provides cells an additional level of gene expression regulation. Unlike mammals, in land plants, RNA editing converts C to U residues in organelles. However, its potential role in response to different stressors (heat, salt and so on) remains unclear. Grape is one of the most popular and economically important fruits in the world, and its production, like other crops, must deal with abiotic and biotic stresses, which cause reductions in yield and fruit quality. In our study, we tested the influence of the environmental factor temperature on RNA processing in the whole mRNA from grape organelle. In total, we identified 123 and 628 RNA editing in chloroplast and mitochondria respectively with the average editing extent nearly ~60%. The analyses revealed that number of non-synonymous editing sites were higher than that of synonymous editing sites, and the amino acid substitution type tend to be hydrophobic. Additionally, the overall editing level decreased with the temperature rises, especially several gene transcripts in chloroplast and mitochondria (matK, ndhB etc.). 245 sites were furthermore determined as stress-responsive sites candidates. We also found that the expression level of PPR genes decreased with the temperature rises, which may contribute to the loss of RNA editing at high temperature. Our findings suggest that the RNA editing events are very sensitive to high temperature, the changes of amino acid in these genes may contribute to the stress adaption for grape.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Zhang, a., jiang, x., zhang, x. ]]></dc:creator>
|
||||
<dc:date>2019-08-24</dc:date>
|
||||
<dc:identifier>doi:10.1101/745364</dc:identifier>
|
||||
<dc:title><![CDATA[Dynamic response of RNA editing to temperature in Grape by RNA deep-sequencing]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-24</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/744003v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Genome-wide analysis of GATA factors in moso bamboo (Phyllostachys edulis) unveils that PeGATAs regulate shoot rapid-growth and rhizome development
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/744003v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
BackgroundMoso bamboo is well-known for its rapid-growth shoots and widespread rhizomes. However, the regulatory genes of these two processes are largely unexplored. GATA factors regulate many developmental processes, but its role in plant height control and rhizome development remains unclear.nnResultsHere, we found that bamboo GATA factors (PeGATAs) are involved in the growth regulation of bamboo shoots and rhizomes. Bioinformatics and evolutionary analysis showed that there are 31 PeGATA factors in bamboo, which can be divided into three subfamilies. Light, hormone, and stress-related cis-elements were found in the promoter region of the PeGATA genes. Gene expression of 12 PeGATA genes was regulated by phytohormone-GA but there was no correlation between auxin and PeGATA gene expression. More than 27 PeGATA genes were differentially expressed in different tissues of rhizomes, and almost all PeGATAs have dynamic gene expression level during the rapid-growth of bamboo shoots. These results indicate that PeGATAs regulate rhizome development and bamboo shoot growth partially via GA signaling pathway. In addition, PeGATA26, a rapid-growth negative regulatory candidate gene modulated by GA treatment, was overexpressed in Arabidopsis, and over-expression of PeGATA26 significantly repressed Arabidopsis primary root length and plant height. The PeGATA26 overexpressing lines were also resistant to exogenous GA treatment, further emphasizing that PeGATA26 inhibits plant height from Arabidopsis to moso bamboo via GA signaling pathway.nnConclusionsOur results provide an insight into the function of GATA transcription factors in regulating shoot rapid-growth and rhizome development, and provide genetic resources for engineering plant height.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Wang, T., Yang, Y., Lou, S., Wei, W., Zhao, Z., Lin, C., Ma, L. ]]></dc:creator>
|
||||
<dc:date>2019-08-22</dc:date>
|
||||
<dc:identifier>doi:10.1101/744003</dc:identifier>
|
||||
<dc:title><![CDATA[Genome-wide analysis of GATA factors in moso bamboo (Phyllostachys edulis) unveils that PeGATAs regulate shoot rapid-growth and rhizome development]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-22</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/743898v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
The maize Hairy Sheath Frayed1 (Hsf1) mutant alters leaf patterning through increased cytokinin signaling
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/743898v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Leaf morphogenesis requires growth polarized along three axes - proximal-distal, medial-lateral and abaxial-adaxial. Grass leaves display a prominent proximal-distal (P-D) polarity consisting of a proximal sheath separated from the distal blade by the auricle and ligule. Although proper specification of the four segments is essential for normal morphology, our knowledge is incomplete regarding the mechanisms which influence P-D specification in monocots like maize (Zea mays). Here we report the identification of the gene underlying the semi-dominant, leaf patterning, maize mutant Hairy Sheath Frayed1 (Hsf1). Hsf1 plants produce leaves with outgrowths consisting of proximal segments - sheath, auricle and ligule - emanating from the distal blade margin. Analysis of three independent Hsf1 alleles revealed gain-of-function missense mutations in the ligand binding domain of the maize cytokinin (CK) receptor Zea mays Histidine Kinase1 (ZmHK1) gene. Biochemical analysis and structural modeling suggest the mutated residues near the CK binding pocket affect CK binding affinity. Treatment of wild type seedlings with exogenous CK phenocopied the Hsf1 leaf phenotypes. Results from expression and epistatic analyses indicated the Hsf1 mutant receptor is expressed normally but appears hypersignaling. Our results demonstrate that hypersignaling of CK in incipient leaf primordia can reprogram developmental patterns in maize.nnSummaryIncreased cytokinin signaling in the maize Hairy Sheath Frayed1 mutant modifies leaf development leading to changes in pattering, growth and cell identity.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Muszynski, M. G., Moss-Taylor, L., Chudalayandi, S., Cahill, J. F., Del-Valle Echevarria, A. R., Alvarez-Castro, I., Petefish, A., Sakakibara, H., Krivosheev, D. M., Lomin, S. N., Romanov, G. A., Thamotharan, S., Dam, T., Li, B., Brugiere, N. ]]></dc:creator>
|
||||
<dc:date>2019-08-22</dc:date>
|
||||
<dc:identifier>doi:10.1101/743898</dc:identifier>
|
||||
<dc:title><![CDATA[The maize Hairy Sheath Frayed1 (Hsf1) mutant alters leaf patterning through increased cytokinin signaling]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-22</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/744326v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Expression atlas of Selaginella moellendorffii provides insights into the evolution of vasculature, secondary metabolism and roots
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/744326v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
O_LIThe lycophyte Selaginella moellendorffii represents early vascular plants and is studied to understand the evolution of higher plant traits such as the vasculature, leaves, stems, roots, and secondary metabolism. However, little is known about the gene expression and transcriptional coordination of Selaginella genes, which precludes us from understanding the evolution of transcriptional programs behind these traits.nC_LIO_LIWe here present a gene expression atlas comprising all major organs, tissue types, and the diurnal gene expression profiles for S. moellendorffii. The atlas is part of the CoNekT-Plants database (conekt.plant.tools), which enables comparative transcriptomic analyses across two algae and seven land plants.nC_LIO_LIWe show that the transcriptional gene module responsible for the biosynthesis of lignocellulose evolved in the ancestor of vascular plants, and pinpoint the duplication and subfunctionalization events that generated multiple gene modules involved in the biosynthesis of various cell wall types. We further demonstrate how secondary metabolism is transcriptionally coordinated and integrated with other cellular pathways. Finally, we identify root-specific genes in vascular plants and show that the evolution of roots did not coincide with an increased appearance of gene families, suggesting that the existing genetic material was sufficient to generate new organs.nC_LIO_LIOur updated database at conekt.plant.tools provides a unique resource to study the evolution of genes, gene families, transcriptomes, and functional gene modules in the Archaeplastida kingdom.nC_LI
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Ferrari, C., Shivhare, D., Hansen, B. O., Winter, N., Pasha, A., Esteban, E., Provart, N. J., Kragler, F., Fernie, A. R., Tohge, T., Mutwil, M. ]]></dc:creator>
|
||||
<dc:date>2019-08-22</dc:date>
|
||||
<dc:identifier>doi:10.1101/744326</dc:identifier>
|
||||
<dc:title><![CDATA[Expression atlas of Selaginella moellendorffii provides insights into the evolution of vasculature, secondary metabolism and roots]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-22</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/742429v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Soybean drought resilience: contributions of a brassinosteroid functional analogue.
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/742429v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Drought is one of the most important causes of severe yield loss in soybean worldwide, threatening food production for the coming years. Phytohormones such as brassinosteroids can increase response to water deficit. However, natural brassinosteroids low stability precludes large-scale field application, challenging research and development of more stable and cost-effective analogues. Seeking functional analogues capable of improving plant drought-response, we investigated for the first time the effect of DI-31 in Arabidopsis and soybean. We found that, in A. thaliana, the DI-31 increased root growth, biomass accumulation, leaf number per plant, triggered antioxidant response and dose-dependent stomatal closure, requiring NADPH and peroxidase-dependent ROS production. In soybean, the relative water content, water use efficiency, biomass production and duration, root length, free proline, chlorophyll and carotenoid accumulation and enzymatic antioxidants activity were stimulated by DI-31 application after four and eight days of mild water shortage, while significantly reduced the lipid-peroxides content. Additionally, our results demonstrated that DI-31 diminishes the nodular senescence and successfully maintains the N homeostasis through a fine tune of biological/assimilative N2-fixation pathways. These findings support the DI-31 potential use as a sustainable alternative for integrative soybean resilience management under drought.nnHighlightBrassinosteroid analogue DI-31 improves soybean growth, water economy, respiration, anti-stress response and nitrogen homeostasis under drought. Thus, they may be considered as a sustainable and environmentally-safe alternative for raising legumes climate resilience.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Perez Borroto, L. S., Toum, L., Castagnaro, A. P., Gonzalez-Olmedo, J. L., Coll-Manchado, F., Welin, B. G. V., Coll-Garcia, Y., Pardo, E. M. ]]></dc:creator>
|
||||
<dc:date>2019-08-22</dc:date>
|
||||
<dc:identifier>doi:10.1101/742429</dc:identifier>
|
||||
<dc:title><![CDATA[Soybean drought resilience: contributions of a brassinosteroid functional analogue.]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-22</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/741314v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Co-catabolism of arginine and succinate drives symbiotic nitrogen fixation
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/741314v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Biological nitrogen fixation emerging from the symbiosis between bacteria and crop plants holds a significant promise to increase the sustainability of agriculture. One of the biggest hurdles for the engineering of nitrogen-fixing organisms is to identify the metabolic blueprint for symbiotic nitrogen fixation. Here, we report on the C4-dicarboxylate Arginine-Transamination Co-catabolism under acidic (H+) conditions to fix Nitrogen (CATCH-N), a novel metabolic network based on co-catabolism of plant-provided arginine and succinate to drive the energy-demanding process of symbiotic nitrogen fixation in endosymbiotic rhizobia. Using systems biology, isotope labelling studies and transposon sequencing in conjunction with biochemical characterization, we uncovered highly redundant network components of the CATCH-N cycle including transaminases that interlink the co-catabolism of arginine and succinate. The CATCH-N cycle shares aspects with the plant mitochondrial arginine degradation pathway. However, it uses N2 as an additional sink for reductant and therefore delivers up to 25% higher yields in nitrogen than classical arginine catabolism -two alanines and three ammonium ions are secreted for each input of arginine and succinate. We argue that the CATCH-N cycle has evolved as part of a specific mechanism to sustain bacterial metabolism in the microoxic and acid environment of symbiosomes. Thus, the CATCH-N cycle entangles the metabolism of both partners to promote symbiosis. In sum, our systems-level findings provide the theoretical framework and enzymatic blueprint for the rational design of plants and plant-associated organisms with new properties for improved nitrogen fixation.nnSignificance StatementSymbiotic bacteria assimilate nitrogen from the air and fix it into a form that can be used by plants in a process known as biological nitrogen fixation. In agricultural systems, this process is restricted mainly to legumes, yet there is considerable interest in exploring whether similar symbioses can be developed in non-legumes including cereals and other important crop plants. Here we present systems-level findings on the minimal metabolic function set for biological nitrogen fixation that provides the theoretical framework for rational engineering of novel organisms with improved nitrogen-fixing capabilities.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Flores-Tinoco, C. E., Christen, M., Christen, B. ]]></dc:creator>
|
||||
<dc:date>2019-08-21</dc:date>
|
||||
<dc:identifier>doi:10.1101/741314</dc:identifier>
|
||||
<dc:title><![CDATA[Co-catabolism of arginine and succinate drives symbiotic nitrogen fixation]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-21</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/742114v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Cross-compatibility of five highbush blueberry varieties and ideal crossing combinations
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/742114v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Blueberry plants require large quantities of pollen deposited on stigmas to produce commercial-quality fruit. Like many agricultural crops, the interaction between pollen-source variety and pollen-recipient variety can be a major determinant of fruit quality in blueberries. However, little information exists to guide growers in optimising fruit set and quality. Using five commonly grown blueberry varieties, I determined whether crossing between varieties (inter-varietal) increased fruit mass and decreased developmental time relative to crossing within a variety (intra-varietal), and if so, what the best crossing combinations are. While intra-varietal pollination often produced fruit, for certain varieties the fruit set and fruit mass were highly reduced compared to inter-varietal pollination. Furthermore, intra-varietal pollination resulted in longer fruit developmental time in comparison to pollination between varieties. For the same pollen-recipient variety, inter-varietal crosses typically outperformed intra-varietal crosses in fruit mass and developmental time; however, the extent to which inter-varietal crosses outperformed intra-varietal crosses differed between pollen-donor varieties. This result suggests that combinations of varieties are not trivial as some inter-varietal combinations may outperform others. Furthermore, some varieties appear to be more susceptible to the negative effects of intra-varietal crosses than others and that less susceptible varieties may be better suited to conditions where pollinator movement is poor. While our study can guide growers in determining optimal co-planting schemes for the varieties tested, for example in South Africa where these varieties are frequently grown. It also serves as a blueprint for similar compatibility studies that can easily be performed prior to planting to determine the best inter-varietal combinations.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Martin, K., Anderson, B., Minnaar, C., de Jager, M. ]]></dc:creator>
|
||||
<dc:date>2019-08-21</dc:date>
|
||||
<dc:identifier>doi:10.1101/742114</dc:identifier>
|
||||
<dc:title><![CDATA[Cross-compatibility of five highbush blueberry varieties and ideal crossing combinations]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-21</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/740043v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Symbiotic signalling is at the core of an endophytic Fusarium solani-legume association
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/740043v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Legumes interact with a wide range of microbes in their root system, ranging from beneficial symbionts to pathogens. Symbiotic rhizobia and arbuscular mycorrhizal glomeromycetes trigger a so-called common symbiotic signalling pathway (CSSP), including the induction of nuclear calcium spiking in the root epidermis. In our study, the recognition of an endophytic Fusarium solani strain K in Lotus japonicus induced the expression of LysM receptors for chitin-based molecules, CSSP members and CSSP-dependent genes in L. japonicus. In LysM and CSSP mutant/RNAi lines, root penetration and fungal intraradical progression was either stimulated or limited while FsK exudates are perceived in a CSSP-dependent manner, triggering nuclear calcium spiking in epidermal cells of Medicago truncatula Root Organ Cultures. Our results corroborate that the CSSP is a more common pathway than previously envisaged, involved in the perception of signals from other microbes beyond the restricted group of symbiotic interactions sensu stricto.nnAbbreviationsAM, Arbuscular Mycorrhizal; AU, airy units; CLSM, Confocal Laser Scanning Microscopy; CO5, pentameric chito-oligosaccharide; CSSP, Common Symbiosis Signalling Pathway; dpi, days post inoculation; Fom, Fusarium oxysporum f. sp. medicaginis; FsK, Fusarium solani strain K; LCOs, lipo-chitooligosaccharides; Lj, Lotus japonicus; Lysin-motif, LysM; Mt, Medicago truncatula; NF, Nod Factor; PM, plasma membrane; RLK, receptor-like kinase; RLS, rhizobium - legume symbiosis; ROCs, Root Organ Cultures; wt, wild-type
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Skiada, V., Avramidou, M., Bonfante, P., Genre, A., PAPADOPOULOU, K. ]]></dc:creator>
|
||||
<dc:date>2019-08-20</dc:date>
|
||||
<dc:identifier>doi:10.1101/740043</dc:identifier>
|
||||
<dc:title><![CDATA[Symbiotic signalling is at the core of an endophytic Fusarium solani-legume association]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-20</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/740126v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
NbCycB2 represses Nbwo activity via a negative feedback loop in the tobacco trichome developmemt
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/740126v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
The wo protein and its downstream gene, SlCycB2 have been demonstrated to regulate the trichome development in tomato. It was shown that only gain-of-function mutant form of wo, Wov (wo woolly motif mutant allele) could induce the increase of trichome density. However, it is still unclear the relationships between wo, Wov and SlCycB2 in trichome regulation. In this study, we demonstrated Nbwo (NbWov) directly regulated the expressions NbCycB2 by binding to the promoter of NbCycB2 and its genomic sequences. As a feedback regulation, NbCycB2 negatively regulates the trichome formation by repressing Nbwo activity at protein level. We further found that the mutations of Nbwo woolly motif could prevent repression of NbWov by NbCycB2, which results in the significant increase of active Nbwo proteins, trichome density and branches. Our results revealed a novel reciprocal mechanism between NbCycB2 and Nbwo during the trichome formation in Nicotiana benthamiana.nnHighlightNbCycB2 is specifically expressed in trichomes of Nicotiana benthamiana and represses the Nbwo activity via a negative feedback loop in tobacco trichome developmemt.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Wu, M., Cui, Y., Ge, L., Cui, L., Xu, Z., Zhang, H., Wang, Z., Zhou, D., Wu, S., Chen, L., Cui, H. ]]></dc:creator>
|
||||
<dc:date>2019-08-20</dc:date>
|
||||
<dc:identifier>doi:10.1101/740126</dc:identifier>
|
||||
<dc:title><![CDATA[NbCycB2 represses Nbwo activity via a negative feedback loop in the tobacco trichome developmemt]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-20</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/740167v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Successes of artemisinin elicitation in low-artemisinin producing Artemisia annua cell cultures constrained by repression of biosynthetic genes
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/740167v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
The sesquiterpene phytolactone derived from Artemisia annua, artemisinin is associated with a variety of novel biological properties, such as immunoregulatory and anticancer effects, and therapeutic applications, apart from its main function as an antimalarial drug. Emerging from the fact that artemisinin production in planta occurs in trace amounts and its compartmentalized synthesis, the irregular agricultural supply often results in market fluctuations and reductions in artemisinin inventory. Further improvement in artemisinin production calls for approaches that act in a supplementary manner, filling the agricultural production gap. Here we investigated the elicitation efficiency of ultraviolet B (UV-B) and dimethyl sulfoxide (DMSO) independently on a low-artemisinin producing (LAP) chemotype of the species A. annua. The exposure of cell suspension cultures to short-term UV-B radiation and DMSO treatment did not result in significant changes in artemisinin yield. The lack of stimulation has been associated with: (i) the general lack of cytodifferentiation of cell cultures; (ii) negative feedback regulation of artemisinin biosynthesis; and (iii) artemisinin sequestration by cellular detoxification. Further molecular analysis revealed the repression of key genes ADS, DBR2 and ALDH1 which affected artemisinin synthesis. This study provides insights into the complexity of stress-induced responses of A. annua cell suspension cultures in relation to metabolic processes (transportation, accumulation and degradation of secondary products) which are important for artemisinin formation.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Kam, M. Y. Y., Yap, W. ]]></dc:creator>
|
||||
<dc:date>2019-08-19</dc:date>
|
||||
<dc:identifier>doi:10.1101/740167</dc:identifier>
|
||||
<dc:title><![CDATA[Successes of artemisinin elicitation in low-artemisinin producing Artemisia annua cell cultures constrained by repression of biosynthetic genes]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-19</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/740001v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
A SAC phosphoinositide phosphatase controls rice development via hydrolyzing phosphatidylinositol 4-phosphate and phosphatidylinositol 4,5-bisphosphate
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/740001v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Phosphoinositides (PIs) as regulatory membrane lipids play essential roles in multiple cellular processes. Although the exact molecular targets of PIs-dependent modulation remain largely elusive, the effects of disturbed PIs metabolism could be employed to propose regulatory modules associated with particular downstream targets of PIs. Here, we identified the role of GRAIN NUMBER AND PLANT HEIGHT 1 (GH1), which encodes a suppressor of actin (SAC) domain-containing phosphatase with unknown function in rice. Endoplasmic reticulum-localized GH1 specifically dephosphorylated and hydrolyzed phosphatidylinositol 4-phosphate (PI4P) and phosphatidylinositol 4,5-bisphosphate [PI(4,5)P2]. Inactivation of GH1 resulted in massive accumulation of both PI4P and PI(4,5)P2, while excessive GH1 caused their depletion. Notably, superabundant PI4P and PI(4,5)P2 could both disrupt actin cytoskeleton organization and suppress cell elongation. Interestingly, both PI4P and PI(4,5)P2 inhibited actin-related proteins 2 and 3 (Arp2/3) complex-nucleated actin branching networks in vitro, whereas PI(4,5)P2 showed more dramatic effect in a dose-dependent manner. Overall, the overaccumulation of PI(4,5)P2 resulted from dysfunction of SAC phosphatase possibly perturbs Arp2/3 complex-mediated actin polymerization, thereby disordering the cell development. These findings imply that Arp2/3 complex might be the potential molecular target of PI(4,5)P2-dependent modulation in eukaryotes, thereby providing new insights into the relationship between PIs homeostasis and plants growth and development.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Guo, T., Lin, H.-X., Chen, K., Dong, N.-Q., Huang, S., Ye, W.-W., Shan, J.-X., Chen, H.-C., Lu, Z.-Q., Diao, M. ]]></dc:creator>
|
||||
<dc:date>2019-08-19</dc:date>
|
||||
<dc:identifier>doi:10.1101/740001</dc:identifier>
|
||||
<dc:title><![CDATA[A SAC phosphoinositide phosphatase controls rice development via hydrolyzing phosphatidylinositol 4-phosphate and phosphatidylinositol 4,5-bisphosphate]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-19</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/736363v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Integrated Multi-omic Framework of the Plant Response to Jasmonic Acid
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/736363v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Understanding the systems-level actions of transcriptional responses to hormones provides insight into how the genome is reprogrammed in response to environmental stimuli. Here, we investigate the signaling pathway of the hormone jasmonic acid (JA), which controls a plethora of critically important processes in plants and is orchestrated by the transcription factor MYC2 and its closest relatives in Arabidopsis thaliana. We generated an integrated framework of the response to JA that spans from the activity of master and secondary-regulatory transcription factors, through gene expression outputs and alternative splicing to protein abundance changes, protein phosphorylation and chromatin remodeling. We integrated time series transcriptome analysis with (phospho)proteomic data to reconstruct gene regulatory network models. These enable us to predict previously unknown points of crosstalk from JA to other signaling pathways and to identify new components of the JA regulatory mechanism, which we validated through targeted mutant analysis. These results provide a comprehensive understanding of how a plant hormone remodels cellular functions and plant behavior, the general principles of which provide a framework for analysis of cross-regulation between other hormone and stress signaling pathways.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Zander, M., Lewsey, M. G., Clarke, N. M., Yin, L., Bartlett, A., Saldierna Guzman, J. P., Hann, E., Langford, A., Jow, B., Wise, A., Nery, J. R., Chen, H., Bar-Joseph, Z., Walley, J., Solano, R., Ecker, J. R. ]]></dc:creator>
|
||||
<dc:date>2019-08-19</dc:date>
|
||||
<dc:identifier>doi:10.1101/736363</dc:identifier>
|
||||
<dc:title><![CDATA[Integrated Multi-omic Framework of the Plant Response to Jasmonic Acid]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-19</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/738971v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Developing a rapid and highly efficient cowpea regeneration and transformation system using embryonic axis explants
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/738971v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Cowpea is one of the most important legume crops planted worldwide, especially in Sub-Saharan Africa and Asia. Despite decades of effort, genetic engineering of cowpea is still challenging due to inefficient in vitro shoot regeneration, Agrobacterium-mediated T-DNA delivery and transgenic selection. Here, we report a rapid and highly efficient cowpea transformation system using embryonic axis explants isolated from imbibed mature seeds. We found that removal of the shoot apical meristem by cutting through the middle of the epicotyl stimulated direct multiple shoot organogenesis from the cotyledonary node tissue. Furthermore, the application of a ternary transformation vector system using an optimized pVIR accessory plasmid provided high levels of Agrobacterium-mediated gene delivery. The utilization of spectinomycin as the selection agent enabled more efficient transgenic selection and plant recovery. Transgenic cowpea shoots developed exclusively from the cotyledonary nodes at high frequencies of 4.5 to 37% across a wide range of cowpea genotypes. We believe that the transformation principles established in this study could also be applied to other legumes to increase transformation efficiencies.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Che, P., Chang, S., Simon, M. K., Zhang, Z., Shaharyar, A., Ourada, J., O'Neill, D., Torres-Mendoza, M., Guo, Y., Marasigan, K. M., Vielle-Calzada, J.-P., Ozias-Akins, P., Albertsen, M. C., Jones, T. J. ]]></dc:creator>
|
||||
<dc:date>2019-08-19</dc:date>
|
||||
<dc:identifier>doi:10.1101/738971</dc:identifier>
|
||||
<dc:title><![CDATA[Developing a rapid and highly efficient cowpea regeneration and transformation system using embryonic axis explants]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-19</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/738914v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Genetic analysis of seed and pod traits in a set of Recombinant Inbred Lines (RILs) in peanut (Arachis hypogaea L.)
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/738914v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Although seed and pod traits are important for peanut breeding, little is known about the inheritance of these traits. A recombinant inbred line (RIL) population of 156 lines from a cross of Tifrunner x NC 3033 was genotyped with the Axiom_Arachis1 SNP array and SSRs to generate a genetic map composed of 1524 markers in 29 linkage groups (LG). The genetic positions of markers were compared with their physical positions on the peanut genome to confirm the validity of the linkage map and explore the distribution of recombination and potential chromosomal rearrangements. This linkage map was then used to identify Quantitative Trait Loci (QTL) for seed and pod traits that were phenotyped over three consecutive years for the purpose of developing trait-associated markers for breeding. Forty-nine QTL were identified in 14 LG for seed size index, kernel percentage, seed weight, pod weight, single-kernel, double-kernel, pod area and pod density. Twenty QTL demonstrated phenotypic variance explained (PVE) greater than 10% and eight more than 20%. Of note, seven of the eight major QTL for pod area, pod weight and seed weight (PVE >20% variance) were attributed to NC 3033 and located in a single linkage group, LG B06_1. In contrast, the most consistent QTL for kernel percentage were located on A07/B07 and derived from Tifrunner.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Chavarro, C., Chu, Y., Holbrook, C. C., Isleib, T., Bertioli, D., Hovav, R., Butts, C., Marshall, L., Sorensen, R., Jackson, S. A., Ozias-Akins, P. ]]></dc:creator>
|
||||
<dc:date>2019-08-18</dc:date>
|
||||
<dc:identifier>doi:10.1101/738914</dc:identifier>
|
||||
<dc:title><![CDATA[Genetic analysis of seed and pod traits in a set of Recombinant Inbred Lines (RILs) in peanut (Arachis hypogaea L.)]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-18</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/738070v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Characterizing allele-by-environment interactions using maize introgression lines
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/738070v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Relatively small genomic introgressions containing quantitative trait loci can have significant impacts on the phenotype of an individual plant. However, the magnitude of phenotypic effects for the same introgression can vary quite substantially in different environments due to allele-by-environment interactions. To study potential patterns of allele-by-environment interactions, fifteen near-isogenic lines (NILs) with >90% B73 genetic background and multiple Mo17 introgressions were grown in 16 different environments. These environments included five geographical locations with multiple planting dates and multiple planting densities. The phenotypic impact of the introgressions was evaluated for up to 26 traits that span different growth stages in each environment to assess allele-by-environment interactions. Results from this study showed that small portions of the genome can drive significant genotype-by-environment interaction across a wide range of vegetative and reproductive traits, and the magnitude of the allele-by-environment interaction varies across traits. Some introgressed segments were more prone to genotype-by-environment interaction than others when evaluating the interaction on a whole plant basis throughout developmental time, indicating variation in phenotypic plasticity throughout the genome. Understanding the profile of allele-by-environment interaction is useful in considerations of how small introgressions of QTL or transgene containing regions might be expected to impact traits in diverse environments.nnKey MessageSignificant allele-by-environment interactions are observed for traits throughout development from small introgressed segments of the genome.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Li, Z., Tirado, S. B., Kadam, D. C., Coffey, L., Miller, N. D., Spalding, E. P., Lorenz, A. J., de Leon, N., Kaeppler, S. M., Schnable, P. S., Springer, N. M., Hirsch, C. N. ]]></dc:creator>
|
||||
<dc:date>2019-08-16</dc:date>
|
||||
<dc:identifier>doi:10.1101/738070</dc:identifier>
|
||||
<dc:title><![CDATA[Characterizing allele-by-environment interactions using maize introgression lines]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-16</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/738021v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
A modular cloning toolkit for genome editing in plants
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/738021v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
The modular cloning (MoClo), based on the Golden Gate (GG) method, has enabled development of cloning systems with standardised genetic parts, e.g. promoters, coding sequences or terminators, that can be easily interchanged and assembled into expression units, which in their own turn can be further assembled into higher order multigene constructs. Here we present an expanded cloning toolkit that contains modules encoding a variety of CRISPR/Cas-based nucleases and their corresponding guide RNA backbones. Among other components, the toolkit includes a number of promoters that allow expression of CRISPR/Cas nucleases (or any other coding sequences) and their guide RNAs in monocots and dicots. As part of the toolkit, we present a set of modules that enable quick and facile assembly of tRNA-sgRNA polycistronic units without a PCR step involved. We believe the toolkit will contribute towards wider adoption of the CRISPR/Cas genome editing technology and modular cloning by researchers across the plant science community.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Hahn, F., Korolev, A., Sanjurjo Loures, L., Nekrasov, V. ]]></dc:creator>
|
||||
<dc:date>2019-08-16</dc:date>
|
||||
<dc:identifier>doi:10.1101/738021</dc:identifier>
|
||||
<dc:title><![CDATA[A modular cloning toolkit for genome editing in plants]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-16</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/736793v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Evaluation of 20 enset (Ensete ventricosum) landraces for response to Xanthomonas vasicola pv. musacearum infection
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/736793v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Bacterial wilt, caused by Xanthomonas vasicola pv. musacearum (Xvm), formerly X. campestris pv. musacearum, is the most threatening and economically important disease of enset (Ensete ventricosum), the multipurpose food security crop orphan to south and southwestern Ethiopia. Xvm has also had a major impact on banana and plantain production in East Africa following its detection in Uganda in 2001 and subsequent spread. Effective control of this disease currently relies on integrated disease management (IDM) strategies including minimization of field pathogen inoculum and deployment of wilt resistant enset landraces. Identifying landraces with stable and durable Xvm resistance will greatly accelerate breeding of varieties that can be included as a component of IDM. In this study, 20 enset landraces previously reported to exhibit lower susceptibility to Xvm were grown in pots under open field conditions and inoculated with an aggressive Xvm inoculum isolated from a disease hotspot area. Longitudinal and survival analyses were applied to each landrace, based on disease units representing a combination of area-under-disease progress stairs, disease index and apparent infection rate. Considerable variation was observed among the 20 landraces; however, none exhibited full immunity to Xvm infection. Three landraces, viz. Haela, Mazia and Lemat (HML), showed lowest susceptibility to Xvm as evidenced by lower disease units and higher survival rates. Landraces Kuro, Gezewet, Bededet, and Alagena showed similar levels of Xvm infection as did HML, but with lower survival rates. By contrast, landrace Arkia showed the highest infection level and lowest survival rate, suggesting a high degree of susceptibility to Xvm. This study identifies new material that can be used in future breeding programmes to develop Xvm-resistant enset varieties.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Muzemil, S., Chala, A., Tesfaye, B., Studholme, D. J., Grant, M., Yemataw, Z., Olango, T. M. ]]></dc:creator>
|
||||
<dc:date>2019-08-15</dc:date>
|
||||
<dc:identifier>doi:10.1101/736793</dc:identifier>
|
||||
<dc:title><![CDATA[Evaluation of 20 enset (Ensete ventricosum) landraces for response to Xanthomonas vasicola pv. musacearum infection]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-15</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/736439v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
A peptide pair coordinates regular ovule initiation patterns with seed number and fruit size
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/736439v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Ovule development in Arabidopsis thaliana involves pattern formation which ensures that ovules are regularly arranged in the pistils to reduce competition for nutrients and space. Mechanisms underlying pattern formation in plants, such as phyllotaxis, flower morphogenesis or lateral root initiation, have been extensively studied, and genes controlling the initiation of ovules have been identified. However, how a regular spacing of ovules is achieved is not known. Using natural variation analysis combined with quantitative trait locus analysis, we found that the spacing of ovules in the developing fruits is controlled by two secreted peptides, EPFL2 and EPFL9 (also known as Stomagen), and their receptors from the ERECTA (ER) family that act from the carpel wall and the placental tissue. We found that a signalling pathway controlled by EPFL9 acting from the carpel wall through the LRR-receptor kinases ER, ERL1 and ERL2 promotes fruit growth. Regular spacing of ovules depends on EPFL2 expression in the carpel wall and in the inter-ovule spaces, where it acts through ERL1 and ERL2. Loss of EPFL2 signalling results in shorter fruits and irregular spacing of ovules or even ovule twinning. The EPFL2 expression pattern between ovules is under negative-feedback regulation by auxin, which accumulates in the arising ovule primordia. We propose that the auxin-EPFL2 signalling module evolved to control the initiation and regular, equidistant spacing of ovule primordia, which serves to minimise competition between developing seeds. Together, EPFL2 and EPFL9 coordinate ovule patterning and thereby seed number with fruit growth through a set of shared receptors.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Kawamoto, N., Pino del Carpio, D., Hofmann, A., Mizuta, Y., Daisuke, K., Higashiyama, T., Uchida, N., Torii, K. U., Colombo, L., Groth, G., Simon, R. ]]></dc:creator>
|
||||
<dc:date>2019-08-15</dc:date>
|
||||
<dc:identifier>doi:10.1101/736439</dc:identifier>
|
||||
<dc:title><![CDATA[A peptide pair coordinates regular ovule initiation patterns with seed number and fruit size]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-15</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/736397v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Multi-Dimensional Machine Learning Approaches for Fruit Shape Recognition and Phenotyping in Strawberry
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/736397v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
BackgroundShape is a critical element of the visual appeal of strawberry fruit and determined by both genetic and non-genetic factors. Current fruit phenotyping approaches for external characteristics in strawberry rely on the human eye to make categorical assessments. However, fruit shape is multi-dimensional, continuously variable, and not adequately described by a single quantitative variable. Morphometric approaches enable the study of complex forms but are often abstract and difficult to interpret. In this study, we developed a mathematical approach for transforming fruit shape classifications from digital images onto an ordinal scale called the principal progression of k clusters (PPKC). We use these human-recognizable shape categories to select features extracted from multiple morphometric analyses that are best fit for genome-wide and forward genetic analyses.nnResultsWe transformed images of strawberry fruit into human-recognizable categories using unsupervised machine learning, discovered four principal shape categories, and inferred progression using PPKC. We extracted 67 quantitative features from digital images of strawberries using a suite of morphometric analyses and multi-variate approaches. These analyses defined informative feature sets that effectively captured quantitative differences between shape classes. Classification accuracy ranged from 68.9 - 99.3% for the newly created, genetically correlated phenotypic variables describing a shape.nnConclusionsOur results demonstrated that strawberry fruit shapes could be robustly quantified, accurately classified, and empirically ordered using image analyses, machine learning, and PPKC. We generated a dictionary of quantitative traits for studying and predicting shape classes and identifying genetic factors underlying phenotypic variability for fruit shape in strawberry. The methods and approaches we applied in strawberry should apply to other fruits, vegetables, and specialty crops.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Feldmann, M. J., Hardigan, M. A., Famula, R. A., Lopez, C. M., Tabb, A., Cole, G. S., Knapp, S. J. ]]></dc:creator>
|
||||
<dc:date>2019-08-15</dc:date>
|
||||
<dc:identifier>doi:10.1101/736397</dc:identifier>
|
||||
<dc:title><![CDATA[Multi-Dimensional Machine Learning Approaches for Fruit Shape Recognition and Phenotyping in Strawberry]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-15</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/734525v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
An ethnobotanical study of the genus Elymus
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/734525v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Grains of domesticated grasses (Poaceae) have long been a global food source and constitute the bulk of calories in the human diet. Recent efforts to establish more sustainable agricultural systems have focused in part on the development of herbaceous, perennial crops. Perennial plants have extensive root systems that stabilize soil and absorb water and nutrients at greater rates than their annual counterparts; consequently, perennial grasses are important potential candidates for grain domestication. While most contemporary grass domesticates consumed by humans are annual plants, there are over 7,000 perennial grass species that remain largely unexplored for domestication purposes. Documenting ethnobotanical uses of wild perennial grasses could aid in the evaluation of candidate species for de novo crop development. The objectives of this study are 1) to provide an ethnobotanical survey of the grass genus Elymus; and 2) to investigate floret size variation in species used by people. Elymus includes approximately 150 perennial species distributed in temperate and subtropical regions, of which at least 21 taxa have recorded nutritional, medicinal, and/or material uses. Elymus species used for food by humans warrant pre-breeding and future analyses to assess potential utility in perennial agricultural systems.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Frawley, E. S., Ciotir, C., Micke, B., Rubin, M. J., Miller, A. ]]></dc:creator>
|
||||
<dc:date>2019-08-15</dc:date>
|
||||
<dc:identifier>doi:10.1101/734525</dc:identifier>
|
||||
<dc:title><![CDATA[An ethnobotanical study of the genus Elymus]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-15</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/736199v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Drought sensitivity of leaflet growth, biomass accumulation, and resource partitioning predicts yield in common bean
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/736199v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
While drought limits yield largely by its impact on photosynthesis and therefore biomass accumulation, biomass is not the strongest predictor of yield under drought. Instead, resource partitioning efficiency, measured by how much total pod weight is contained in seeds at maturity (Pod Harvest Index), is the stronger correlate in Phaseolus vulgaris. Using 20 field-grown genotypes, we expanded on this finding by pairing yield and resource partitioning data with growth rates of leaflets and pods. We hypothesized that genotypes which decreased partitioning and yield most under drought would also have strongest decreases in growth rates. We found that while neither leaflet nor pod growth rates correlated with seed yield or partitioning, impacts to leaflet growth rates under drought correlate with impacts to yield and partitioning. As expected, biomass production correlated with yield, yet correlations between the decreases to these two traits under drought were even stronger. This suggests that while biomass contributes to yield, biomass sensitivity to drought is a stronger predictor. Lastly, under drought, genotypes may achieve similar canopy biomass yet different yields, which can be explained by higher or lower partitioning efficiencies. Our findings suggest that inherent sensitivity to drought may be used as a predictor of yield.nnHIGHLIGHTIn common bean, higher biomass accumulation under drought alone does not guarantee higher yield, as maintenance of higher growth rates and partitioning processes act as an additional requirement.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Hageman, A. N., Urban, M. O., Van Volkenburgh, E. ]]></dc:creator>
|
||||
<dc:date>2019-08-15</dc:date>
|
||||
<dc:identifier>doi:10.1101/736199</dc:identifier>
|
||||
<dc:title><![CDATA[Drought sensitivity of leaflet growth, biomass accumulation, and resource partitioning predicts yield in common bean]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-15</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/736249v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Dynamic regulation of immunity through post-translational control of defense transcript splicing
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/736249v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Survival of all living organisms requires the ability to detect attack and swiftly counter with protective immune responses. Despite considerable mechanistic advances, interconnectivity of signaling circuits often remains unclear. A newly-characterized protein, IMMUNOREGULATORY RNA-BINDING PROTEIN (IRR), negatively regulates immune responses in both maize and Arabidopsis, with disrupted function resulting in enhanced disease resistance. IRR physically interacts with, and promotes canonical splicing of, transcripts encoding defense signaling proteins, including the key negative regulator of pattern recognition receptor signaling complexes, CALCIUM-DEPENDENT PROTEIN KINASE 28 (CPK28). Upon immune activation by Plant Elicitor Peptides (Peps), IRR is dephosphorylated, disrupting interaction with CPK28 transcripts and resulting in accumulation of an alternative splice variant encoding a truncated CPK28 protein with impaired kinase activity and diminished function as a negative regulator. We demonstrate a novel circuit linking Pep-induced post-translational modification of IRR with post-transcriptionally-mediated attenuation of CPK28 function to dynamically amplify Pep signaling and immune output.nnOne Sentence SummaryPlant innate immunity is promoted by post-translational modification of a novel RNA-binding protein that regulates alternative splicing of transcripts encoding defense signaling proteins to dynamically increase immune receptor signaling capacity through deactivation of a key signal-buffering circuit.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Dressano, K., Weckwerth, P., Poretsky, E., Takahashi, Y., Villarreal, C., Shen, Z., Schroeder, J., Briggs, S., Huffaker, A. ]]></dc:creator>
|
||||
<dc:date>2019-08-15</dc:date>
|
||||
<dc:identifier>doi:10.1101/736249</dc:identifier>
|
||||
<dc:title><![CDATA[Dynamic regulation of immunity through post-translational control of defense transcript splicing]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-15</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/733857v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
How do three cytosolic glutamine synthetase isozymes of wheat perform N assimilation and translocation?
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/733857v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
To understand how the three cytosolic glutamine synthetase (GS1) isozymes of wheat (Triticum aestivum L., TaGS1) perform nitrogen assimilation and translocation, we studied the kinetic properties of TaGS1 isozymes, the effects of nitrogen on the expression and localization of TaGS1 isozymes with specific antibodies, and the nitrogen metabolism. The results showed TaGS1;1, the dominant TaGS1 isozyme, had a high affinity for substrates, and was widely localized in the mesophyll cells, root pericycle and root tip meristematic zone, suggesting it was the primary isozyme for N assimilation. TaGS1;2, with a high affinity for Glu, was activated by Gln, and was mainly localized in the around vascular tissues, indicating that TaGS1;2 catalyzed Gln synthesis in low Glu concentration, then the Gln returned to activate TaGS1;2, which may lead to the rapid accumulation of Gln around the vascular tissues. TaGS1;3 had low affinity for substrates but the highest Vmax among TaGS1, was mainly localized in the root tip meristematic zone; exogenous NH4+ could promote TaGS1;3 expressing, indicating that TaGS1;3 could rapidly assimilate NH4+ to relieve NH4+ toxicity. In conclusion, TaGS1;1, TaGS1;2 and TaGS1;3 have different role in N assimilation, Gln translocation and relieving ammonium toxicity, respectively, and synergistically perform nitrogen assimilation and translocation.nnHighlightThree cytosolic glutamine synthase isozymes of wheat have different role and synergistically perform nitrogen assimilation and translocation.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Wei, Y., Wang, X., Zhang, Z., Xiong, S., Zhang, Y., Wang, L., Meng, X., Zhang, J., Ma, X. ]]></dc:creator>
|
||||
<dc:date>2019-08-14</dc:date>
|
||||
<dc:identifier>doi:10.1101/733857</dc:identifier>
|
||||
<dc:title><![CDATA[How do three cytosolic glutamine synthetase isozymes of wheat perform N assimilation and translocation?]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-14</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
<item rdf:about="http://biorxiv.org/cgi/content/short/726125v1?rss=1">
|
||||
<title>
|
||||
<![CDATA[
|
||||
Cell-surface receptors enable perception of extracellular cytokinins
|
||||
]]>
|
||||
</title>
|
||||
<link>
|
||||
http://biorxiv.org/cgi/content/short/726125v1?rss=1
|
||||
</link>
|
||||
<description><![CDATA[
|
||||
Cytokinins are mobile multifunctional plant hormones with roles in development and stress resilience 1,2. Although cytokinin receptors are substantially localised to the endoplasmic reticulum 3-5, the cellular sites of cytokinin perception continue to be debated 1,6,7. Several cytokinin types display bioactivity 8,9 and recently a cell-specific cytokinin gradient was reported in roots 10. Yet, the importance of spatially heterogeneous cytokinin distribution and the specific cytokinin(s) that account for the different responses remain unclear. Here we show that cytokinin perception by plasma membrane receptors is an effective path for cytokinin response in root cells. Readout from a Two Component Signalling cytokinin-specific reporter (TCSn::GFP;11) is closely matched to intracellular cytokinin content, yet a proportion of bioactive cytokinins are detected in the extracellular fluid. Using cytokinins covalently linked to beads that could not pass the plasma membrane, we demonstrate that strong TCSn activation still occurs and that this response is greatly diminished in cytokinin receptor mutants. Although intracellular receptors play significant roles, we argue for a revision of concepts of cytokinin perception to include the spatial dimensions. In particular, selective ligand-receptor affinities, cellular localisation and tissue distribution of bioactive cytokinins, their receptors, transporters and inactivation enzymes appear all to be components of the signalling regulatory mechanisms.
|
||||
]]></description>
|
||||
<dc:creator><![CDATA[ Antoniadi, I., Novak, O., Gelova, Z., Johnson, A., Plihal, O., Vain, T., Simersky, R., Mik, V., Karady, M., Pernisova, M., Plackova, L., Opassathian, K., Hejatko, J., Robert, S., Friml, J., Dolezal, K., Ljung, K., Turnbull, C. ]]></dc:creator>
|
||||
<dc:date>2019-08-14</dc:date>
|
||||
<dc:identifier>doi:10.1101/726125</dc:identifier>
|
||||
<dc:title><![CDATA[Cell-surface receptors enable perception of extracellular cytokinins]]></dc:title>
|
||||
<dc:publisher>Cold Spring Harbor Laboratory</dc:publisher>
|
||||
<prism:publicationDate>2019-08-14</prism:publicationDate>
|
||||
<prism:section></prism:section>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
|
||||
</rdf:RDF>
|
2
Parser/Tests/ParserTests/Resources/cloudblog.rss
Normal file
2
Parser/Tests/ParserTests/Resources/cloudblog.rss
Normal file
File diff suppressed because one or more lines are too long
2329
Parser/Tests/ParserTests/Resources/coco.html
Normal file
2329
Parser/Tests/ParserTests/Resources/coco.html
Normal file
File diff suppressed because one or more lines are too long
1
Parser/Tests/ParserTests/Resources/curt.json
Normal file
1
Parser/Tests/ParserTests/Resources/curt.json
Normal file
File diff suppressed because one or more lines are too long
1475
Parser/Tests/ParserTests/Resources/dcrainmaker.xml
Normal file
1475
Parser/Tests/ParserTests/Resources/dcrainmaker.xml
Normal file
File diff suppressed because one or more lines are too long
61
Parser/Tests/ParserTests/Resources/donthitsave.xml
Normal file
61
Parser/Tests/ParserTests/Resources/donthitsave.xml
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Don't Hit Save</title>
<link>https://donthitsave.com</link>
<description>The webcomic that dares to take on the gritty world of software, technology, and indie game development (by Jeff Lofvers).</description>
<language>en-us</language>
<item>
<title>Skipping Around</title>
<pubDate>Fri, 24 May 2019 00:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/05/24/skipping-around</link>
<description><![CDATA[<img style="max-width:800px;" alt="Skipping Around" src="https://donthitsave.com/comicimages/dhs_2019-05-24_skipping-around.png"><p>I was nearly murdered by a coconut today.</p>
|
||||
|
||||
<p>For real.</p>
|
||||
|
||||
<p>This happened as I took the dogs on their morning poop stroll (I can no longer speak or spell the word “walk” in my home). I wandered up and down the sidewalk, on some grass, and headed back home.</p>
|
||||
|
||||
<p>Near the end of the walk, I stopped to fiddle with my phone. I was listening to a podcast, and needed to skip the commercials. I stood in place for 30, maybe 40 seconds. When I was done, I took two steps... <a href="https://donthitsave.com/comic/2019/05/24/skipping-around">[read more]</a></p>]]></description>
</item>
<item>
<title>Budgets</title>
<pubDate>Fri, 17 May 2019 00:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/05/17/budgets</link>
<description><![CDATA[<img style="max-width:800px;" alt="Budgets" src="https://donthitsave.com/comicimages/dhs_2019-05-17_budgets.png"><p>It's time I woke up before dawn again.</p>
|
||||
|
||||
|
||||
<p>Voluntarily.</p>
|
||||
|
||||
|
||||
<p>It not easy to beat the sun at its own game. Most of my social and computer life has been dominated by a late nights. I work during the day because I have to, then stay up as late as I want because I'm a grown up. Every once in a while, however, I will mix things up:</p>
|
||||
|
||||
|
||||
<ul>
|
||||
<li>A decade ago, I woke up every morning at 4:30 in order to go running. I did this for over two years. This lasted until I... <a href="https://donthitsave.com/comic/2019/05/17/budgets">[read more]</a></p>]]></description>
</item>
<item>
<title>Trade-In</title>
<pubDate>Fri, 10 May 2019 00:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/05/10/trade-in</link>
<description><![CDATA[<img style="max-width:800px;" alt="Trade-In" src="https://donthitsave.com/comicimages/dhs_2019-05-10_trade-in.png"><p>I bought a hammer this week. My first Hawaiian hammer.</p>
|
||||
|
||||
<p>I will use it not to hang something, not to bang something, but to remove a nail.</p>
|
||||
|
||||
<p><em>I hate that nail. </em></p>
|
||||
|
||||
<p>Today is a good day.</p>
|
||||
]]></description>
</item>
<item>
<title>The Screwup</title>
<pubDate>Fri, 03 May 2019 06:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/05/03/the-screwup</link>
<description><![CDATA[<img style="max-width:800px;" alt="The Screwup" src="https://donthitsave.com/comicimages/dhs_2019-05-03_the-screwup.png"><p>How's your night vision?</p>
|
||||
|
||||
|
||||
<p>When I was little, mine was excellent. I could spot animals while walking at night, read in low light conditions, and effectively navigate the house based on the light of digital clocks. It only got better with time.</p>
|
||||
|
||||
|
||||
<p>This wasn't a super power. I was adjusting to my surroundings.</p>
|
||||
|
||||
|
||||
<p>I spent my younger years in upstate NY. Winter and cloudy weather seemed to last 9 months, which meant that I got used to gloomy conditions. Our house... <a href="https://donthitsave.com/comic/2019/05/03/the-screwup">[read more]</a></p>]]></description>
</item>
<item>
<title>Focus On The Task At Hand With A Little Help From Google</title>
<pubDate>Fri, 26 Apr 2019 00:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/04/26/ads-for-google</link>
<description><![CDATA[<img style="max-width:800px;" alt="Focus On The Task At Hand With A Little Help From Google" src="https://donthitsave.com/comicimages/dhs_2019-04-26_ads-for-google.png"><p>Game of Thrones is back, Avengers are back, and the Tick Season 2 is out. It is a good month for amusing ourselves.</p>
|
||||
|
||||
<p>So why are you spending your time here?</p>
|
||||
|
||||
<p>Oh I know, you need some sort of control sample. What’s the point of having compelling and enjoyable entertainment if you don’t have something dull to compare it to? I see. It all makes sense now.</p>
|
||||
|
||||
<p>Wow. I can’t believe you think this comic dull. Nice, reader, reeeeeaaaal nice. I’ll remember... <a href="https://donthitsave.com/comic/2019/04/26/ads-for-google">[read more]</a></p>]]></description>
</item>
<item>
<title>Clever Variable Name</title>
<pubDate>Fri, 19 Apr 2019 00:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/04/19/clever-variable-name</link>
<description><![CDATA[<img style="max-width:800px;" alt="Clever Variable Name" src="https://donthitsave.com/comicimages/dhs_2019-04-19_clever-variable-name.png"><p>Apologies for the extraordinarily late comic.</p>
|
||||
|
||||
<p>This is the first one ever delayed at length by my news. Here's why:</p>
|
||||
|
||||
<p>I wrote the comic on Thursday night. When I went to write the news in the morning, I decided to focus on the adorable habits of my two dogs. One of the monsters was acting weird. Comically weird.</p>
|
||||
|
||||
<p>I decided to write the news about that. I found it quite funny. I wrote a page and a half about how my dog was suddenly scared of everything. She ran from... <a href="https://donthitsave.com/comic/2019/04/19/clever-variable-name">[read more]</a></p>]]></description>
</item>
<item>
<title>Wrapping Up Client Work</title>
<pubDate>Fri, 12 Apr 2019 00:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/04/12/finishing-up-client-work</link>
<description><![CDATA[<img style="max-width:800px;" alt="Wrapping Up Client Work" src="https://donthitsave.com/comicimages/dhs_2019-04-12_moved-on.png"><p>I made Tofacos for dinner-- tofu tacos that are absolutely abhorrent. I love the food here in Hawaii, but when breakfast, lunch, and dinner consists of fish and noodles, things tend to get weird when I deviate.</p>
|
||||
|
||||
<p>I left about as big a mess as you can imagine while stirring the “food”. I’m not a great cook, I’m an adequate one. My system involves hopping from one nearly burning pile of food to another, narrowly staying ahead of a charred, blackened meal.</p>
|
||||
|
||||
<p>I’m... <a href="https://donthitsave.com/comic/2019/04/12/finishing-up-client-work">[read more]</a></p>]]></description>
</item>
<item>
<title>Personal Journey</title>
<pubDate>Fri, 05 Apr 2019 00:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/04/05/personal-journey</link>
<description><![CDATA[<img style="max-width:800px;" alt="Personal Journey" src="https://donthitsave.com/comicimages/dhs_2019-04-05_no-words.png"><p>To prove that I can.</p>]]></description>
</item>
<item>
<title>Reward</title>
<pubDate>Fri, 29 Mar 2019 08:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/03/29/reward</link>
<description><![CDATA[<img style="max-width:800px;" alt="Reward" src="https://donthitsave.com/comicimages/dhs_2019-03-29_reward.png"><p>Today’s comic is dedicated to my very sick team lead. Too bad you’re too sick to check and see if today’s comic is true.</p>
|
||||
|
||||
<p>For everyone else out there, welcome to the last 3 days you have to enter my <a href="http://donthitsave.com/comic/2019/02/22/forgotten-headphones">sweepstakes thingy</a>. If you want me to draw you, the deadline is Sunday, March 31st. Get to it!</p>
|
||||
|
||||
<p>If you’re reading after that date, or if you don’t care about my sweepstakes, I have only one... <a href="https://donthitsave.com/comic/2019/03/29/reward">[read more]</a></p>]]></description>
</item>
<item>
<title>Costs</title>
<pubDate>Fri, 22 Mar 2019 00:00:00 -0700</pubDate>
<link>https://donthitsave.com/comic/2019/03/22/costs</link>
<description><![CDATA[<img style="max-width:800px;" alt="Costs" src="https://donthitsave.com/comicimages/dhs_2019-03-22_cost.png"><p>It's 2am here. What time is it there? I'll post some news in some time.</p>
|
||||
|
||||
<p>…</p>
|
||||
|
||||
<p>Okay, I’m back… at 2am. A different 2am. Let’s make this quick:</p>
|
||||
|
||||
<p>For those checking later, or too busy to google, here is <a href="https://www.wired.com/story/facebook-password-bug-android-phone-spy-airbnb-tech-news/" target="_blank">an article</a> about Facebook’s latest mishap, as mentioned in today’s comic.</p>
|
||||
|
||||
<p>In personal news, I've been thorough enjoying the new... <a href="https://donthitsave.com/comic/2019/03/22/costs">[read more]</a></p>]]></description>
</item>
</channel>
</rss>
|
930
Parser/Tests/ParserTests/Resources/expertopinionent.atom
Normal file
930
Parser/Tests/ParserTests/Resources/expertopinionent.atom
Normal file
@ -0,0 +1,930 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:thr="http://purl.org/syndication/thread/1.0">
|
||||
<title>ENT expert opinion</title>
|
||||
<link rel="self" type="application/atom+xml" href="http://expertopinionent.typepad.com/my-blog/atom.xml" />
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/" />
|
||||
<id>tag:typepad.com,2003:weblog-107776253500823309</id>
|
||||
<updated>2017-04-07T12:20:50+10:30</updated>
|
||||
|
||||
<generator uri="http://www.typepad.com/">TypePad</generator>
|
||||
<entry>
|
||||
<title>Advances and Testing in Vestibular Pathology</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2017/04/advances-and-testing-in-vestibular-pathology.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2017/04/advances-and-testing-in-vestibular-pathology.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01bb098db7b9970d</id>
|
||||
<published>2017-04-07T12:20:50+10:30</published>
|
||||
<updated>2017-04-07T12:20:50+10:30</updated>
|
||||
<summary>Dr Nicholas Jufas interviews Dr Dave Pothier, a Staff Otologist & Neurotologist in the Department of Otolaryngology, Head & Neck Surgery at Toronto General Hospital, and Assistant Professor at the University of Toronto. He has a strong interest in clinical and translational research and has published and presentented extensively. In...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><div><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/DavePothier-EeoPodcast.mp3">Dr Nicholas Jufas interviews Dr Dave <span class="il">Pothier</span>, a Staff Otologist & Neurotologist in the Department of Otolaryngology, Head & Neck Surgery at Toronto General Hospital, and Assistant Professor at the University of Toronto.</a><br /><br /><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/DavePothier-EeoPodcast.mp3">He has a strong interest in clinical and translational research and has published and presentented extensively.  In 2011, he won the Politzer Society Prize for research into the treatment of oscillopsia. His main research interests are in bilateral vestibular loss, oscillopsia and catastrophization.</a></div>
|
||||
<div> </div>
|
||||
<div><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/DavePothier-EeoPodcast.mp3">This interview focuses on controversies in vestibular testing, as well as recent advances.  Clinical applicability and approaches to patients with difficult vestibulopathic issues are also covered.</a></div></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/DavePothier-EeoPodcast.mp3" />
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/DavePothier-EeoPodcast.mp3" />
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/DavePothier-EeoPodcast.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Advanced Cochlear Implantation</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2017/03/advanced-cochlear-implantation.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2017/03/advanced-cochlear-implantation.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01bb0985cb42970d</id>
|
||||
<published>2017-03-21T16:28:51+11:00</published>
|
||||
<updated>2017-03-21T16:30:43+11:00</updated>
|
||||
<summary>Dr. Nicholas Jufas speaks with Professor Thomas Balkany, an otolaryngologist and neurotologist specializing in cochlear implantation. He is the Hotchkiss Endowment Distinguished Professor and Chairman Emeritus in the Department of Otolaryngology and Professor of Neurological Surgery and Pediatrics at the University of Miami Miller School of Medicine. He holds 14...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
<category term="Science" />
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Eeo-ThomasBalkanyfinal.mp3">Dr. Nicholas Jufas speaks with Professor <span class="il">Thomas</span> Balkany, an otolaryngologist and neurotologist specializing in cochlear implantation.  He is the Hotchkiss Endowment Distinguished Professor and Chairman Emeritus in the Department of Otolaryngology and Professor of Neurological Surgery and Pediatrics at the University of Miami Miller School of Medicine. </a><br /><br /><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Eeo-ThomasBalkanyfinal.mp3">He holds 14 U.S. and international patents on cochlear implant technologies. Additionally, he has written three books and more than 300 scientific publications on the topic of ear surgery.  In 2012, he founded the Institute for Cochlear Implant Training, a nonprofit corporation based in Florida, which provides 3 month-long advanced training courses in cochlear implant surgery.</a><br /><br /><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Eeo-ThomasBalkanyfinal.mp3">This interview focuses on current controversies and advanced techniques in cochlear implantation.</a></p>
|
||||
<div>
|
||||
<div><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Eeo-ThomasBalkanyfinal.mp3"> </a></div>
|
||||
<div> </div>
|
||||
</div></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Eeo-ThomasBalkanyfinal.mp3" />
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Eeo-ThomasBalkanyfinal.mp3" />
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Eeo-ThomasBalkanyfinal.mp3" />
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Eeo-ThomasBalkanyfinal.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Middle Ear Mechanics</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2016/05/middle-ear-mechanics.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2016/05/middle-ear-mechanics.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01b7c85ea3ef970b</id>
|
||||
<published>2016-05-19T01:34:31+10:30</published>
|
||||
<updated>2016-05-19T01:34:31+10:30</updated>
|
||||
<summary>Dr Nicholas Jufas interviews Professor Manohar Bance, an Otologist and Neurotologist and Head of Otolaryngology-Head and Neck Surgery at QEII Health Services Centre and Dalhousie University in Halifax. He is Director of the EAR lab, a middle ear mechanics laboratory at Dalhousie University. The interview covers the basics principles in...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Bance-MeePodcast.mp3">Dr Nicholas Jufas interviews Professor Manohar Bance, an Otologist and Neurotologist and Head of Otolaryngology-Head and Neck Surgery at QEII Health Services Centre and Dalhousie University in Halifax. He is Director of the EAR lab, a middle ear mechanics laboratory at Dalhousie University. The interview covers the basics principles in middle ear mechanics, and how these can be applied to everyday surgery and pathology of the ear. Along we way, some myths will be debunked and some intriguing approaches discussed.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Bance-MeePodcast.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Dysphagia</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2016/03/dysphagia.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2016/03/dysphagia.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01b8d1ae5718970c</id>
|
||||
<published>2016-03-15T02:49:01+11:00</published>
|
||||
<updated>2016-03-15T02:49:01+11:00</updated>
|
||||
<summary>In this podcast, Dr Niall Jefferson interviews Dr Gregory Postma, an international leader in the diagnosis and management of dysphagia. The discussion defines the symptom and elaborates on the history, exam and relevant investigations. We close with a discussion on current and future directions for diagnosis and management of this...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Postma1.mp3">In this podcast, Dr Niall Jefferson interviews Dr Gregory Postma, an international leader in the diagnosis and management of dysphagia. The discussion defines the symptom and elaborates on the history, exam and relevant investigations. We close with a discussion on current and future directions for diagnosis and management of this sometimes complex condition.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Postma1.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Congenital Aural Atresia</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2016/01/congenital-aural-atresia.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2016/01/congenital-aural-atresia.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01bb08aa7785970d</id>
|
||||
<published>2016-01-12T03:34:07+11:00</published>
|
||||
<updated>2016-01-12T03:34:07+11:00</updated>
|
||||
<summary>Dr Niall Jefferson sits down with Dr Dan Choo, current director of the division of pediatric otolaryngology at Cincinnati Children’s Hospital and medical center to discuss Congenital aural atresia. The discussion covers incidence, clinical examination and physical examination pearls of the CAA patient as well as operative candidates, tips and...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p class="yiv3093686624MsoNormal" id="yui_3_16_0_1_1452523606110_6466"><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Choo-congenitalAuralAtresia.mp3">Dr Niall Jefferson sits down with Dr Dan Choo, current director of the division of pediatric otolaryngology at Cincinnati Children’s Hospital and medical center to discuss Congenital aural atresia. The discussion covers incidence, clinical examination and physical examination pearls of the CAA patient as well as operative candidates, tips and techniques.</a></p>
|
||||
<p> </p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/Choo-congenitalAuralAtresia.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Non-Pulsatile Tinnitus</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/12/non-pulsatile-tinnitus.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/12/non-pulsatile-tinnitus.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01bb08974494970d</id>
|
||||
<published>2015-12-01T03:28:02+11:00</published>
|
||||
<updated>2015-12-01T03:28:02+11:00</updated>
|
||||
<summary>Dr Nicholas Jufas interviews Professor Michael Seidman, Director of the Division of Otologic/Neurotologic Surgery at the Henry Ford Health System in Bloomfield, Michigan. He is also the Director of the Center for Integrative Medicine and Professor at Wayne State University. He is the past chair of the Board of Governors...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/MichaelSeidman-EeoPodcast-Non-pulsatileTinnitus.mp3">Dr Nicholas Jufas interviews Professor Michael Seidman, Director of the Division of Otologic/Neurotologic Surgery at the Henry Ford Health System in Bloomfield, Michigan. He is also the Director of the Center for Integrative Medicine and Professor at Wayne State University. He is the past chair of the Board of Governors and on the Board of Directors for the American Academy of Otolaryngology, Head and Neck Surgery. He has recently co-authored an article on tinnitus in Current Opinion in Otolaryngology. The interview covers the definition, workup and prognosis and a range of treatments for non-pulsatile tinnitus including lifestyle changes, nutritional supplements, masking treatments, medical and surgical options. It also looks at future directions in the treatment of tinnitus.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/MichaelSeidman-EeoPodcast-Non-pulsatileTinnitus.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Bone Anchored Hearing Devices</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/11/bone-anchored-hearing-devices.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/11/bone-anchored-hearing-devices.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01bb088c3489970d</id>
|
||||
<published>2015-11-07T02:08:16+11:00</published>
|
||||
<updated>2015-11-07T02:08:16+11:00</updated>
|
||||
<summary>In this podcast Dr Niall Jefferson sits down again with Dr Phillip Chang an ENT surgeon based in Sydney to discuss bone anchored devices. The discussion covers typical presentation, standard workup, relevant investigations and the pros and cons of different implant devices as well as expanding technologies and future directions.</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://ia801702.us.archive.org/23/items/ENTExpertOpinion/BahaPhillipChangFinal.mp3">In this podcast Dr Niall Jefferson sits down again with Dr Phillip Chang an ENT surgeon based in Sydney to discuss bone anchored devices. The discussion covers typical presentation, standard workup, relevant investigations and the pros and cons of different implant devices as well as expanding technologies and future directions.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia801702.us.archive.org/23/items/ENTExpertOpinion/BahaPhillipChangFinal.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Thyroid Nodules</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/10/thyroid-nodules.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/10/thyroid-nodules.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01b8d169f098970c</id>
|
||||
<published>2015-10-20T03:41:58+11:00</published>
|
||||
<updated>2015-10-20T03:41:58+11:00</updated>
|
||||
<summary>Dr. Niall Jefferson talks Thyroid Nodules with Dr. Faruque Riffat</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/ThyroidNodule.wav" target="_blank" title="">Dr. Niall Jefferson talks Thyroid Nodules with Dr. Faruque Riffat</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Sudden Sensorineural Hearing Loss</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/09/sudden-sensorineural-hearing-loss.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/09/sudden-sensorineural-hearing-loss.html" thr:count="1" thr:updated="2015-12-05T22:58:38+11:00" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01bb086c2e44970d</id>
|
||||
<published>2015-09-03T00:35:21+10:30</published>
|
||||
<updated>2015-09-03T00:35:21+10:30</updated>
|
||||
<summary>Dr Nicholas Jufas interviews Dr Nirmal Patel. The interview covers the definition, workup and prognosis and treatment of sudden sensorineural hearing loss, as well as salvage treatment and management options and future directions.</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/SsnhlPodcastPatel.mp3" target="_self" title="">Dr Nicholas Jufas interviews Dr Nirmal Patel. The interview covers the definition, workup and prognosis and treatment of sudden sensorineural hearing loss, as well as salvage treatment and management options and future directions.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://ia601702.us.archive.org/23/items/ENTExpertOpinion/SsnhlPodcastPatel.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Expanding Indications for Cochlear Implantation - An Update </title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/04/expanding-indications-for-cochlear-implantation-an-update-.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/04/expanding-indications-for-cochlear-implantation-an-update-.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01b7c779cb5e970b</id>
|
||||
<published>2015-04-16T01:06:26+10:30</published>
|
||||
<updated>2015-04-16T22:46:00+10:30</updated>
|
||||
<summary>Dr. Niall Jefferson discusses the evolving trends in Cochlear implantation with Dr. Phillip Chang</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/DrPhillipchangcochlear.mp3" target="_blank" title="">Dr. Niall Jefferson discusses the evolving trends in Cochlear implantation with Dr. Phillip Chang</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/DrPhillipchangcochlear.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Thyroglossal Duct Remnants</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/01/thyroglossal-duct-remnants.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2015/01/thyroglossal-duct-remnants.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01b7c72e940f970b</id>
|
||||
<published>2015-01-06T01:16:23+11:00</published>
|
||||
<updated>2015-01-06T01:16:23+11:00</updated>
|
||||
<summary>Dr Niall Jefferson talks with Associate Professor Jeffery Koempel about thyroglossal duct remnants. They discuss its origins, presentation and relevant investigations in particular the role of imaging. The surgical management covers the key tips and pitfalls especially when managing the supra-hyoid area.</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><div dir="ltr" id="yiv8699201427yui_3_16_0_1_1416949250468_3064"><a href="https://archive.org/download/ENTExpertOpinion/JefferyKoempelThyroglossalDuctRemnants.mp3" target="_self" title="">Dr Niall Jefferson talks with Associate Professor Jeffery Koempel about thyroglossal duct remnants. They discuss its origins, presentation and relevant investigations in particular the role of imaging. The surgical management covers the key tips and pitfalls especially when managing the supra-hyoid area.</a></div></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/JefferyKoempelThyroglossalDuctRemnants.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Endoscopic Ear Surgery</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/12/endoscopic-ear-surgery.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/12/endoscopic-ear-surgery.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01bb07b926ff970d</id>
|
||||
<published>2014-12-01T11:55:01+11:00</published>
|
||||
<updated>2014-12-01T11:56:02+11:00</updated>
|
||||
<summary>Dr. Nicholas Jufas interviews Prof. Daniele Marchioni, an ENT surgeon based in Verona, Italy. He is a founding member of the International Working Group on Endoscopic Ear Surgery (IWGEES). Prof Marchioni has recently published the first textbook on the technique - "Endoscopic Ear Surgery: Principles, Indications and Techniques". The interview...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/DanieleMarchioni.mp3" target="_self" title="">Dr. Nicholas Jufas interviews Prof. Daniele Marchioni, an ENT surgeon based in Verona, Italy. He is a founding member of the International Working Group on Endoscopic Ear Surgery (IWGEES). Prof Marchioni has recently published the first textbook on the technique - "Endoscopic Ear Surgery: Principles, Indications and Techniques". The interview covers key indications, contraindications and what you need to get started in endoscopic ear surgery, as well as some future directions for the technique.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/DanieleMarchioni.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Radiotherapy for Metastatic Head and Neck Squamous Cell Carcinoma </title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/11/radiotherapy-for-metastatic-head-and-neck-squamous-cell-carcinoma-.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/11/radiotherapy-for-metastatic-head-and-neck-squamous-cell-carcinoma-.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01b7c70456df970b</id>
|
||||
<published>2014-11-11T18:13:20+11:00</published>
|
||||
<updated>2014-12-01T15:24:55+11:00</updated>
|
||||
<summary>Dr Niall Jefferson interviews Dr Stephen Cooper, a radiation oncologist and current chair of the multi-disciplinary Head and Neck cancer meeting at St Vincent’s hospital in Sydney on the topic of radiotherapy for metastatic squamous cell carcinoma of the head and neck. The discussion covers the basics of radiotherapy, current...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/StehenCooper.mp3" target="_self">Dr Niall Jefferson interviews Dr Stephen Cooper, a radiation oncologist and current chair of the multi-disciplinary Head and Neck cancer meeting at St Vincent’s hospital in Sydney on the topic of radiotherapy for metastatic squamous cell carcinoma of the head and neck. The discussion covers the basics of radiotherapy, current indications, methods of delivery, precautions, and side effects of treatment and future directions. </a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/StehenCooper.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Advanced Mastoid Surgery</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/09/advanced-mastoid-surgery.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/09/advanced-mastoid-surgery.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01b7c6db790d970b</id>
|
||||
<published>2014-09-08T13:19:42+10:30</published>
|
||||
<updated>2014-09-08T13:19:42+10:30</updated>
|
||||
<summary>Professor Paul Fagan is one of Australia's most influential skull base surgeons. In this interview we discuss canal wall up and canal wall down surgery, the role of the facial nerve monitor as well as pre and post operative considerations. It is important to note that the bulk of Professor...</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/ProfPaulFaganAdvancedMastoidSurgery.mp3" target="_blank" title="">Professor Paul Fagan is one of Australia's most influential skull base surgeons. In this interview we discuss canal wall up and canal wall down surgery, the role of the facial nerve monitor as well as pre and post operative considerations. It is important to note that the bulk of Professor Fagan's practice has been extensive skull base approaches to CPA lesions as well as other pathologies of the medial temporal bone and so cannot be applied in the same way for more limited lateral temporal bone surgery.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/ProfPaulFaganAdvancedMastoidSurgery.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Cutaneous Melanoma- Everything You Need To Know</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/07/cutaneous-melanoma-everything-you-need-to-know.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/07/cutaneous-melanoma-everything-you-need-to-know.html" thr:count="1" thr:updated="2014-11-17T22:48:32+11:00" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01a3fd32f627970b</id>
|
||||
<published>2014-07-16T21:11:23+10:30</published>
|
||||
<updated>2014-07-16T21:12:18+10:30</updated>
|
||||
<summary>Dr Niall Jefferson interviews Associate Professor Gerald Fogarty a leading global expert on cutaneous melanoma</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/ProfGeraldFogartyFinal.mp3" target="_blank" title="">Dr Niall Jefferson interviews Associate Professor Gerald Fogarty a leading global expert on cutaneous melanoma</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/ProfGeraldFogartyFinal.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Understanding Pediatric Audiology </title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/04/understanding-pediatric-audiology-.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/04/understanding-pediatric-audiology-.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01a511964e76970c</id>
|
||||
<published>2014-04-04T12:09:31+11:00</published>
|
||||
<updated>2014-04-04T12:09:31+11:00</updated>
|
||||
<summary>Dr Jefferson talks with Melisha Sirisena, a paediatric audiologist about hearing loss in children. The discussion covers newborn hearing screening, the age-appropriate tests to assess hearing, as well as future directions for audiology. This podcast is a comprehensive discussion on hearing in the paediatric population.</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/MelishaSirisenaAudiology.mp3" target="_blank" title="">Dr Jefferson talks with Melisha Sirisena, a paediatric audiologist about hearing loss in children. The discussion covers newborn hearing screening, the age-appropriate tests to assess hearing, as well as future directions for audiology. This podcast is a comprehensive discussion on hearing in the paediatric population. </a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/MelishaSirisenaAudiology.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Glomus Tumours (Paragangliomas)</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/03/glomus-tumours-paragangliomas.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/03/glomus-tumours-paragangliomas.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01a73d97e3c1970d</id>
|
||||
<published>2014-03-24T10:30:16+11:00</published>
|
||||
<updated>2014-03-24T10:30:16+11:00</updated>
|
||||
<summary>Dr Sean Flannagan and Niall Jefferson talk Glomus tumours which are a vascular tumour that can affect the head and neck.</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/DrSeanFlannaganGlomusTumors.mp3" target="_blank" title="">Dr Sean Flannagan and Niall Jefferson talk Glomus tumours which are a vascular tumour that can affect the head and neck.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/DrSeanFlannaganGlomusTumors.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Pediatric Speech for Cleft Palate</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/03/pediatric-speech-for-cleft-palate.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/03/pediatric-speech-for-cleft-palate.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01a3fcd3c6a4970b</id>
|
||||
<published>2014-03-13T14:16:10+11:00</published>
|
||||
<updated>2014-03-13T14:16:10+11:00</updated>
|
||||
<summary>Dr. Jefferson and David Fitzsimmons discuss the latest in the assessment of Pediatric Speech for Cleft Palate</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/DavidFitzsimmonsSpeechCleftPalate.mp3" target="_blank" title="">Dr. Jefferson and David Fitzsimmons discuss the latest in the assessment of Pediatric Speech for Cleft Palate</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/DavidFitzsimmonsSpeechCleftPalate.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Pediatric Cochlear Implantation</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/03/pediatric-cochlear-implantation.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/03/pediatric-cochlear-implantation.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01a5117a865c970c</id>
|
||||
<published>2014-03-03T13:05:54+11:00</published>
|
||||
<updated>2014-03-03T13:05:54+11:00</updated>
|
||||
<summary>Dr. Jefferson and AProf. Cathrine Birman discuss Pediatric Cochlear Implants- newborn screening, intraoperative tips and pearls, post op considerations and the future of cochlear technology.</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/AprofCathyBirmanCochlearImplantation.mp3" target="_blank">Dr. Jefferson and AProf. Cathrine Birman discuss Pediatric Cochlear Implants- newborn screening, intraoperative tips and pearls, post op considerations and the future of cochlear technology.</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/AprofCathyBirmanCochlearImplantation.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Pediatric Drooling</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/02/pediatric-drooling.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/02/pediatric-drooling.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01a5116515a5970c</id>
|
||||
<published>2014-02-06T07:59:56+11:00</published>
|
||||
<updated>2014-02-06T07:59:56+11:00</updated>
|
||||
<summary>Dr. Piera Taylor: Pediatric Drooling</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/DrPieraTaylorPediatricDrooling.mp3" target="_blank" title="">Dr. Piera Taylor: Pediatric Drooling</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/DrPieraTaylorPediatricDrooling.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Otitis Externa</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/01/otitis-externa.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2014/01/otitis-externa.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01a3fc39b3af970b</id>
|
||||
<published>2014-01-15T14:03:52+11:00</published>
|
||||
<updated>2014-01-15T14:03:52+11:00</updated>
|
||||
<summary>Dr. Alex Saxby: Otitis Externa</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/SaxbyOtitisExterna/Saxby%20Otitis%20Externa.mp3" target="_blank" title="">Dr. Alex Saxby: Otitis Externa</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/SaxbyOtitisExterna/Saxby%20Otitis%20Externa.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Pediatric Laryngeal Cleft</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/12/pediatric-laryngeal-cleft.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/12/pediatric-laryngeal-cleft.html" thr:count="1" thr:updated="2014-02-20T11:43:31+11:00" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c019b0228be19970c</id>
|
||||
<published>2013-12-05T14:21:33+11:00</published>
|
||||
<updated>2013-12-05T14:21:33+11:00</updated>
|
||||
<summary>Prof Alan Cheng: Pediatric Laryngeal Cleft</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/ProfAlanChengLaryngealCleft.mp3" target="_blank" title="">Prof Alan Cheng: Pediatric Laryngeal Cleft</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/ProfAlanChengLaryngealCleft.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Douching the Nose</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/11/douching-the-nose.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/11/douching-the-nose.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c019b01659797970d</id>
|
||||
<published>2013-11-21T06:32:55+11:00</published>
|
||||
<updated>2013-11-21T06:32:55+11:00</updated>
|
||||
<summary>Dr. Larry Kalish: Douching the Nose</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="https://archive.org/download/ENTExpertOpinion/DrLarryKalishDouchingTheNose.mp3" target="_blank" title="">Dr. Larry Kalish: Douching the Nose</a></p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/DrLarryKalishDouchingTheNose.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Scapula Tip Flap</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/11/scapula-tip-flap.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/11/scapula-tip-flap.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c019b00b6f681970d</id>
|
||||
<published>2013-11-06T13:42:41+11:00</published>
|
||||
<updated>2013-11-06T13:42:41+11:00</updated>
|
||||
<summary>Dr. Sam Dowthwaite: Scapula Tip Flap</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="https://archive.org/download/ENTExpertOpinion/DowthwaiteScapulaTipFlap.mp3" target="_blank">Dr. Sam Dowthwaite: Scapula Tip Flap</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/DowthwaiteScapulaTipFlap.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Oropharyngeal Reconstruction</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/10/oropharyngeal-reconstruction.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/10/oropharyngeal-reconstruction.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c019b00398b13970c</id>
|
||||
<published>2013-10-23T11:41:52+11:00</published>
|
||||
<updated>2013-10-23T11:41:52+11:00</updated>
|
||||
<summary>Dr Sam Dowthwaite: Oropharyngeal Reconstruction</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="https://archive.org/download/ENTExpertOpinion/DrSamDowthwaiteOropharyngealReconstruction.mp3" target="_blank">Dr Sam Dowthwaite: Oropharyngeal Reconstruction </a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/DrSamDowthwaiteOropharyngealReconstruction.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Expert Opinion: Who We Are.</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/10/hello-is-it-me-youre-looking-for.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/10/hello-is-it-me-youre-looking-for.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c019affe0a760970c</id>
|
||||
<published>2013-10-09T20:48:12+11:00</published>
|
||||
<updated>2013-10-09T22:35:58+11:00</updated>
|
||||
<summary>Dr. Niall Jefferson: Who What Where Why</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="https://archive.org/download/ENTExpertOpinion/EeoPodcastIntroToUsOct92103.mp3" target="_blank">Dr. Niall Jefferson: Who What Where Why</a><br /></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/EeoPodcastIntroToUsOct92103.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Allergic Rhinitis</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/09/allergic-rhinitis.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/09/allergic-rhinitis.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c019aff936e39970b</id>
|
||||
<published>2013-09-24T21:37:25+10:30</published>
|
||||
<updated>2013-09-24T21:37:25+10:30</updated>
|
||||
<summary>AProf Richard Harvey: Allergic Rhinitis</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="https://archive.org/download/ENTExpertOpinion/AprofRichardHarveyAllergicRhinitis.mp3" target="_blank">AProf Richard Harvey: Allergic Rhinitis</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/AprofRichardHarveyAllergicRhinitis.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Cystic Fibrosis and CRS- Assessment and Implications for Management</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/09/cystic-fibrosis-and-crs-assessment-and-implications-for-management.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/09/cystic-fibrosis-and-crs-assessment-and-implications-for-management.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c019aff4e93d0970b</id>
|
||||
<published>2013-09-11T07:10:00+10:30</published>
|
||||
<updated>2013-09-11T07:10:00+10:30</updated>
|
||||
<summary>AProf Bradford Woodworth: Cystic Fibrosis and CRS- Assessment and Implications for Management</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="https://archive.org/download/ENTExpertOpinion/AprofBradwoodworthCfAndCrs.mp3" target="_blank">AProf Bradford Woodworth: Cystic Fibrosis and CRS- Assessment and Implications for Management</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/AprofBradwoodworthCfAndCrs.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>The Role of CT Imaging In CRS</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/08/aprof-david-conley-ct-scans-in-chronic-rhinosinusitis.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/08/aprof-david-conley-ct-scans-in-chronic-rhinosinusitis.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c019aff0b53f4970d</id>
|
||||
<published>2013-08-28T13:59:53+10:30</published>
|
||||
<updated>2013-08-28T21:31:11+10:30</updated>
|
||||
<summary>AProf David Conley: CT Scans in Chronic Rhinosinusitis</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/AprofDavidConleyCtscan.mp3" target="_blank">AProf David Conley: CT Scans in Chronic Rhinosinusitis</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/AprofDavidConleyCtscan.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Nasal Steroids</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/08/dr-larry-kalish-nasal-steroids.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/08/dr-larry-kalish-nasal-steroids.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c0192ac879f2b970d</id>
|
||||
<published>2013-08-14T15:18:49+10:30</published>
|
||||
<updated>2013-08-14T15:19:37+10:30</updated>
|
||||
<summary>Dr. Larry Kalish: Nasal Steroids</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/Dr.LarryKalishNasalSteroids.mp3" target="_blank">Dr. Larry Kalish: Nasal Steroids</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/Dr.LarryKalishNasalSteroids.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Spontaneous CSF Leak- Presentation and Management</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/08/spontaneous-csf-leak-presentation-and-management.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/08/spontaneous-csf-leak-presentation-and-management.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c0191048337e9970c</id>
|
||||
<published>2013-08-01T15:17:02+10:30</published>
|
||||
<updated>2013-08-01T15:17:02+10:30</updated>
|
||||
<summary>Prof. Rodney Schlosser: Spontaneous CSF Leak- Presentation and Management</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="https://archive.org/download/ENTExpertOpinion/Prof.RodneySchlosser-SpontaneousCsfLeak.mp3" target="_blank">Prof. Rodney Schlosser: Spontaneous CSF Leak- Presentation and Management</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="https://archive.org/download/ENTExpertOpinion/Prof.RodneySchlosser-SpontaneousCsfLeak.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Pediatric Tonsils and Adenoids</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/07/pediatric-tonsils-and-adenoids.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/07/pediatric-tonsils-and-adenoids.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01901e4fb060970b</id>
|
||||
<published>2013-07-18T05:47:12+10:30</published>
|
||||
<updated>2013-07-18T05:47:12+10:30</updated>
|
||||
<summary>Dr. Shyan Vijayasekaran: Pediatric Tonsils and Adenoids</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/DrShyanVijayasekaranPedTanda.mp3" target="_blank">Dr. Shyan Vijayasekaran: Pediatric Tonsils and Adenoids</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/DrShyanVijayasekaranPedTanda.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Anesthesia in Pediatric ENT</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/07/anesthesia-in-pediatric-ent.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/07/anesthesia-in-pediatric-ent.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c0191040f9005970c</id>
|
||||
<published>2013-07-03T19:57:46+10:30</published>
|
||||
<updated>2013-07-03T19:57:46+10:30</updated>
|
||||
<summary>Dr. Nick Hogan: Anesthesia in Pediatric ENT</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/DrNickHoganPediatricAnaestheticsInRelationToEnt.mp3" target="_self">Dr. Nick Hogan: Anesthesia in Pediatric ENT</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/DrNickHoganPediatricAnaestheticsInRelationToEnt.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Surgical Management of Adult Sleep Apnea</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/06/surgical-management-of-adult-sleep-apnea.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/06/surgical-management-of-adult-sleep-apnea.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c0191037a2610970c</id>
|
||||
<published>2013-06-18T20:36:47+10:30</published>
|
||||
<updated>2013-06-18T20:36:47+10:30</updated>
|
||||
<summary>AProf Stuart MacKay: Surgical Management of Adult Sleep Apnea.</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/AprofStuartMackaySurgicalManagementOfAdultSleepApnea.mp3" target="_self">AProf Stuart MacKay: Surgical Management of Adult Sleep Apnea.</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/AprofStuartMackaySurgicalManagementOfAdultSleepApnea.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Assessment of Adult Sleep Apnea</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/06/assessment-of-adult-sleep-apnea.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/06/assessment-of-adult-sleep-apnea.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c0192aac80fec970d</id>
|
||||
<published>2013-06-06T10:44:45+10:30</published>
|
||||
<updated>2013-06-06T10:44:45+10:30</updated>
|
||||
<summary>AProf Stuart MacKay: Assessment of Adult Sleep Apnea</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><p><a href="http://archive.org/download/ENTExpertOpinion/AprofStuartMackayAssesOfAdultSleepApnea.mp3" target="_self">AProf Stuart MacKay: Assessment of Adult Sleep Apnea</a></p>
|
||||
<p> </p></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/AprofStuartMackayAssesOfAdultSleepApnea.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Otosclerosis and Stapedectomy</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/05/otosclerosis-and-stapedectomy.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/05/otosclerosis-and-stapedectomy.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01901c78713c970b</id>
|
||||
<published>2013-05-23T13:37:25+10:30</published>
|
||||
<updated>2013-05-23T13:37:25+10:30</updated>
|
||||
<summary>Dr. David Pohl: Otosclerosis and Stapedectomy</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/DavidPohlStapedectomy.mp3" target="_self">Dr. David Pohl: Otosclerosis and Stapedectomy</a></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/DavidPohlStapedectomy.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>The Facial Nerve</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/05/the-facial-nerve.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/05/the-facial-nerve.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c01901be3ad0e970b</id>
|
||||
<published>2013-05-07T13:10:52+10:30</published>
|
||||
<updated>2013-05-07T20:13:45+10:30</updated>
|
||||
<summary>AProf Glen Croxson: Facial Nerve</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/AprofGlenCroxsonFacialNerve.mp3" target="_self">AProf Glen Croxson: Facial Nerve</a><br title="AProf Glen Croxson: Facial Nerve" /></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/AprofGlenCroxsonFacialNerve.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>CRS</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/04/crs.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/04/crs.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c017d427d23dc970c</id>
|
||||
<published>2013-04-03T16:32:02+11:00</published>
|
||||
<updated>2013-04-05T13:11:15+11:00</updated>
|
||||
<summary>AProf Richard Harvey: CRS</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/AProf%20Richard%20Harvey%20CRS.mp3" target="_self">AProf Richard Harvey: CRS</a><br /></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/AProf%20Richard%20Harvey%20CRS.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Pediatric Vocal Cord Palsy</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/03/pediatric-vocal-cord-palsy.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/03/pediatric-vocal-cord-palsy.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c017c3816ee80970b</id>
|
||||
<published>2013-03-25T21:24:22+11:00</published>
|
||||
<updated>2013-04-03T20:50:42+11:00</updated>
|
||||
<summary>Dr. Shyan Vijayasekaran: Pediatric Vocal Cord Palsy</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/Dr%20Shyan%20Vijayasekaran%20Pediatric%20Vocal%20Cord%20Palsy.mp3" target="_self">Dr. Shyan Vijayasekaran: Pediatric Vocal Cord Palsy </a><br class="inline-player" /></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/Dr%20Shyan%20Vijayasekaran%20Pediatric%20Vocal%20Cord%20Palsy.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Airway Balloon Dilatation</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/03/airway-balloon-dilatation.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/03/airway-balloon-dilatation.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c017d42461886970c</id>
|
||||
<published>2013-03-25T21:22:03+11:00</published>
|
||||
<updated>2013-04-03T20:51:39+11:00</updated>
|
||||
<summary>Prof Mike Rutter: Balloon Dilatation</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/Prof%20Mike%20Rutter%20Balloon%20Dilatation.mp3" target="_self">Prof Mike Rutter: Balloon Dilatation </a><br /></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/Prof%20Mike%20Rutter%20Balloon%20Dilatation.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Airway Reconstruction</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/03/airway-reconstruction.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/03/airway-reconstruction.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c017d4243a1b7970c</id>
|
||||
<published>2013-03-25T13:14:29+11:00</published>
|
||||
<updated>2013-04-03T20:52:19+11:00</updated>
|
||||
<summary>Dr Shyan Vijayasekaran: Airway Reconstruction</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
<category term="Science" />
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/Dr%20Shyan%20Vijayasekaran%20Airway%20Reconstruction.mp3" target="_self">Dr Shyan Vijayasekaran: Airway Reconstruction</a><br />
|
||||
<br /></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/Dr%20Shyan%20Vijayasekaran%20Airway%20Reconstruction.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Fungal Ball</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/03/fungal-ball-with-aprof-richard-harvey.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2013/03/fungal-ball-with-aprof-richard-harvey.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c017c37af8296970b</id>
|
||||
<published>2013-03-15T11:30:13+11:00</published>
|
||||
<updated>2013-04-03T20:52:57+11:00</updated>
|
||||
<summary>AProf Richard Harvey: Fungal Ball</summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a href="http://archive.org/download/ENTExpertOpinion/Richard%20Harvey%20Fungal%20Ball.mp3" target="_self">AProf Richard Harvey: Fungal Ball</a><br /></div>
|
||||
</content>
|
||||
|
||||
|
||||
<link rel="enclosure" type="audio/mpeg" href="http://archive.org/download/ENTExpertOpinion/Richard%20Harvey%20Fungal%20Ball.mp3" />
|
||||
|
||||
</entry>
|
||||
<entry>
|
||||
<title>Welcome to ENT Expert Opinion audio podcast</title>
|
||||
<link rel="alternate" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2012/12/welcome-to-your-new-typepad-blog.html" />
|
||||
<link rel="replies" type="text/html" href="http://expertopinionent.typepad.com/my-blog/2012/12/welcome-to-your-new-typepad-blog.html" thr:count="0" />
|
||||
<id>tag:typepad.com,2003:post-6a017d3e79c482970c017ee5ee7964970d</id>
|
||||
<published>2012-12-05T19:51:24+11:00</published>
|
||||
<updated>2013-03-15T11:25:45+11:00</updated>
|
||||
<summary></summary>
|
||||
<author>
|
||||
<name>ENTexpertopinion</name>
|
||||
</author>
|
||||
|
||||
|
||||
<content type="xhtml" xml:lang="en-US" xml:base="http://expertopinionent.typepad.com/my-blog/">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"><a class="asset-img-link" href="http://expertopinionent.typepad.com/.a/6a017d3e79c482970c017eea56dccb970d-pi" style="display: inline;"><img alt="Ent1_copy" border="0" class="asset asset-image at-xid-6a017d3e79c482970c017eea56dccb970d image-full" src="http://expertopinionent.typepad.com/.a/6a017d3e79c482970c017eea56dccb970d-800wi" title="Ent1_copy" /></a><br /></div>
|
||||
</content>
|
||||
|
||||
|
||||
|
||||
</entry>
|
||||
|
||||
</feed>
|
||||
|
||||
<!-- ph=1 -->
|
372
Parser/Tests/ParserTests/Resources/furbo.html
Executable file
372
Parser/Tests/ParserTests/Resources/furbo.html
Executable file
@ -0,0 +1,372 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- <meta name="viewport" content="width=device-width">-->
|
||||
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Language" content="en-us" />
|
||||
|
||||
<meta name="description" content="furbo.org is Craig Hockenberry's place to write for the web. He makes app and runs websites." />
|
||||
|
||||
<meta name="keywords" content="chock, woot" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="NOODP">
|
||||
|
||||
<title>furbo.org by Craig Hockenberry</title>
|
||||
|
||||
<link rel="alternate" type="application/rss+xml" title="Iconfactory News Feed" href="http://furbo.org/feed/" />
|
||||
|
||||
<link href="http://furbo.org/wp-content/themes/furbo/reset.css?ver=20150205" media="screen" rel="Stylesheet" type="text/css" />
|
||||
<link href="http://furbo.org/wp-content/themes/furbo/style.css?ver=20150206" media="screen" rel="Stylesheet" type="text/css" />
|
||||
|
||||
<link rel="shortcut icon" href="http://furbo.org/favicon.ico" />
|
||||
|
||||
<script type="text/javascript" src="http://furbo.org/wp-content/themes/furbo/scripts/minified.js"></script>
|
||||
<script type="text/javascript" src="http://furbo.org/wp-content/themes/furbo/scripts/furbo.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="home blog">
|
||||
<div id="page" class="hfeed site">
|
||||
<a class="skip-link screen-reader-text" href="#content">Skip to content</a>
|
||||
|
||||
<header id="masthead" class="site-header" role="banner">
|
||||
<h1 class="site-title screen-reader-text">
|
||||
<a href="http://furbo.org/" rel="home">
|
||||
furbo.org </a>
|
||||
</h1>
|
||||
|
||||
<div id="logo">
|
||||
<a href="http://furbo.org/" rel="home">
|
||||
furbo.org </a>
|
||||
</div>
|
||||
<div id="collapsed-logo">
|
||||
<a href="http://furbo.org/" rel="home">
|
||||
furbo.org </a>
|
||||
</div>
|
||||
<div id="controls">
|
||||
<p id="control-info" onclick="toggleSidebar();">info</p>
|
||||
</div>
|
||||
</header><!-- .site-header -->
|
||||
|
||||
<div id="background">
|
||||
</div>
|
||||
|
||||
<div id="sidebar" class="sidebar">
|
||||
<div id="secondary" class="secondary">
|
||||
|
||||
<aside id="about-me" class="widget widget_text">
|
||||
<h2 class="widget-title">About Me</h2>
|
||||
<div class="textwidget">
|
||||
<p>
|
||||
I'm Craig Hockenberry and this is where I write for the web. I make <a href="http://iconfactoryapps.com">apps</a> and run <a href="http://iconfactory.com">websites</a>.
|
||||
</p>
|
||||
<p>
|
||||
You can learn <a href="/about">more about me</a>, view <a href="/resume">my résumé</a>, or <a href="http://twitter.com/chockenberry">follow me</a>.
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div id="widget-area" class="widget-area" role="complementary">
|
||||
<aside id="search-3" class="widget widget_search"><form role="search" method="get" class="search-form" action="http://furbo.org/">
|
||||
<label>
|
||||
<span class="screen-reader-text">Search for:</span>
|
||||
<input type="search" class="search-field" placeholder="Search …" value="" name="s" title="Search for:" />
|
||||
</label>
|
||||
<input type="submit" class="search-submit" value="Search" />
|
||||
</form></aside> <aside id="recent-writing-3" class="widget widget_recent_entries"> <h2 class="widget-title">Recent Writing</h2> <ul>
|
||||
<li>
|
||||
<a href="http://furbo.org/2016/02/20/the-forensic-shit-show/">The Forensic Shit Show</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/12/29/strapped-in/">Strapped In</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/12/28/the-new-ipod/">The New iPod</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/11/04/a-responsive-factory/">A Responsive Factory</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/10/07/ipulse-3/">iPulse 3</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/07/23/code-signing-in-el-capitan/">Code Signing in El Capitan</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/07/22/half-assed/">Half-Assed</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/07/14/a-watch-water-and-workouts/">A Watch, Water and Workouts</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/07/09/i-left-my-system-fonts-in-san-francisco/">I Left My System Fonts in San Francisco</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://furbo.org/2015/06/15/bug-writing-season/">Bug Writing Season</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside><aside id="history-2" class="widget widget_archive"><h2 class="widget-title">Archives</h2> <ul>
|
||||
<li><a href='http://furbo.org/2016/'>2016</a></li>
|
||||
<li><a href='http://furbo.org/2015/'>2015</a></li>
|
||||
<li><a href='http://furbo.org/2014/'>2014</a></li>
|
||||
<li><a href='http://furbo.org/2013/'>2013</a></li>
|
||||
<li><a href='http://furbo.org/2012/'>2012</a></li>
|
||||
<li><a href='http://furbo.org/2011/'>2011</a></li>
|
||||
<li><a href='http://furbo.org/2010/'>2010</a></li>
|
||||
<li><a href='http://furbo.org/2009/'>2009</a></li>
|
||||
<li><a href='http://furbo.org/2008/'>2008</a></li>
|
||||
<li><a href='http://furbo.org/2007/'>2007</a></li>
|
||||
</ul>
|
||||
</aside> </div><!-- .widget-area -->
|
||||
|
||||
<aside id="other" class="widget widget_text">
|
||||
<div class="textwidget">
|
||||
<p>
|
||||
<a href="http://furbo.org/feed">RSS</a>
|
||||
</p>
|
||||
<p>
|
||||
© 2016 Craig Hockenberry
|
||||
</p>
|
||||
<p>
|
||||
All rights reserved
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
</div><!-- .secondary -->
|
||||
|
||||
</div><!-- .sidebar -->
|
||||
|
||||
<div id="content" class="site-content">
|
||||
|
||||
<div id="primary" class="content-area">
|
||||
<main id="main" class="site-main" role="main">
|
||||
|
||||
<!-- index is_home = 1 is_front_page = 1-->
|
||||
|
||||
|
||||
|
||||
|
||||
<article id="post-1984" class="post-1984 post type-post status-publish format-standard hentry category-miscellaneous category-opinion">
|
||||
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2016/02/20/the-forensic-shit-show/" rel="bookmark">The Forensic Shit Show</a></h2> </div><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>It turns out someone at the FBI advised another law enforcement officer in San Bernardino to <a href="https://twitter.com/countywire/status/700887823482630144" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">reset the iPhone</a> that the government wants Apple to unlock.</p>
|
||||
<p>This is just another episode in a complete forensic shit show.</p>
|
||||
<p>Remember, this is the same case where <a href="http://www.nbcnews.com/storyline/san-bernardino-shooting/go-inside-home-syed-farook-tashfeen-malik-n474601" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.nbcnews.com');">the media was allowed to roam freely through a crime scene</a>. One of the photos in that gallery <a href="http://media4.s-nbcnews.com/j/msnbc/components/photo/_new/ss-151204-farook-apartment-jsw-13.nbcnews-ux-1024-900.jpg" onclick="javascript:pageTracker._trackPageview('/outbound/article/media4.s-nbcnews.com');">shows a computer without an Ethernet connection on the wall</a> (the age of the apartment also suggests that there would be no wired Internet.)</p>
|
||||
<p>What are the chances that there was a wireless network in that apartment? What are the chances that there are IP logs on that router? Or maybe some kind of data backed up to a disk on the router? Here’s another wild guess: maybe that router was used to <a href="https://twitter.com/kennwhite/status/700865678274752512" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">connect to an online backup service</a>.</p>
|
||||
<p>Yep, someone did the equivalent of a “restore factory defaults” on a device under active investigation.</p>
|
||||
<p>What we’re seeing here is law enforcement’s complete lack of understanding of how digital devices store and transmit data. This new evidence is much more intricate than smoking guns or blood splatters. The important stuff is what you don’t see: it’s a hard problem where the people dealing with it are untrained. Shit, I work in this business and trying to decipher what’s going on makes my head spin.</p>
|
||||
<p>Yet law enforcement is asking Apple to not only provide data, but also to create a forensic <strong>instrument</strong> that allows them to extract information from any device. And by its very nature, <a href="http://www.zdziarski.com/blog/?p=5645" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.zdziarski.com');">this tool would be made widely available throughout the forensic and law enforcement community</a>.</p>
|
||||
<p>Basically, the government is asking Apple to hand over a golden key that can defeat the security of <strong>any</strong> device to folks that can’t even secure a wireless network. Worse, this whole process is being overseen by politicians that think the problem is <a href="http://thehill.com/policy/cybersecurity/262658-feinstein-vows-to-offer-encryption-piercing-bill" onclick="javascript:pageTracker._trackPageview('/outbound/article/thehill.com');">predators getting access to their grandkid’s Playstation</a>.</p>
|
||||
<p>This is why the entire tech community is saying “No fucking way.”</p>
|
||||
<p><strong>Updated February 21st, 2016:</strong> Several people have commented about my use of “restore factory defaults” in the post above. My intention was figurative, not literal.</p>
|
||||
<p>The folks involved with the investigation were pressing buttons without understanding the consequences of their actions. To me, it feels like a “reboot to fix” approach. The password reset did not damage any data, it just made automatic backups stop working because iCloud information on the device needed to be updated, and that can’t be done without a passcode.</p>
|
||||
<p>Others have reminded me that the FBI had cleared the crime scene. That’s true, but since the Wi-Fi equipment was not collected as evidence, it still shows that the investigators were out of their league. In an electronic investigation, a router is a key piece of the puzzle.</p>
|
||||
<p>Both of these things are details in a bigger picture: the FBI wants to hold the private keys to a public key encryption system that affects the privacy of hundreds of millions people. If they can’t get the details of an online backup service right, how the hell do we expect them to guard a back door?</p>
|
||||
<p>There’s also <a href="http://daringfireball.net/2016/02/san_bernardino_password_reset" onclick="javascript:pageTracker._trackPageview('/outbound/article/daringfireball.net');">a possibility that the iCloud password reset was intentional</a>. If this is the case, we have a government that is extorting Apple by essentially planting evidence. Imagine what they could do with a private key.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<div class="entry-footer">
|
||||
Written on February 20th, 2016 for <a href="http://furbo.org/category/miscellaneous/" title="View all posts in Miscellaneous" rel="category tag">Miscellaneous</a>, <a href="http://furbo.org/category/opinion/" title="View all posts in Opinion" rel="category tag">Opinion</a> </div><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-## -->
|
||||
|
||||
<article id="post-1953" class="post-1953 post type-post status-publish format-standard hentry category-miscellaneous category-observation">
|
||||
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2015/12/29/strapped-in/" rel="bookmark">Strapped In</a></h2> </div><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>A lot has happened since I purchased <a href="http://www.apple.com/shop/buy-watch/apple-watch-sport/42mm-silver-aluminum-case-with-blue-sport-band?product=MLC52LL/A&step=detail" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.apple.com');">my Apple Watch</a> on April 10th, 2015. One unexpected aspect to owning this device is my fascination with watch bands:</p>
|
||||
<figure>
|
||||
<a href="http://furbo.org/wp-content/uploads/2015/12/AppleWatchBands.jpeg" ><img src="http://furbo.org/wp-content/uploads/2015/12/AppleWatchBands.jpeg" alt="Apple Watch Bands" width="1120" height="862" class="aligncenter size-full wp-image-1954" /></a></p>
|
||||
<figcaption>My current collection of watch bands. And no, <a href="http://furbo.org/2015/05/22/apple-watch-ergonomics/" >the watch isn’t upside down</a>.</figcaption>
|
||||
</figure>
|
||||
<p>From left to right, in order of date purchased:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://www.apple.com/shop/buy-watch/apple-watch-sport/42mm-silver-aluminum-case-with-blue-sport-band?product=MLC52LL/A&step=detail" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.apple.com');">Sport Model with Blue Band</a> ($400) – I picked the aluminum watch with a blue band because I knew it would be spending a lot of time in the water. To date, I’ve <a href="http://furbo.org/2015/07/14/a-watch-water-and-workouts/" >used it 110 times for over 35 hours of swimming</a>.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.apple.com/shop/product/MJ5F2/42mm-milanese-loop" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.apple.com');">Milanese Loop</a> ($150) – I was intrigued by this band as soon as I saw it during the video at the product announcement. I love how the metal feels a lot like fabric. It also dresses up the utilitarian Sport model so it doesn’t look out of place when I’m someplace nice.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.clockworksynergy.com/shop/tech-straps/straps-for-apple-watch/2-piece-heavy-nato-straps/black-grey-two-piece-heavy-nato-nylon-watch-strap-fits-apple-watch/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.clockworksynergy.com');">Black & Silver Nylon</a> ($30) – This NATO-style band from Clockwork Synergy popped up on my <a href="https://twitter.com/capttaco/status/625339467080040448" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">Twitter timeline</a> thanks to my pal <a href="https://twitter.com/capttaco" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">Rob Rhyne</a>. I love that it dresses up the watch <strong>and</strong> is waterproof.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.apple.com/shop/product/MLDJ2/42mm-productred-band" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.apple.com');">Red Sport</a> ($50) – When Apple started selling additional colors for the sport bands, getting one in my favorite color was a no-brainer. I also like that a little of my purchase goes to a worthwhile charity.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.amazon.com/gp/product/B00JP09C7I/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00JP09C7I&linkCode=as2&tag=furboorg-20&linkId=4LVZVTJFJG5OY4UU" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');">Orange Silicone</a> ($20) – This band by MoKo was <a href="https://twitter.com/mrgan/status/643847960850247680" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">another recommendation</a> from Twitter by <a href="https://twitter.com/mrgan" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">Neven Mrgan</a>. To me, the most interesting thing about this band is that it shows why Apple went with <a href="https://en.wikipedia.org/wiki/Fluoroelastomer" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"> fluoroelastomer</a> for their bands: it’s stiffer and “breathes” better than silicone.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.lucrin.com/straps-and-watches/watch-straps/apple-watch/apple-band-42mm-elegance.htm" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.lucrin.com');">Black Goat Leather</a> ($200) – The leather bands from Apple are nice, but I prefer the classic look of this one from Lucrin. The company also offers a huge range of colors: my wife loves the dark green one I gave at Christmas.
|
||||
</li>
|
||||
</ul>
|
||||
<p>In this survey of my growing collection, there’s an interesting datapoint: the value of these bands ($450) exceeds the cost of the watch itself ($400).</p>
|
||||
<p>If Apple decides to change the interchange mechanism in some future version of the watch, I will have very little desire to upgrade. As I continue to “work in” my leather band, I hope I’ll be using them for a long time.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<div class="entry-footer">
|
||||
Written on December 29th, 2015 for <a href="http://furbo.org/category/miscellaneous/" title="View all posts in Miscellaneous" rel="category tag">Miscellaneous</a>, <a href="http://furbo.org/category/observation/" title="View all posts in Observation" rel="category tag">Observation</a> </div><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-## -->
|
||||
|
||||
<article id="post-1942" class="post-1942 post type-post status-publish format-standard hentry category-business category-observation">
|
||||
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2015/12/28/the-new-ipod/" rel="bookmark">The New iPod</a></h2> </div><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Something tells me that there were a lot of Apple Watches under the tree this year:</p>
|
||||
<p><a href="http://furbo.org/wp-content/uploads/2015/09/ClickerDownloads.jpeg" ><img src="http://furbo.org/wp-content/uploads/2015/09/ClickerDownloads.jpeg" alt="Clicker Downloads for December 2015" width="560" height="966" class="aligncenter size-full wp-image-1948" /></a></p>
|
||||
<p>That graph shows the last month of downloads for my free <a href="https://itunes.apple.com/us/app/clicker-count-anything/id1043951998?ls=1&mt=8&uo=4&at=10l4G7&ct=FURBO" onclick="javascript:pageTracker._trackPageview('/outbound/article/itunes.apple.com');">Clicker</a> app for watchOS. Since this app does nothing on an iPhone or iPad, the only reason to get it is if you have a new watch.</p>
|
||||
<p>Many of us, myself included, originally thought of the Apple Watch as a device in and of itself. But the more I use the computer on my wrist, the more it feels like a satellite to the computer that’s sitting in my pocket.</p>
|
||||
<p>Accessories have always made great gifts for folks who love their computers. Giving the watch as a gift is a perfect option for someone who’s always playing around with the apps on their iPhone. Just like the iPod was an ideal match for someone who loved playing music on their desktop computer.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<div class="entry-footer">
|
||||
Written on December 28th, 2015 for <a href="http://furbo.org/category/business/" title="View all posts in Business" rel="category tag">Business</a>, <a href="http://furbo.org/category/observation/" title="View all posts in Observation" rel="category tag">Observation</a> </div><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-## -->
|
||||
|
||||
<article id="post-1932" class="post-1932 post type-post status-publish format-link hentry category-miscellaneous">
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2015/11/19/clicker-1-1/" title="Permalink" class="permalink">►</a> <a href="https://itunes.apple.com/us/app/clicker-count-anything/id1043951998?ls=1&mt=8&uo=4&at=10l4G7&ct=FURBO">Clicker 1.1</a></h2> </div>
|
||||
<!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>The <a href="https://itunes.apple.com/us/app/clicker-count-anything/id1043951998?ls=1&mt=8&uo=4&at=10l4G7&ct=FURBO" onclick="javascript:pageTracker._trackPageview('/outbound/article/itunes.apple.com');">Make John Gruber Happy Edition™</a> of Clicker is now available. <a href="http://daringfireball.net/linked/2015/10/14/clicker" onclick="javascript:pageTracker._trackPageview('/outbound/article/daringfireball.net');">Finally.</a></p>
|
||||
</div>
|
||||
<!-- .entry-content -->
|
||||
|
||||
|
||||
|
||||
</article><!-- #post-## -->
|
||||
|
||||
<article id="post-1920" class="post-1920 post type-post status-publish format-standard hentry category-design category-development">
|
||||
|
||||
<div class="entry-header">
|
||||
<h2 class="entry-title"><a href="http://furbo.org/2015/11/04/a-responsive-factory/" rel="bookmark">A Responsive Factory</a></h2> </div><!-- .entry-header -->
|
||||
|
||||
<div class="entry-content">
|
||||
<p>Back in May 2014, we introduced a new <a href="http://iconfactory.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/iconfactory.com');">Iconfactory home page</a>. One of the main design goals for that site was to make the layout a <a href="https://en.wikipedia.org/wiki/Responsive_web_design" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">responsive web design</a>: the same site looked great whether you were looking at it on a desktop PC or an iPhone. Reading <a href="http://abookapart.com/products/responsive-web-design" onclick="javascript:pageTracker._trackPageview('/outbound/article/abookapart.com');">Ethan Marcotte’s book</a> was a revelation.</p>
|
||||
<p>Of course, that site was just a beginning. <a href="http://design.iconfactory.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/design.iconfactory.com');">We</a> <a href="http://iconfactoryapps.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/iconfactoryapps.com');">run</a> <a href="http://blog.iconfactory.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/blog.iconfactory.com');">a</a> <a href="http://flareapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/flareapp.com');">lot</a> <a href="http://twitterrific.com/ios" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitterrific.com');">of</a> <a href="http://xscopeapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/xscopeapp.com');">web</a> <a href="http://ipulseapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/ipulseapp.com');">sites</a> (<a href="http://appdevmanual.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/appdevmanual.com');">including</a> <a href="http://astronutapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/astronutapp.com');">some</a> <a href="http://chameleonproject.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/chameleonproject.org');">you’ve</a> <a href="http://dine-o-matic.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/dine-o-matic.com');">probably</a> <a href="http://frenzic.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/frenzic.com');">never</a> <a href="http://pickintimeapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/pickintimeapp.com');">heard</a> <a href="http://pixelpalooza.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/pixelpalooza.com');">of</a> <a href="http://takefiveapp.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/takefiveapp.com');">before</a>). Clearly we had to pick our responsive battles.</p>
|
||||
<p>We started with an <a href="http://blog.iconfactory.com/2015/01/welcome-to-the-new-iconfactory-blog/" onclick="javascript:pageTracker._trackPageview('/outbound/article/blog.iconfactory.com');">update to our blog</a> in January 2015. In October, we updated our <a href="http://iconfactoryapps.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/iconfactoryapps.com');">iOS and OS X app catalog</a>. And yesterday we launched a <a href="http://design.iconfactory.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/design.iconfactory.com');">responsive design portfolio</a>.</p>
|
||||
<p>A year and a half after our first responsive design, we’ve hit a milestone. All of the sites listed in the Iconfactory’s red navigation bar are responsive designs and will display correctly on any device. Woo hoo!</p>
|
||||
<p>Along the way, we cleaned up some of our branding elements and worked toward a more consistent experience across all the sites. Check out the post at the Iconfactory about <a href="http://blog.iconfactory.com/2015/11/the-new-favicon/" onclick="javascript:pageTracker._trackPageview('/outbound/article/blog.iconfactory.com');">the new SVG icons in Safari</a> to see what that’s all about.</p>
|
||||
<p>It’s clear we’re at a point in time where the vast assortment of screens is daunting. If you haven’t thought about how your site works on this wide variety of devices, <a href="http://alistapart.com/article/responsive-web-design" onclick="javascript:pageTracker._trackPageview('/outbound/article/alistapart.com');">now is a great time to start</a>.</p>
|
||||
</div><!-- .entry-content -->
|
||||
|
||||
<div class="entry-footer">
|
||||
Written on November 4th, 2015 for <a href="http://furbo.org/category/design/" title="View all posts in Design" rel="category tag">Design</a>, <a href="http://furbo.org/category/development/" title="View all posts in Development" rel="category tag">Development</a> </div><!-- .entry-footer -->
|
||||
|
||||
</article><!-- #post-## -->
|
||||
<div id="post-navigation">
|
||||
<div class="older"><a href="http://furbo.org/page/2/" >Older</a></div>
|
||||
</div>
|
||||
|
||||
</main><!-- .site-main -->
|
||||
</div><!-- .content-area -->
|
||||
|
||||
|
||||
</div><!-- .site-content -->
|
||||
|
||||
<footer id="footer" class="site-footer" role="contentinfo">
|
||||
<div class="features">
|
||||
<div class="feature">
|
||||
<script type="text/javascript">
|
||||
var content = [];
|
||||
|
||||
content.push('\
|
||||
<a href="http://design.iconfactory.com">\
|
||||
<img src="/footer/designsite.png" width="198" height="101" />\
|
||||
</a>\
|
||||
<h5>Do you write awesome code?</h5>\
|
||||
<p>Of course, you do!</p>\
|
||||
<p>But if you\'re like me, you struggle with design and opening Photoshop makes you break out in a cold sweat.</p>\
|
||||
<p>\
|
||||
<a href="http://design.iconfactory.com">\
|
||||
The designers that help me can also help you…</a>\
|
||||
</p>\
|
||||
');
|
||||
|
||||
content.push('\
|
||||
<a href="http://xscopeapp.com">\
|
||||
<img src="/footer/xscope4.png" width="198" height="101" />\
|
||||
</a>\
|
||||
<h5>I made this app because I needed it.</h5>\
|
||||
<p>\
|
||||
If you do any kind of Web, iOS or Mac development, you need these \
|
||||
<a href="http://xscopeapp.com/guide">\
|
||||
awesome tools</a>. I did.\
|
||||
</p>\
|
||||
<p>\
|
||||
After a few days with xScope\'s \
|
||||
<a href="http://xscopeapp.com">\
|
||||
FREE trial</a>, you\'ll wonder how you ever did work without it. Trust me.\
|
||||
</p>\
|
||||
');
|
||||
|
||||
content.push('\
|
||||
<a href="http://furbo.org/2014/09/03/the-terminal/">\
|
||||
<img src="/footer/terminal.png" width="200" height="100" />\
|
||||
</a>\
|
||||
<h5>$ sudo rm -rf /</h5>\
|
||||
<p>You use this app every day, but do you know what happens when you option click in it?</p>\
|
||||
<p>\
|
||||
Find out in my magnum opus on \
|
||||
<a href="http://furbo.org/2014/09/03/the-terminal/">\
|
||||
The Terminal</a>.\
|
||||
</p>\
|
||||
');
|
||||
|
||||
content.push('\
|
||||
<a href="https://itunes.apple.com/us/app/ipulse/id1028916583?ls=1&mt=12&uo=4&at=10l4G7&ct=FURBO_FOOTER">\
|
||||
<img src="/footer/ipulse3.png" width="200" height="100" />\
|
||||
</a>\
|
||||
<h5>What is your Mac doing right now?</h5>\
|
||||
<p>If you\'re reading this site, chances are good that you know <em>a lot</em> about your computer.</p>\
|
||||
<p>\
|
||||
But do you know what\'s going on behind the scenes? \
|
||||
<a href="https://itunes.apple.com/us/app/ipulse/id1028916583?ls=1&mt=12&uo=4&at=10l4G7&ct=FURBO_FOOTER">\
|
||||
iPulse does</a>.\
|
||||
</p>\
|
||||
');
|
||||
|
||||
var min = 0;
|
||||
var max = content.length - 1;
|
||||
var index = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
|
||||
document.write(content[index]);
|
||||
</script>
|
||||
|
||||
</div><!-- .feature -->
|
||||
</div><!-- .features -->
|
||||
</footer><!-- .site-footer -->
|
||||
|
||||
</div><!-- .site -->
|
||||
|
||||
|
||||
<!-- Google Analytics for WordPress | http://yoast.com/wordpress/google-analytics/ -->
|
||||
<script type="text/javascript">
|
||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var pageTracker = _gat._getTracker("UA-12748460-3");
|
||||
// Cookied already:
|
||||
pageTracker._trackPageview();
|
||||
} catch(err) {}
|
||||
</script><!-- End of Google Analytics code -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- Dynamic Page Served (once) in 0.171 seconds -->
|
||||
<!-- super cache -->
|
172
Parser/Tests/ParserTests/Resources/inessential.html
Executable file
172
Parser/Tests/ParserTests/Resources/inessential.html
Executable file
@ -0,0 +1,172 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>inessential: weblog</title>
|
||||
<meta name="MSSmartTagsPreventParsing" content="true" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<style type="text/css" media="all">@import "http://inessential.com/styles/styleSheet.css";</style>
|
||||
<script type="text/javascript"> </script> <!-- FOUC hack: http://www.bluerobot.com/web/css/fouc.asp -->
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="http://inessential.com/xml/rss.xml" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="banner"><div id="innerbanner"><span id="biglink"><a href="http://inessential.com/"><img src="http://inessential.com/images/inessential_logo@2x.png" height=57 width=263 alt="inessential.com" /></a></span> <span id="bigbyline">by Brent Simmons</span></div>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/03/04/lizcast">Lizcast</a></h3>
|
||||
<div class="weblogPostBody"><p>The Omni Group’s Liz Marley, who recently transitioned from testing to engineering, <a href="https://overcast.fm/+BO9vrOfGc">appears on the NSNorth 2016 podcast</a>. She talks about…</p>
|
||||
|
||||
<blockquote><p>…challenges in engineering school, working with office cats, making the transition from software engineering to testing to developing and how knitting, like code, has the ultimate undo.</p></blockquote>
|
||||
|
||||
<p>Knitting is serious (though not somber) business here at Omni.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/03/04/lizcast">04 Mar 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/03/01/omnioutliner_4_5">OmniOutliner 4.5</a></h3>
|
||||
<div class="weblogPostBody"><p><a href="https://www.omnigroup.com/omnioutliner">OmniOutliner 4.5</a> is up on Omni’s site, and should be in the Mac App Store within days.</p>
|
||||
|
||||
<p>With this release — <a href="https://www.omnigroup.com/releasenotes/omnioutliner-mac">see the release notes</a> — I helped work on, of all things, <em>printing</em> bugs and features. This is the first time in my entire career where I worked on printing support that was more than just the most basic possible thing.</p>
|
||||
|
||||
<p>And that sounds weird for the year 2016, I realize. But here’s the thing: working on printing support is far from glamorous. You wouldn’t call it <em>fun</em>. But the people who need these features really do need them, and it’s a matter of respect for OmniOutliner users that we do a great job even with printing.</p>
|
||||
|
||||
<p>But I sure am glad to get it finished and shipping. And I’m proud of the work we did — more proud than I expected to be. It’s solid, and I think the people who print from OmniOutliner will be very pleased.</p>
|
||||
|
||||
<p>Now we’re on to <a href="https://www.omnigroup.com/blog/looking-back-looking-ahead-2016-edition">other new features</a>, including editing Markdown documents with OmniOutliner.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/03/01/omnioutliner_4_5">01 Mar 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/25/omnidev">OmniDev</a></h3>
|
||||
<div class="weblogPostBody"><p><a href="https://www.omnigroup.com/jobs/#mac-and-ios-developer">Omni is hiring a Mac/iOS developer</a>!</p>
|
||||
|
||||
<p>We’re also hiring a <a href="https://www.omnigroup.com/jobs/#senior-front-end-web-developer">web developer</a>, <a href="https://www.omnigroup.com/jobs/#graphic-designer">graphic designer</a>, and <a href="https://www.omnigroup.com/jobs/#phone-support-human">phone support humans</a>.</p>
|
||||
|
||||
<p>I’ll let you try out my new beanbag chair.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/25/omnidev">25 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/18/omnijobs">OmniJobs</a></h3>
|
||||
<div class="weblogPostBody"><p><a href="https://www.omnigroup.com/jobs">We’re hiring</a> a senior front-end web developer, a graphic designer, and support humans.</p>
|
||||
|
||||
<p>You should apply.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/18/omnijobs">18 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/11/it_will_be_trump">It Will Be Trump</a></h3>
|
||||
<div class="weblogPostBody"><p>The South Carolina primary is where the establishment fixes the errors of Iowa and New Hampshire. It’s Lee Atwater’s firewall.</p>
|
||||
|
||||
<p>When Buchanan threatens Dole, South Carolina shuts it down. When McCain threatens Bush, South Carolina applies the kibosh.</p>
|
||||
|
||||
<p>But is there any hope that it will function that way this time?</p>
|
||||
|
||||
<p>I don’t think so. The establishment candidates are Bush, Rubio, and Kasich. They don’t have a shot. Nor does Cruz. Trump wins South Carolina.</p>
|
||||
|
||||
<p>If that’s true, then it’s all over. If South Carolina fails — if the very primary that’s designed to toss the ball back to the establishment fails — then there’s no hope at all.</p>
|
||||
|
||||
<p>Cruz will go on to win a few states, most notably Texas. But otherwise it’s going to be Trump. He’ll get the delegates he needs, and that will be that.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/11/it_will_be_trump">11 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/09/origin_of_good_and_bad_hair_day">Origin of Good (and Bad) Hair Day</a></h3>
|
||||
<div class="weblogPostBody"><p>When I was in middle school in the late ’70s I struggled to get my hair to feather properly. It just didn’t want to do it.</p>
|
||||
|
||||
<p>Like many kids that age I was newly conscious of my appearance — and I naïvely thought that well-feathered hair was a necessary (though not sufficient) key to fitting in. (Which was probably true, by the way.)</p>
|
||||
|
||||
<p>Every morning I would find that my hair behaved, at least somewhat, or it didn’t. So I categorized each day as a “good hair day” and a “bad hair day.”</p>
|
||||
|
||||
<p>I told my friends about this categorization — including a neighborhood girl named Sarah. She ended up telling other kids at school.</p>
|
||||
|
||||
<p>And pretty soon those kids, even kids I didn’t really know, would stop me in the halls or at lunch and say, “Hey Brent — good hair day or bad hair day?” Not meanly. Teasingly. It was funny.</p>
|
||||
|
||||
<p>Years later I started hearing the phrase on TV, and I was surprised that my little middle-school thing had spread and become <a href="http://www.ecenglish.com/learnenglish/lessons/why-do-we-say-bad-hair-day">part of the culture</a>.</p>
|
||||
|
||||
<p style="text-align:center">* * *</p>
|
||||
|
||||
|
||||
<p>Of course, it’s also possible that I <a href="http://www.word-detective.com/072104.html">picked it up from Jane Pauley</a>. But for all these years I’ve believed — no joke — that it was me, that it was my phrase. Maybe Jane Pauley got it (indirectly) from me.</p>
|
||||
|
||||
<p>It’s <em>highly</em> unlikely — of course, I know this — that I’m the originator. But still, it had to be someone, right?</p>
|
||||
|
||||
<p>(Not necessarily. It’s kind of obvious and could have had many originators.)</p>
|
||||
|
||||
<p style="text-align:center">* * *</p>
|
||||
|
||||
|
||||
<p>I stopped categorizing good and bad hair days by the time I got to high school. And these days I’m just glad that I still have some hair.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/09/origin_of_good_and_bad_hair_day">09 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/08/river5">River5</a></h3>
|
||||
<div class="weblogPostBody"><p>River5 is Dave Winer’s <a href="https://github.com/scripting/river5">river-of-news RSS aggregator</a>.</p>
|
||||
|
||||
<p>It’s a Node app. You can run it on a public machine and access it anywhere, or run it on your desktop and just read your news there.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/08/river5">08 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/06/stop_watch">Stop Watch</a></h3>
|
||||
<div class="weblogPostBody"><p>Some time last week my iPhone started prompting me frequently to re-enter my iCloud password. And then my Watch started doing the same, about once a minute — with a little tap on the wrist each time.</p>
|
||||
|
||||
<p>Obviously I <em>did</em> re-enter my password — and have done so a dozen or so times now — but it doesn’t seem to matter.</p>
|
||||
|
||||
<p>So I stopped wearing my Watch and have switched to a mid-sixties Hamilton that my Dad gave me. (He had gotten it as a high school graduation present.)</p>
|
||||
|
||||
<p>I’m no watch aficionado — but I do appreciate a good and attractive watch (which this is), and I appreciate even more an old watch that’s a family thing.</p>
|
||||
|
||||
<p>Here’s the thing, though: the Apple Watch contains a hundred miracles of engineering and design, surely, but serious problems with software and services can turn even the most incredible hardware into something you just sit on your desk and ignore.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/06/stop_watch">06 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/05/on_sanders_governing">On Sanders Governing</a></h3>
|
||||
<div class="weblogPostBody"><p>The Atlantic, <a href="http://www.theatlantic.com/politics/archive/2016/02/why-bernie-sanders-cant-win-and-cant-govern/460182/">Norm Ornstein</a>:</p>
|
||||
|
||||
<blockquote><p>But is there any real evidence that there is a hidden “sleeper cell” of potential voters who are waiting for the signal to emerge and transform the electorate? No.</p></blockquote>
|
||||
|
||||
<p>Pure candidates on both sides of the spectrum often claim that their purity will bring in the checked-out voters, because they’re just waiting for a <em>real</em> conservative or a <em>real</em> liberal.</p>
|
||||
|
||||
<p>It’s an enduring fairy tale with terrible consequences. To put faith in it is to lose to the other party.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/05/on_sanders_governing">05 Feb 2016</a></span></div>
|
||||
</div><div class="weblogPost">
|
||||
<h3><a href="http://inessential.com/2016/02/05/cocoaconf_podcast_with_me">CocoaConf Podcast with Me</a></h3>
|
||||
<div class="weblogPostBody"><p>Cesare Rocchi interviewed me for the latest <a href="http://cocoaconf.com/podcast/16">CocoaConf Podcast</a> on life before the App Store.</p>
|
||||
|
||||
<p>There <em>was</em> a life, by the way. It was fun! We could release software any time we wanted to.</p></div>
|
||||
<div class="weblogPostDateline"><span class="weblogPostDisplayDate"><a href="http://inessential.com/2016/02/05/cocoaconf_podcast_with_me">05 Feb 2016</a></span></div>
|
||||
</div>
|
||||
<p><a href="http://inessential.com/archive">Archive</a></p>
|
||||
|
||||
</div> <!-- content -->
|
||||
|
||||
<div id="deckad">
|
||||
<div id="innerdeckad">
|
||||
<span id="adsViaTheDeck"><a href="http://decknetwork.net/">Ads via The Deck</a></span>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
(function(id) {
|
||||
document.write('<script type="text/javascript" src="' +
|
||||
'http://connect.decknetwork.net/deck' + id + '_js.php?' +
|
||||
(new Date().getTime()) + '"></' + 'script>');
|
||||
})("IE");
|
||||
//]]>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
|
||||
<!-- <p class="sidebarText"><a id="vesperlink" href="http://vesperapp.co/appstore">Vesper</a> - iOS app • <a href="http://therecord.co/">The Record</a> - podcast</p> -->
|
||||
<!-- <p class="sidebarText">Twitter: <a href="https://twitter.com/brentsimmons">brentsimmons</a> • <a href="https://twitter.com/inessential">inessential</a></p> -->
|
||||
<p class="sidebarText">What I work on at Omni<br /><a href="http://www.omnigroup.com/omnifocus">OmniFocus for Mac</a> • <a href="http://www.omnigroup.com/omnioutliner">OmniOutliner for Mac</a></p>
|
||||
<!-- <p class="sidebarText"> Open source<br /><a href="https://github.com/quartermaster/QSKit">Q Branch Standard Kit</a> •
|
||||
<a href="https://github.com/quartermaster/DB5">DB5</a></p> -->
|
||||
<p class="sidebarText"><a href="http://inessential.com/swiftdiary">Swift Diary</a><br />
|
||||
<a href="http://inessential.com/vespersyncdiary">Vesper Sync Diary</a> • <a href="http://inessential.com/hownottocrash">How Not to Crash</a><br />
|
||||
<a href="http://inessential.com/apps_ive_made">Apps I’ve Made</a> • <a href="http://inessential.com/xml/rss.xml">RSS</a></p>
|
||||
|
||||
|
||||
|
||||
</div> <!-- sidebar -->
|
||||
|
||||
<div id="footer">
|
||||
<p>© 1999-2016 Brent Simmons</p>
|
||||
<p>Made in Seattle. Go Hawks!</p>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
156
Parser/Tests/ParserTests/Resources/inessential.json
Normal file
156
Parser/Tests/ParserTests/Resources/inessential.json
Normal file
File diff suppressed because one or more lines are too long
2
Parser/Tests/ParserTests/Resources/kc0011.rss
Normal file
2
Parser/Tests/ParserTests/Resources/kc0011.rss
Normal file
File diff suppressed because one or more lines are too long
266
Parser/Tests/ParserTests/Resources/livemint.xml
Normal file
266
Parser/Tests/ParserTests/Resources/livemint.xml
Normal file
@ -0,0 +1,266 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
|
||||
<channel>
|
||||
<atom:link type="application/rss+xml" rel="self" href="https://www.livemint.com/rss/News"/>
|
||||
<title>Livemint - News</title>
|
||||
<description>Get the latest news and analysis on business, finance, politics from Livemint, the website of the Mint newspaper, one of India's leading business and financial dailies</description>
|
||||
<link>https://www.livemint.com/rss/News</link>
|
||||
<item>
|
||||
<title><![CDATA[PM Modi-Xi Jinping informal summit in Varanasi this October: Report]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/pm-modi-xi-jinping-informal-summit-in-varanasi-this-october-report-1559104147038.html]]></link>
|
||||
<description><![CDATA[PM Modi hosting Xi in Varanasi is in return for Xi receiving Modi in Wuhan last year in April.Previously, Modi hosted Xi in Ahmedabad, during Xi’s first ever visit to India in 2014]]></description>
|
||||
<author><![CDATA[Elizabeth Roche]]></author>
|
||||
<pubDate><![CDATA[Wed, 29 May 2019 10:16:00 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559104147038]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/29/90x90/PTI5_27_2019_000025B_1559105066780_1559105082772.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/29/250x250/PTI5_27_2019_000025B_1559105066780_1559105082772.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/29/600x338/PTI5_27_2019_000025B_1559105066780_1559105081104.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/29/250x250/PTI5_27_2019_000025B_1559105066780_1559105082772.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[US takes India off watchlist for currency practices]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/us-takes-india-off-watchlist-for-currency-practices-1559099681337.html]]></link>
|
||||
<description><![CDATA[Nine were included on a watchlist because they met some of the criteria.Apart from India, the Trump administration has also removed Switzerland from the list]]></description>
|
||||
<author><![CDATA[Staff Writer]]></author>
|
||||
<pubDate><![CDATA[Wed, 29 May 2019 08:53:48 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559099681337]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/29/90x90/2019-05-20T060757Z_1_LYNXNPEF4J0D5_RTROPTP_3_INDIA-ECONOMY-RUPEE-IMPORTS_1558929222985_1559100119000.JPG</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/29/250x250/2019-05-20T060757Z_1_LYNXNPEF4J0D5_RTROPTP_3_INDIA-ECONOMY-RUPEE-IMPORTS_1558929222985_1559100119000.JPG</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/29/600x338/2019-05-20T060757Z_1_LYNXNPEF4J0D5_RTROPTP_3_INDIA-ECONOMY-RUPEE-IMPORTS_1558929222985_1559100118447.JPG</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/29/250x250/2019-05-20T060757Z_1_LYNXNPEF4J0D5_RTROPTP_3_INDIA-ECONOMY-RUPEE-IMPORTS_1558929222985_1559100119000.JPG</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Singapore dethrones US as world's most competitive economy]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/world/singapore-dethrones-us-as-world-s-most-competitive-economy-1559072087104.html]]></link>
|
||||
<description><![CDATA[Singapore's advanced technological infrastructure, availability of skilled workers, favorable immigration laws, and efficiency for starting businesses supported its rise to the No. 1 spot.The US fell two spots as the confidence boost from tax cuts faded and high-technology exports weakened]]></description>
|
||||
<pubDate><![CDATA[Wed, 29 May 2019 01:15:10 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559072087104]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/singapore_1559072663725_1559072663977.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/singapore_1559072663725_1559072663977.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/singapore_1559072663725.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/singapore_1559072663725_1559072663977.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Naveen Patnaik govt to have 21 ministers, 10 new faces]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/naveen-patnaik-govt-to-have-21-ministers-10-new-faces-1559070546142.html]]></link>
|
||||
<description><![CDATA[The ministers and the chief minister will be administered the oath of the office Wednesday at 10.30 am.Senior MLA and former minister S N Patro is likely to be named as the Speaker of the Odisha Legislative Assembly]]></description>
|
||||
<pubDate><![CDATA[Wed, 29 May 2019 00:56:58 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559070546142]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/naveenpatnaik_1559071022218_1559071022726.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/naveenpatnaik_1559071022218_1559071022726.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/naveenpatnaik_1559071022218.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/naveenpatnaik_1559071022218_1559071022726.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[FDI inflows contract for the first time in 6 years]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/fdi-inflows-contract-for-the-first-time-in-6-years-1559070850501.html]]></link>
|
||||
<description><![CDATA[FY19 saw FDI equity inflows falling 1% to $44.4 bn in fresh policy challenge.The two sectors where FDI inflows dropped the most in 2018-19 are telecom (fell 56%) and pharma (dropped 74%)]]></description>
|
||||
<author><![CDATA[Asit Ranjan Mishra]]></author>
|
||||
<pubDate><![CDATA[Wed, 29 May 2019 00:50:01 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559070850501]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/chart3_1559070912589_1559070913170.png</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/chart3_1559070912589_1559070913170.png</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/chart3_1559070912589.png</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/chart3_1559070912589_1559070913170.png</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Apple and Nike brace for China's wrath after Huawei ban]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/world/apple-and-nike-brace-for-china-s-wrath-after-huawei-ban-1559067462522.html]]></link>
|
||||
<description><![CDATA[US companies counting on China for a major part of their growth have targets on their backs as Beijing and Washington ratchet up trade-war tensions.Blowback from Trump’s Huawei ban could cost Apple about 3% to 5% of its iPhone sales in China over the next 12 to 18 months, says an analyst]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 23:53:23 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559067462522]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/users_iqjWHBFdfxIU_ix35wG8drej4_v0_piFq5T3pJF0qzS8rF9LjsWaQ_-1x-1_1559067616552_1559067617151.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/users_iqjWHBFdfxIU_ix35wG8drej4_v0_piFq5T3pJF0qzS8rF9LjsWaQ_-1x-1_1559067616552_1559067617151.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/users_iqjWHBFdfxIU_ix35wG8drej4_v0_piFq5T3pJF0qzS8rF9LjsWaQ_-1x-1_1559067616552.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/users_iqjWHBFdfxIU_ix35wG8drej4_v0_piFq5T3pJF0qzS8rF9LjsWaQ_-1x-1_1559067616552_1559067617151.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Revised tax treaties usher in a new era in investment, taxation]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/revised-tax-treaties-usher-in-a-new-era-in-investment-taxation-1559067152154.html]]></link>
|
||||
<description><![CDATA[Policy tweaks curb evasion, show India can woo investments without sops.Singapore, another major FDI contributor to India, too, enjoyed the same tax sops under a similar treaty with India]]></description>
|
||||
<author><![CDATA[Gireesh Chandra Prasad]]></author>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 23:50:46 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559067152154]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/tax111_1559067450892_1559067451272.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/tax111_1559067450892_1559067451272.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/tax111_1559067450892.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/tax111_1559067450892_1559067451272.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Now, Netherlands and France favourites for routing investments]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/now-netherlands-and-france-favourites-for-routing-investments-1559066670041.html]]></link>
|
||||
<description><![CDATA[Changes signal shift to moderate tax investment regime instead of a potential tax-free regime.India shall now have, among others, the right to tax capital gains arising from alienation of shares acquired on or after 1 April 2017]]></description>
|
||||
<author><![CDATA[Girish Vanvari]]></author>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 23:39:17 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559066670041]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/1855d964-65b8-11e9-9325-ae0c7018af1d_1559066669263_1559066925162.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/1855d964-65b8-11e9-9325-ae0c7018af1d_1559066669263_1559066925162.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/1855d964-65b8-11e9-9325-ae0c7018af1d_1559066669263_1559066923369.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/1855d964-65b8-11e9-9325-ae0c7018af1d_1559066669263_1559066925162.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[GST Network releases prototype of simplified return filing system]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/gst-network-releases-prototype-of-simplified-return-filing-system-1559056140534.html]]></link>
|
||||
<description><![CDATA[It will allow users to use functionality such as drop down menus, invoice upload, upload of purchase register for matching with system created inward supplies.Annexure of supplies (GST ANX-1) and Annexure of Inward Supplies (GST ANX-2) will be filed as part of these returns]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 20:44:02 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559056140534]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/GST_1559056306038_1559056306332.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/GST_1559056306038_1559056306332.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/GST_1559056306038.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/GST_1559056306038_1559056306332.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Amnesty loses five bosses after report on 'toxic' workplace]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/world/amnesty-loses-five-bosses-after-report-on-toxic-workplace-1559056207175.html]]></link>
|
||||
<description><![CDATA[London-based Amnesty's Secretary-General ordered an independent review after two employees killed themselves last year.Five of the seven members of the senior leadership team at Amnesty’s international secretariat are leaving the organisation after a review]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 20:43:20 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559056207175]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/2019-05-14T141911Z_738553057_RC16BC3148C0_RTRMADP_3_EU-ELECTION-PROTEST_1559056266154_1559056294247.JPG</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/2019-05-14T141911Z_738553057_RC16BC3148C0_RTRMADP_3_EU-ELECTION-PROTEST_1559056266154_1559056294247.JPG</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/2019-05-14T141911Z_738553057_RC16BC3148C0_RTRMADP_3_EU-ELECTION-PROTEST_1559056266154_1559056292065.JPG</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/2019-05-14T141911Z_738553057_RC16BC3148C0_RTRMADP_3_EU-ELECTION-PROTEST_1559056266154_1559056294247.JPG</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Commercial flight operations likely to begin at Hindon airport by June end]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/commercial-flight-operations-likely-to-begin-at-hindon-airport-by-june-end-1559053314116.html]]></link>
|
||||
<description><![CDATA[The airport is around 40 kms from the Indira Gandhi International Airport in Delhi.Ministry of Defence has approved the proposal of Ghodawat Enterprises for operation of regional air connectivity scheme flights from the airport]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 19:54:56 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559053314116]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/2019-02-25T112526Z_1_LYNXNPEF1O0V1_RTROPTP_3_AVIATION-INDIA-LOSSES_1557334981399_1559053437650.JPG</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/2019-02-25T112526Z_1_LYNXNPEF1O0V1_RTROPTP_3_AVIATION-INDIA-LOSSES_1557334981399_1559053437650.JPG</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/2019-02-25T112526Z_1_LYNXNPEF1O0V1_RTROPTP_3_AVIATION-INDIA-LOSSES_1557334981399_1559053437199.JPG</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/2019-02-25T112526Z_1_LYNXNPEF1O0V1_RTROPTP_3_AVIATION-INDIA-LOSSES_1557334981399_1559053437650.JPG</image>
|
||||
</item><item>
|
||||
<title><![CDATA[5 kg LPG refill to power Ujjwala scheme under Modi 2.0]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/5-kg-lpg-refill-to-power-ujjwala-scheme-under-modi-2-0-1559050401777.html]]></link>
|
||||
<description><![CDATA[The Pradhan Mantri Ujjwala Yojana is expected to be further expanded under the incoming govt with a focus on delivery and use of smaller 5 kg LPG gas cylinders.The targeted 80 million connections under the Ujjwala scheme will be completed in the first 100 days of the incoming government]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 19:18:48 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559050401777]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/Ujjwala_1559051056965_1559051058915.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/Ujjwala_1559051056965_1559051058915.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/Ujjwala_1559051056965.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/Ujjwala_1559051056965_1559051058915.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Now that elections are over, fuel prices are again on the rise]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/now-that-elections-are-over-fuel-prices-are-again-on-the-rise-1559049671350.html]]></link>
|
||||
<description><![CDATA[Prices have been on the rise since May 20, a day after the final phase of polling for the Lok Sabha elections ended.The price of petrol has risen by 83 paise per litre in the nine days and diesel by 73 paise]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 18:56:48 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559049671350]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/Petrolpump-k9KF--621x414@LiveMint_1559049780846.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/Petrolpump-k9KF--621x414@LiveMint_1559049780846.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/Petrolpump-k9KF--621x414@LiveMint_1559049780380.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/Petrolpump-k9KF--621x414@LiveMint_1559049780846.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[NIA to join Sri Lanka bombings probe]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/world/nia-to-join-sri-lanka-bombings-probe-1559048238560.html]]></link>
|
||||
<description><![CDATA[A two-member team of the NIA is visiting Colombo to join the investigation in Sri Lanka.On 26 February, the NIA had passed on inputs to Sri Lanka about a plot that hinted at a deadly attack with ‘maximum damage’]]></description>
|
||||
<author><![CDATA[Shaswati Das]]></author>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 18:43:09 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559048238560]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/homeministry1a_1559048369787_1559048370946.JPG</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/homeministry1a_1559048369787_1559048370946.JPG</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/homeministry1a_1559048369787.JPG</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/homeministry1a_1559048369787_1559048370946.JPG</image>
|
||||
</item><item>
|
||||
<title><![CDATA[FDI inflows record 1st decline in six years in FY19]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/fdi-inflows-record-1st-decline-in-six-years-in-fy19-1559047086976.html]]></link>
|
||||
<description><![CDATA[FDI declined for the first time in the last six years in 2018-19, falling by 1% to $44.37 billion as overseas fund inflows subsided in telecom, pharma and other sectors.Since 2012-13, the inflows had been continuously growing and reached a record high in 2017-18]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 18:26:18 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559047086976]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/dollar_1559047989818_1559047992119.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/dollar_1559047989818_1559047992119.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/dollar_1559047989818.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/dollar_1559047989818_1559047992119.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[China rejects US envoy's call for talks with Dalai Lama]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/world/china-rejects-us-envoy-s-call-for-talks-with-dalai-lama-1559046449943.html]]></link>
|
||||
<description><![CDATA[Terry Branstad visited Tibet from May 19 to 25, the first such visit by a US ambassador to China to the remote Himalayan region since 2015.Over 120 Tibetans committed self-immolations in recent years calling for the return of the Dalai Lama from his exile in India]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 18:01:22 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559046449943]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/07afb0b997354dbe989d8cc5791161dd-07afb0b997354dbe989d8cc5791161dd-0_1559046474955_1559046504896.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/07afb0b997354dbe989d8cc5791161dd-07afb0b997354dbe989d8cc5791161dd-0_1559046474955_1559046504896.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/07afb0b997354dbe989d8cc5791161dd-07afb0b997354dbe989d8cc5791161dd-0_1559046474955_1559046503234.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/07afb0b997354dbe989d8cc5791161dd-07afb0b997354dbe989d8cc5791161dd-0_1559046474955_1559046504896.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[GST Council to examine legal, technical aspects of e-invoice for B2B sales]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/gst-council-to-examine-legal-technical-aspects-of-e-invoice-for-b2b-sales-1559044729324.html]]></link>
|
||||
<description><![CDATA[While one sub-group will examine the business process, policy and legal aspects for generation of e-invoice.The other sub group will recommend technical aspects for its roll-out]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 17:52:41 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559044729324]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/gst_1559046123932_1559046125342.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/gst_1559046123932_1559046125342.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/gst_1559046123932.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/gst_1559046123932_1559046125342.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[SmartCity Kochi to raise <span class='webrupee'>₹</span>4,000 crore from investors]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/smartcity-kochi-to-raise-rs-4-000-crore-from-investors-1559040304828.html]]></link>
|
||||
<description><![CDATA[In terms of development of IT business infrastructure, further investments to the tune of ₹2,200 crore could be attracted.The full build out investment potential of SmartCity Kochi is estimated to be ₹9,000 crore]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 16:25:41 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559040304828]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/real_estate_1559040791259_1559040793638.JPG</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/real_estate_1559040791259_1559040793638.JPG</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/real_estate_1559040791259.JPG</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/real_estate_1559040791259_1559040793638.JPG</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Statue of Unity enters 2019 World Architecture News Awards]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/statue-of-unity-enters-2019-world-architecture-news-awards-1559037851490.html]]></link>
|
||||
<description><![CDATA[Constructed in a record 33 months, the world's tallest statue stands at 182 metre.The statue is a memorial to the contributions of Sardar Vallabhbhai Patel in the country's freedom struggle]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 15:48:38 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559037851490]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/Patel-kyU--621x414@LiveMint_1559038064137.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/Patel-kyU--621x414@LiveMint_1559038064137.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/Patel-kyU--621x414@LiveMint_1559038063642.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/Patel-kyU--621x414@LiveMint_1559038064137.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Inside the printing press that churns out new 100 and 200 euro banknotes]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/world/inside-the-printing-press-that-churns-out-new-100-and-200-euro-banknotes-1559036543628.html]]></link>
|
||||
<description><![CDATA[The new euro notes with enhanced security feature.National central banks within the euro zone have jointly printed the euro currency banknotes since 2002]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 15:23:23 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559036543628]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/2019-05-28T001204Z_1671817231_RC1447E30500_RTRMADP_3_ECB-BANKNOTES_1559036993914_1559037047616.JPG</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/2019-05-28T001204Z_1671817231_RC1447E30500_RTRMADP_3_ECB-BANKNOTES_1559036993914_1559037047616.JPG</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/2019-05-28T001204Z_1671817231_RC1447E30500_RTRMADP_3_ECB-BANKNOTES_1559036993914_1559037045523.JPG</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/2019-05-28T001204Z_1671817231_RC1447E30500_RTRMADP_3_ECB-BANKNOTES_1559036993914_1559037047616.JPG</image>
|
||||
</item><item>
|
||||
<title><![CDATA[PM Modi's landslide victory lets him undertake 'difficult' tasks, says adviser]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/pm-modi-s-landslide-victory-lets-him-undertake-difficult-tasks-says-adviser-1559035840274.html]]></link>
|
||||
<description><![CDATA[Investors in India are closely watching for how RBI as well as Modi drive the economy amid concerns of a prolonged slowdown.Expectations are also growing for Modi to push through land and labour reforms after BJP won 303 of 543 elected seats in the recently concluded Lok Sabha polls]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 15:11:52 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559035840274]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/2019-05-28T081830Z_2_LYNXNPEF4Q13L_RTROPTP_3_INDIA-ELECTION-BUSINESS_1559036257879_1559036276435.JPG</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/2019-05-28T081830Z_2_LYNXNPEF4Q13L_RTROPTP_3_INDIA-ELECTION-BUSINESS_1559036257879_1559036276435.JPG</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/2019-05-28T081830Z_2_LYNXNPEF4Q13L_RTROPTP_3_INDIA-ELECTION-BUSINESS_1559036257879_1559036275737.JPG</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/2019-05-28T081830Z_2_LYNXNPEF4Q13L_RTROPTP_3_INDIA-ELECTION-BUSINESS_1559036257879_1559036276435.JPG</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Centre asks all depts to make public compassionate appointments related details]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/centre-asks-all-depts-to-make-public-compassionate-appointments-related-details-1559032453770.html]]></link>
|
||||
<description><![CDATA[The move comes following an order by the Central Information Commission (CIC) in this regard.The Ministry of Personnel had on May 20 issued an order to all central government departments seeking compliance of the directive of the CIC]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 14:09:59 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559032453770]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/CIC_1559032740836_1559032741197.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/CIC_1559032740836_1559032741197.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/CIC_1559032740836.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/CIC_1559032740836_1559032741197.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[The macroeconomic dilemma for India’s new government]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/the-macroeconomic-dilemma-for-india-s-new-government-1559029581234.html]]></link>
|
||||
<description><![CDATA[The Indian economy appears to be losing momentum, with only five of the sixteen high-frequency indicators tracked by the Mint Macro Tracker in the green last month]]></description>
|
||||
<author><![CDATA[Nikita Kwatra]]></author>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 13:38:28 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559029581234]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/AFP_1F93NY_1559030645359_1559030665942.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/AFP_1F93NY_1559030645359_1559030665942.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/AFP_1F93NY_1559030645359_1559030663920.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/AFP_1F93NY_1559030645359_1559030665942.jpg</image>
|
||||
</item><item>
|
||||
<title><![CDATA[Gurgaon Metro could soon be extended between HUDA City Centre and Cyber City. Haryana govt clears DPR]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/gurgaon-metro-could-soon-be-extended-between-huda-city-centre-and-cyber-city-1559026138709.html]]></link>
|
||||
<description><![CDATA[The new metro line in Gurgaon will have 25 stations and six interchange stations.Gurugram Metropolitan Development Authority expects the new Metro to be operational by 2023]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 12:29:30 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559026138709]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/gurgaon_metro_1559026598546_1559026599543.JPG</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/gurgaon_metro_1559026598546_1559026599543.JPG</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/gurgaon_metro_1559026598546.JPG</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/gurgaon_metro_1559026598546_1559026599543.JPG</image>
|
||||
</item><item>
|
||||
<title><![CDATA[India among fastest-growing alternative accommodation markets for Booking.com]]></title>
|
||||
<link><![CDATA[https://www.livemint.com/news/india/india-among-fastest-growing-alternative-accommodation-markets-for-booking-com-1559022596374.html]]></link>
|
||||
<description><![CDATA[A large chunk of Booking.com's supply growth in these categories came from states of Goa, Karnataka and Kerala.The company witnessed a 68% 'supply growth' in this category in 2017-18 in India]]></description>
|
||||
<pubDate><![CDATA[Tue, 28 May 2019 11:51:32 GMT]]> </pubDate>
|
||||
<guid><![CDATA[1559022596374]]></guid>
|
||||
<thumbnail>https://images.livemint.com/img/2019/05/28/90x90/Leela--621x414_1559024321928.jpg</thumbnail>
|
||||
<smallimage>https://images.livemint.com/img/2019/05/28/250x250/Leela--621x414_1559024321928.jpg</smallimage>
|
||||
<bigimage>https://images.livemint.com/img/2019/05/28/600x338/Leela--621x414_1559024321238.jpg</bigimage>
|
||||
<image>https://images.livemint.com/img/2019/05/28/250x250/Leela--621x414_1559024321928.jpg</image>
|
||||
</item></channel>
|
||||
</rss>
|
3123
Parser/Tests/ParserTests/Resources/macworld.rss
Normal file
3123
Parser/Tests/ParserTests/Resources/macworld.rss
Normal file
File diff suppressed because it is too large
Load Diff
228
Parser/Tests/ParserTests/Resources/manton.rss
Executable file
228
Parser/Tests/ParserTests/Resources/manton.rss
Executable file
@ -0,0 +1,228 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
|
||||
xmlns:content="http://purl.org/rss/1.0/modules/content/"
|
||||
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:atom="http://www.w3.org/2005/Atom"
|
||||
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
|
||||
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
|
||||
>
|
||||
|
||||
<channel>
|
||||
<title>Manton Reece</title>
|
||||
<atom:link href="http://www.manton.org/feed" rel="self" type="application/rss+xml" />
|
||||
<link>http://www.manton.org</link>
|
||||
<description></description>
|
||||
<lastBuildDate>Fri, 25 Sep 2015 14:26:40 +0000</lastBuildDate>
|
||||
<language>en-US</language>
|
||||
<sy:updatePeriod>hourly</sy:updatePeriod>
|
||||
<sy:updateFrequency>1</sy:updateFrequency>
|
||||
<generator>http://wordpress.org/?v=4.2.5</generator>
|
||||
<item>
|
||||
<title></title>
|
||||
<link>http://www.manton.org/2015/09/3071.html</link>
|
||||
<comments>http://www.manton.org/2015/09/3071.html#comments</comments>
|
||||
<pubDate>Fri, 25 Sep 2015 14:26:40 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Snippets]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3071</guid>
|
||||
<description><![CDATA[This week’s Core Intuition is out with a discussion about new and old iPhones, the latest rumors about an Apple Car, and a follow-up on WebKit for Apple TV.]]></description>
|
||||
<content:encoded><![CDATA[<p><a href="http://coreint.org/199">This week’s Core Intuition</a> is out with a discussion about new and old iPhones, the latest rumors about an Apple Car, and a follow-up on WebKit for Apple TV.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/3071.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title></title>
|
||||
<link>http://www.manton.org/2015/09/3069.html</link>
|
||||
<comments>http://www.manton.org/2015/09/3069.html#comments</comments>
|
||||
<pubDate>Fri, 25 Sep 2015 00:38:25 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Snippets]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3069</guid>
|
||||
<description><![CDATA[I probably shouldn’t have started installing watchOS 2.0 right before needing to leave the house. Taking… for… ev… er.]]></description>
|
||||
<content:encoded><![CDATA[<p>I probably shouldn’t have started installing watchOS 2.0 right before needing to leave the house. Taking… for… ev… er.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/3069.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title></title>
|
||||
<link>http://www.manton.org/2015/09/3067.html</link>
|
||||
<comments>http://www.manton.org/2015/09/3067.html#comments</comments>
|
||||
<pubDate>Thu, 24 Sep 2015 15:51:51 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Snippets]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3067</guid>
|
||||
<description><![CDATA[Looking forward to NSDrinking tonight, 8pm at Radio Coffee & Beer.]]></description>
|
||||
<content:encoded><![CDATA[<p>Looking forward to NSDrinking tonight, 8pm at Radio Coffee & Beer.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/3067.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title>Instagram hits 400 million users</title>
|
||||
<link>http://www.manton.org/2015/09/instagram-hits-400-million-users.html</link>
|
||||
<comments>http://www.manton.org/2015/09/instagram-hits-400-million-users.html#comments</comments>
|
||||
<pubDate>Wed, 23 Sep 2015 14:34:12 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Technology]]></category>
|
||||
<category><![CDATA[facebook]]></category>
|
||||
<category><![CDATA[instagram]]></category>
|
||||
<category><![CDATA[photos]]></category>
|
||||
<category><![CDATA[scaling]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3064</guid>
|
||||
<description><![CDATA[From Graham Spencer at MacStories, commenting on the latest Instagram numbers and that the service is only 5 years old: “But I was really surprised to remember that Facebook acquired Instagram in April 2012, when Instagram had ‘only’ 40 million users. If I recall correctly, a lot of people thought Facebook was crazy to buy […]]]></description>
|
||||
<content:encoded><![CDATA[<p><a href="https://www.macstories.net/news/instagram-hits-400-million-users/">From Graham Spencer</a> at MacStories, commenting on the latest Instagram numbers and that the service is only 5 years old:</p>
|
||||
<blockquote><p>
|
||||
“But I was really surprised to remember that <a href="http://techcrunch.com/2012/04/09/facebook-to-acquire-instagram-for-1-billion/">Facebook acquired Instagram in April 2012</a>, when Instagram had ‘only’ 40 million users. If I recall correctly, a lot of people thought Facebook was crazy to buy Instagram for $1 billion. Well, I think Facebook got the last laugh on that one, and as <a href="http://www.forbes.com/sites/kathleenchaykowski/2015/09/22/instagram-hits-400-million-users-soaring-past-twitter/">Forbes points out</a>, Instagram now has more monthly active users than Twitter (316 million).”
|
||||
</p></blockquote>
|
||||
<p>Impressive growth, but it fits. Instagram has crafted a user experience that encourages thoughtful posts and never feels overwhelming in the way a Twitter or Facebook timeline can be. If Instagram was a paid product, I bet Instagram’s <a href="https://baremetrics.com/academy/saas-churn">churn rate</a> would be the lowest of any of the big social networks. They did it with a small team and weren’t afraid to grow slowly.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/instagram-hits-400-million-users.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title>Complete mirror of this blog</title>
|
||||
<link>http://www.manton.org/2015/09/complete-mirror-of-this-blog.html</link>
|
||||
<comments>http://www.manton.org/2015/09/complete-mirror-of-this-blog.html#comments</comments>
|
||||
<pubDate>Sun, 20 Sep 2015 19:00:33 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Weblogs]]></category>
|
||||
<category><![CDATA[github]]></category>
|
||||
<category><![CDATA[httrack]]></category>
|
||||
<category><![CDATA[mirror]]></category>
|
||||
<category><![CDATA[openweb]]></category>
|
||||
<category><![CDATA[wordpress]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3061</guid>
|
||||
<description><![CDATA[I’ve been blogging here for 13 years. If you take any random post from that first year, the majority of the links to other web sites are broken. The default outcome for any site that isn’t maintained — including the one you’re reading right now — is for it to vanish. Permanence doesn’t exist on the […]]]></description>
|
||||
<content:encoded><![CDATA[<p>I’ve been blogging here for 13 years. If you take any random post from that first year, the majority of the links to other web sites are broken. The default outcome for any site that isn’t maintained — including the one you’re reading right now — is for it to vanish. <a href="http://www.manton.org/2012/07/permanence.html">Permanence</a> doesn’t exist on the web.</p>
|
||||
<p>We can <a href="http://www.manton.org/2015/09/web-history-and-ipfs.html">solve this</a>, but it will take time. For now I think mirroring our writing is a great solution, to guard against domain names expiring and other inevitable failures. But where to mirror to?</p>
|
||||
<p>Only 2 companies keep coming to mind: WordPress.com and GitHub. I believe both will last for decades, maybe even 100 years, and both embrace the open web in a way that most other centralized web sites do not.</p>
|
||||
<p>Even though I self-host this weblog on WordPress, I’ve chosen to mirror to GitHub because of their focus on simple, static publishing via <a href="https://pages.github.com/">GitHub Pages</a>. It has the best chance of running for a long time without intervention.</p>
|
||||
<p>I exported all of manton.org with the <a href="http://www.httrack.com/">httrack</a> command-line tool and checked it into GitHub, with a CNAME for <a href="http://mirror.manton.org/">mirror.manton.org</a>. It works perfectly. I still need to automate this process so that it updates regularly, but I’m very happy to finally have a complete mirror for the first time.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/complete-mirror-of-this-blog.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title>Steve Jobs and ET</title>
|
||||
<link>http://www.manton.org/2015/09/steve-jobs-and-et.html</link>
|
||||
<comments>http://www.manton.org/2015/09/steve-jobs-and-et.html#comments</comments>
|
||||
<pubDate>Sat, 19 Sep 2015 23:00:36 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Movies]]></category>
|
||||
<category><![CDATA[atari]]></category>
|
||||
<category><![CDATA[documentaries]]></category>
|
||||
<category><![CDATA[et]]></category>
|
||||
<category><![CDATA[games]]></category>
|
||||
<category><![CDATA[stevejobs]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3059</guid>
|
||||
<description><![CDATA[I watched two documentaries last week. The first was “Steve Jobs: The Man in the Machine”, which I somewhat regret paying $7 to rent. It had its moments, but also seemed to become more negative and dramatic the longer it went on. I guess we should all hope to be so lucky and famous to […]]]></description>
|
||||
<content:encoded><![CDATA[<p>I watched two documentaries last week. The first was <a href="http://www.magpictures.com/stevejobsthemaninthemachine/">“Steve Jobs: The Man in the Machine”</a>, which I somewhat regret paying $7 to rent. It had its moments, but also seemed to become more negative and dramatic the longer it went on. I guess we should all hope to be so lucky and famous to have people try to bring out the best and worst of us.</p>
|
||||
<p>The second documentary I watched was <a href="http://www.netflix.com/title/80042198">“Atari: Game Over”</a>, which was free on Netflix. It was great, interspersing a history of the rise and fall of Atari with the effort to dig up the ET game cartridges supposedly buried in New Mexico. Highly recommended.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/steve-jobs-and-et.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title>Peace, indies, and the App Store</title>
|
||||
<link>http://www.manton.org/2015/09/peace-indies-and-the-app-store.html</link>
|
||||
<comments>http://www.manton.org/2015/09/peace-indies-and-the-app-store.html#comments</comments>
|
||||
<pubDate>Sat, 19 Sep 2015 15:53:31 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Technology]]></category>
|
||||
<category><![CDATA[appstore]]></category>
|
||||
<category><![CDATA[indie]]></category>
|
||||
<category><![CDATA[ios9]]></category>
|
||||
<category><![CDATA[marco]]></category>
|
||||
<category><![CDATA[peace]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3056</guid>
|
||||
<description><![CDATA[You’ve probably heard that Marco Arment has pulled his content-blocking app Peace from the App Store. The app was extremely successful: “As I write this, Peace has been the number one paid app in the U.S. App Store for about 36 hours. It’s a massive achievement that should be the highlight of my professional career. […]]]></description>
|
||||
<content:encoded><![CDATA[<p>You’ve probably heard that Marco Arment has pulled his content-blocking app Peace from the App Store. The app was <a href="http://www.marco.org/2015/09/18/just-doesnt-feel-good">extremely successful</a>:</p>
|
||||
<blockquote><p>
|
||||
“As I write this, Peace has been the number one paid app in the U.S. App Store for about 36 hours. It’s a massive achievement that should be the highlight of my professional career. If <a href="https://overcast.fm/">Overcast</a> even broke the top 100, I’d be over the moon.”
|
||||
</p></blockquote>
|
||||
<p>I’ve seen some comments asking why he didn’t think to do this sooner, before he even shipped the app. But we are just now starting to understand the impact of ad blockers in iOS 9. I don’t think it’s an exaggeration to say that the web is different than it was a few days ago, and so our choices — and Marco’s — are different too. As I mentioned <a href="http://www.manton.org/2015/09/wrap-up-thoughts-on-the-tv-web.html">yesterday</a>, content blockers are one facet of an overall shake-up for the web.</p>
|
||||
<p><a href="http://inessential.com/2015/09/18/why_we_love_indies">Brent Simmons writes</a> that only indies can do what Marco did. Marco must have left a lot of money on the table with this decision. It will always look like the right call to me when someone goes with their gut feeling and not with profit.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/peace-indies-and-the-app-store.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title>Core Intuition and ATP this week</title>
|
||||
<link>http://www.manton.org/2015/09/core-intuition-and-atp-this-week.html</link>
|
||||
<comments>http://www.manton.org/2015/09/core-intuition-and-atp-this-week.html#comments</comments>
|
||||
<pubDate>Fri, 18 Sep 2015 20:25:57 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Technology]]></category>
|
||||
<category><![CDATA[appletv]]></category>
|
||||
<category><![CDATA[atp]]></category>
|
||||
<category><![CDATA[coreint]]></category>
|
||||
<category><![CDATA[web]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3051</guid>
|
||||
<description><![CDATA[On this week’s Core Intuition, Daniel and I spend the whole show talking about the Apple TV. The first half is about the Apple TV dev kit lottery, and the second half is about whether we need the web on our TVs. There’s also a good discussion on the Accidental Tech Podcast about this. Here’s […]]]></description>
|
||||
<content:encoded><![CDATA[<p>On <a href="http://coreint.org/198">this week’s Core Intuition</a>, Daniel and I spend the whole show talking about the Apple TV. The first half is about the Apple TV dev kit lottery, and the second half is about whether we need the web on our TVs.</p>
|
||||
<p>There’s also a good discussion on the Accidental Tech Podcast about this. Here’s <a href="https://overcast.fm/+CdR7CP78/1:15:38">an Overcast link about halfway into the episode</a>.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/core-intuition-and-atp-this-week.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title>Wrap-up thoughts on the TV web</title>
|
||||
<link>http://www.manton.org/2015/09/wrap-up-thoughts-on-the-tv-web.html</link>
|
||||
<comments>http://www.manton.org/2015/09/wrap-up-thoughts-on-the-tv-web.html#comments</comments>
|
||||
<pubDate>Fri, 18 Sep 2015 14:58:39 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Weblogs]]></category>
|
||||
<category><![CDATA[appletv]]></category>
|
||||
<category><![CDATA[iphone]]></category>
|
||||
<category><![CDATA[openweb]]></category>
|
||||
<category><![CDATA[steve]]></category>
|
||||
<category><![CDATA[wap]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3048</guid>
|
||||
<description><![CDATA[I’m going to mostly let John Gruber have the last word on the Apple TV vs. the web debate, because I could write about this every day and my readers would run away before I run out of material. I’m glad John addressed the Mac vs. the command-line argument, though, because it didn’t seem quite […]]]></description>
|
||||
<content:encoded><![CDATA[<p>I’m going to mostly let John Gruber <a href="http://daringfireball.net/linked/2015/09/17/apple-tv-and-the-web">have the last word</a> on the Apple TV vs. the web debate, because I could write about this every day and my readers would run away before I run out of material. I’m glad John addressed the Mac vs. the command-line argument, though, because it didn’t seem quite right to me either. He says:</p>
|
||||
<blockquote><p>
|
||||
“The difference is that the command-line-less Mac was intended to <em>replace</em> command-line-based computers. The GUI relegated the command-line interface to a permanent tiny niche. Apple TV and Apple Watch aren’t like that at all — they’re not meant to replace any device you already use to access the open web.”
|
||||
</p></blockquote>
|
||||
<p>This is the most hopeful part of the Apple ecosystem as it relates to the web. Apple’s other platforms really do have a great web experience. Remember when web sites were faster and worked better on a PC than a Mac? If anything, the opposite is true now.</p>
|
||||
<p>One of the themes I keep hearing is that a “web browser” on a TV will make for a poor user experience, so don’t bother. I tried to correct that misunderstanding in <a href="http://www.manton.org/2015/09/the-web-without-html.html">this post</a>; it’s not about standalone Safari, it’s about web technologies that could be used in native apps. But ignoring that, I think everyone too easily forgets what the mobile web was like before the iPhone.</p>
|
||||
<p>Steve Jobs, from the original iPhone introduction:</p>
|
||||
<blockquote><p>
|
||||
“We wanted the best web browser in the world on our phone. Not a baby web browser or a WAP browser — a <em>real</em> browser. […] It is the first fully usable HTML browser on a phone.”
|
||||
</p></blockquote>
|
||||
<p>That was a breakthrough. I believe the same evolution is possible on tvOS — to include parts of the open web and do it with a great user experience. You can start by weaving it together inside native apps. (I <a href="http://openradar.appspot.com/radar?id=6066362971586560">filed a bug with Apple</a> yesterday with a suggestion. It was marked as a duplicate.)</p>
|
||||
<p>The web is at a fascinating, pivotal time right now. It has been shaken up by centralized publishing, closed platforms, and now content blockers. Users no longer value the concepts that made <a href="https://en.wikipedia.org/wiki/Web_2.0">Web 2.0</a> special. The web can still have a strong future, but we have to <em>try something</em>, and we have to try it on every platform we can.</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/wrap-up-thoughts-on-the-tv-web.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
<item>
|
||||
<title></title>
|
||||
<link>http://www.manton.org/2015/09/3046.html</link>
|
||||
<comments>http://www.manton.org/2015/09/3046.html#comments</comments>
|
||||
<pubDate>Fri, 18 Sep 2015 13:43:21 +0000</pubDate>
|
||||
<dc:creator><![CDATA[manton]]></dc:creator>
|
||||
<category><![CDATA[Snippets]]></category>
|
||||
|
||||
<guid isPermaLink="false">http://www.manton.org/?p=3046</guid>
|
||||
<description><![CDATA[Expecting two packages today: the new Apple TV, and my new iPhone 5S (32 GB, space gray).]]></description>
|
||||
<content:encoded><![CDATA[<p>Expecting two packages today: the new Apple TV, and my new iPhone 5S (32 GB, space gray).</p>
|
||||
]]></content:encoded>
|
||||
<wfw:commentRss>http://www.manton.org/2015/09/3046.html/feed</wfw:commentRss>
|
||||
<slash:comments>0</slash:comments>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
|
||||
<!-- Dynamic page generated in 0.851 seconds. -->
|
||||
<!-- Cached page generated by WP-Super-Cache on 2015-09-25 19:01:08 -->
|
273
Parser/Tests/ParserTests/Resources/monkeydom.rss
Normal file
273
Parser/Tests/ParserTests/Resources/monkeydom.rss
Normal file
@ -0,0 +1,273 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>cocoa-dom</title>
|
||||
<description>coding bits I use, come across, like, hate, the whole shebang.</description>
|
||||
<link>https://coding.monkeydom.de</link>
|
||||
<atom:link href="https://coding.monkeydom.de/posts.rss" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<title>TCMPortMapper 2018 Edition</title>
|
||||
<description><p>About 10 years ago, I released one of my first pieces of open source software. For the collaboration part of <a href="https://subethaedit.net/">SubEthaEdit</a> we needed a way to make the app&#39;s network service available to the outside world, so you could invite people to documents without any additional setup.</p>
|
||||
|
||||
<p>To achieve this without any other additional central service to communicate over, one way is to leverage the port mapping services of your router to open up to the outside world. And for this reason, I created <a href="https://github.com/monkeydom/TCMPortMapper">TCMPortMapper</a>.framework with its companion example and in itself useful <a href="https://github.com/monkeydom/TCMPortMapper/releases/tag/PortMap-2.0.0">Port Map.app</a>.</p>
|
||||
|
||||
<p><a href="https://github.com/monkeydom/TCMPortMapper/releases/tag/PortMap-2.0.0">
|
||||
<img alt="Port Map App Icon" src="/assets/posts/PortMapAppIcon-512.png" width="256">
|
||||
</a></p>
|
||||
|
||||
<p>Since this piece of software was more or less just working after its inception, I left it quite unmaintained for a long time. This is also signified by the fact, that the original repo was on google code, and it didn&#39;t even make it over to another hoster when it got shutdown, so the commit history was lost.</p>
|
||||
|
||||
<p>So I took some time to revive it, clean it up, get the original commit history and update the libraries it leverages. </p>
|
||||
|
||||
<p>The funny thing is, that 10 years ago the thought was, when IPv6 will be here, port mapping would be obsolete. Sadly, that is not the case. Both IPv6 is not available everywhere yet, and as it turns out, the default IPv6 behaviour of routers is to close you off from the outside world too. With most routers it is not an easy feat to open it up again for the layperson. With that in mind I&#39;ll probably look into what kind of information/help for IPv6 connectivity <a href="https://github.com/monkeydom/TCMPortMapper">TCMPortMapper</a>.framework can give you, now that the codebase is cleaned up again and fit for the next few years.</p>
|
||||
|
||||
<p>Until then, the existing <a href="https://github.com/monkeydom/TCMPortMapper/releases/tag/PortMap-2.0.0">Port Map.app</a> can be helpful for you in some of the following scenarios:</p>
|
||||
|
||||
<ul>
|
||||
<li>make your SSH avaialable temporarily, or permanently e.g. if you use dyn-dns for your home router.</li>
|
||||
<li>temporarily expose some services to others. E.g. I use it so show my local web development things by turning on Port Map for e.g. 4000 and copy the link to others in a chat.</li>
|
||||
<li>know your external IPv4 address, if any.</li>
|
||||
<li>looking up what ports are currently mapped in your UPnP enabled router.</li>
|
||||
</ul>
|
||||
|
||||
<p><a href="https://github.com/monkeydom/TCMPortMapper/">
|
||||
<img alt="Port Map Screenshot" src="/assets/posts/PortMapApp-Screenshot.png" width="602"></a></p>
|
||||
<p><a href="https://coding.monkeydom.de/posts/2018/09/06/tcmportmapper">⚓</a></p></description>
|
||||
<pubDate>Thu, 6 Sep 2018 13:27:00 +0000</pubDate>
|
||||
<link>https://github.com/monkeydom/TCMPortMapper</link>
|
||||
<guid>cocoa-dom:tcmportmapper</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>Command line utilities: exa and bat</title>
|
||||
<description><p>While surfing through the <a href="http://rust-lang.org">rust</a> ecosystem, I stumbled across a few excellent command line utilities I enjoy using and installed on all my systems. I&#39;m going to post about them for my own reference and in case they might be useful to you too.</p>
|
||||
|
||||
<h3>exa</h3>
|
||||
|
||||
<p><a href="https://the.exa.website">exa</a> is a modern replacement for ls. Apart from great coloring I use it mostly for its tree based view and git and extended attributes integration.</p>
|
||||
<div class="highlight"><pre class="highlight plaintext"><code>dom@dreizehn:/usr/local/etc$ exa -lTF
|
||||
drwxrwxr-x - dom 3 Jul 13:02 ./
|
||||
drwxr-xr-x - dom 3 Jul 13:02 ├── bash_completion.d/
|
||||
lrwxr-xr-x 36 dom 29 Jun 2017 │ ├── brew -&gt; ../../Homebrew/completions/bash/brew
|
||||
lrwxr-xr-x 48 dom 5 Oct 2017 │ ├── exa -&gt; ../../Cellar/exa/0.8.0/etc/bash_completion.d/exa
|
||||
lrwxr-xr-x 51 dom 29 Apr 4:36 │ ├── fd.bash -&gt; ../../Cellar/fd/7.0.0/etc/bash_completion.d/fd.bash
|
||||
lrwxr-xr-x 65 dom 22 Jun 16:38 │ ├── git-completion.bash -&gt; ../../Cellar/git/2.18.0/etc/bash_completion.d/git-completion.bash
|
||||
lrwxr-xr-x 59 dom 22 Jun 16:38 │ ├── git-prompt.sh -&gt; ../../Cellar/git/2.18.0/etc/bash_completion.d/git-prompt.sh
|
||||
…
|
||||
</code></pre></div>
|
||||
<h3>bat</h3>
|
||||
|
||||
<p><a href="https://github.com/sharkdp/bat">bat</a> to me is a great less/cat combination that adds syntax highlighting and line numbers. Especially useful on remote servers.</p>
|
||||
<div class="highlight"><pre class="highlight plaintext"><code>dom@dreizehn:/etc$ bat hosts
|
||||
───────┬────────────────────────────────────────────────────────────────────────────────────────────
|
||||
│ File: hosts
|
||||
───────┼────────────────────────────────────────────────────────────────────────────────────────────
|
||||
1 │ ##
|
||||
2 │ # Host Database
|
||||
3 │ #
|
||||
4 │ # localhost is used to configure the loopback interface
|
||||
5 │ # when the system is booting. Do not change this entry.
|
||||
6 │ ##
|
||||
7 │ 127.0.0.1 localhost
|
||||
8 │ 255.255.255.255 broadcasthost
|
||||
9 │ ::1 localhost
|
||||
───────┴────────────────────────────────────────────────────────────────────────────────────────────
|
||||
</code></pre></div></description>
|
||||
<pubDate>Thu, 5 Jul 2018 12:30:39 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2018/07/05/command-line-utilities-exa-and-bat</link>
|
||||
<guid>cocoa-dom:command-line-utilities-exa-and-bat</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>Swift Library naming warts</title>
|
||||
<description><p>This is just a collection of oddities / naming choices that I ran across while playing with Swift. This list isn&#39;t exhaustive, nor do I have a good solution for all of them. It&#39;s just an observational piece on things that produced a negative gut reaction.</p>
|
||||
|
||||
<ul>
|
||||
<li>the <strong>func</strong> keyword <br/>
|
||||
somehow that one really doesn&#39;t sit well with me. I&#39;d much more preferred it if the keyword would have called <strong>function</strong>. However It has been pointed out that other abbreviations are used as well in standard keywords. Most notably: <strong>enum</strong>, <strong>var</strong></li>
|
||||
<li><p><strong>succ(), pred()</strong> <br/>
|
||||
The ForwardIndex and BidirectionalIndex use these methods to move forward and backward. I&#39;d much rather have seen <strong>successor()</strong> and <strong>predecessor()</strong> there. Or <strong>nextIndex()</strong> and <strong>previousIndex()</strong> since these methods are tied to Indexes. Or <strong>succedingIndex()</strong> and <strong>precedingIndex()</strong>. Actually this hits a sore point in the current Swift Library quite on the mark: how general or specific should the naming of methods and global functions be? And how can this be as consistent as possible to give future code writers the right idea and direction to make these in a way they fit in the language? Something that is very established in Cocoa and imho needs to be established in Swift as soon as possible.</p></li>
|
||||
<li><p><strong>advance()</strong> <br/>
|
||||
This is used to move an index forward by a distance. So why not <strong>advanceIndex()</strong>? And also what is the inverse? I found no good one in the english language, but I&#39;m foreign. There is no inverse because you are supposed to use advance with a negative distance to move backward. I like that the name includes the direction, so you know your moving forwards with a positive distance and backwards with a negative index. However, I don&#39;t like the impetus of advance, suggesting to me going forward is the only way. <br/>Interestingly enough it hits another point: <strong>mutability</strong>. Does advance() change the index given or not? This is not clear by the naming, but I think it should be.</p></li>
|
||||
<li><p><strong>join(a,b)</strong> and <strong>&lt;Type&gt;.join(b)</strong> <br/>
|
||||
These behave consistent but are confusing to me as hell. Compared to cocoa&#39;s <code>[NSArray componentsJoinedByString:]</code> which is clear to me. So to join [&quot;c&quot;,&quot;d&quot;,&quot;e&quot;] with &quot;-&quot; as glue you have to do either <strong>join(&quot;-&quot;,[&quot;c&quot;,&quot;d&quot;,&quot;e&quot;])</strong> or <strong>&quot;-&quot;.join([&quot;c&quot;,&quot;d&quot;,&quot;e&quot;])</strong>. I think the more intuitive version would be something like <strong>[&quot;c&quot;,&quot;d&quot;,&quot;e&quot;].joinBy(&quot;-&quot;)</strong> - I also don&#39;t get exactly why it is both a global function and a method. I would prefer the function to be defined as <strong>join(a, by: b)</strong> with the semantics reversed. Everything would be much clearer and visible to the first time reader at a glance. However, the swift library does not make use of named parameters on a function level.</p></li>
|
||||
<li><p><strong>Array.append(), Array.extend(), Array.filter(), Array.sort()</strong> <br/>
|
||||
This again hits my sore spot on <strong>mutablity</strong>. Which of these methods return a new, changed array, which do mutate the array? Without consulting the documentation or Xcode there isn&#39;t a clear indication. It turns out <strong>sort()</strong> sorts in place while <strong>filter()</strong> returns a filtered array. We need a consistent way of expressing this in the naming.</p></li>
|
||||
<li><p><strong>contains(a,b)</strong> <br/>
|
||||
Does this test if a contains b, or if a is contained in b? You can&#39;t be sure without looking at the signature. I would much rather prefer this to be something like <strong>does(a, contain: b)</strong>. and for it&#39;s brethren with the predicate <strong>doesAnyElementOf(a, satisfyPredicate: b)</strong>. However, with trailing closures being able to lose the last parameter name and just putting the closure at the end that might look awkward. But I sure as hell don&#39;t get why one should name basic functions in a short way, if there is a possible improvement in readability and less ambiguity by writing a longer signature.</p></li>
|
||||
</ul>
|
||||
</description>
|
||||
<pubDate>Sat, 5 Jul 2014 08:30:39 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2014/07/05/swift-library-naming-warts</link>
|
||||
<guid>cocoa-dom:swift-library-naming-warts</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>Autolayout and Complexity</title>
|
||||
<description><p>I recently dipped my toes into some autolayout on both iOS and OS X. Short term verdict: powerful, but too complex for daily use. And that&#39;s an issue.</p>
|
||||
|
||||
<p>Remember the times of handling touches before there were <code>UIGestureRecognizers</code>? That is exactly the state autolayout is in. The underlying technology is great but it is in desperate need of a good abstraction layer on top of it that makes the standard use cases easy and maintainable. I&#39;m looking forward to this year&#39;s WWDC to see some of these aspects to be addressed.</p>
|
||||
</description>
|
||||
<pubDate>Wed, 5 Feb 2014 04:31:39 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2014/02/05/autolayout-and-complexity</link>
|
||||
<guid>cocoa-dom:autolayout-and-complexity</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>Blocks need considering</title>
|
||||
<description><p>While I don&#39;t agree with <a href="http://sealedabstract.com/code/nsnotificationcenter-with-blocks-considered-harmful/">this post</a> in most respects, it still alerted me to a crucial thing when using blocks: Be sure the Macros you use in them don&#39;t reference self directly. And if they do and you need to be sure to not create a retain cycle, use either <code>@weakify</code> and <code>@strongify</code> from <a href="https://github.com/jspahrsummers/libextobjc">libextobjc</a> or do shadow self yourself this way:</p>
|
||||
<div class="highlight"><pre class="highlight objective_c"><code><span class="n">__weak</span> <span class="n">typeof</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="n">weakSelf</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
|
||||
|
||||
<span class="c1">// some api that takes blocks, when you give the block do </span>
|
||||
<span class="o">^</span><span class="p">{</span>
|
||||
<span class="n">__strong</span> <span class="n">typeof</span><span class="p">(</span><span class="n">weakSelf</span><span class="p">)</span> <span class="n">self</span> <span class="o">=</span> <span class="n">weakSelf</span><span class="p">;</span>
|
||||
<span class="p">}</span>
|
||||
</code></pre></div>
|
||||
<p>So thanks for ranting so much to alert me to the fact that <code>NSAssert()</code> does reference self directly, and as such make the block retain self.</p>
|
||||
|
||||
<p><strong>Update:</strong> <a href="http://www.cimgf.com/2010/05/02/my-current-prefix-pch-file/">ZAssert</a> seems to be a reasonable replacement for <code>NSAssert()</code> in blocks which is mentioned in the comments of the Article.</p>
|
||||
|
||||
<p><strong>Update 2:</strong> Since this is such a common pattern, I&#39;ve seen people using <code>welf</code> instead of <code>weakSelf</code>, which makes me chuckle. Also the <code>__typeof__</code> can be replaced by <code>typeof</code>, which makes it less atrocious.</p>
|
||||
</description>
|
||||
<pubDate>Wed, 20 Nov 2013 19:17:00 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2013/11/20/blocks-need-considering</link>
|
||||
<guid>cocoa-dom:blocks-need-considering</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>iOS 7 Game Center Sandbox</title>
|
||||
<description><p><a href="http://ishamrock.com/post/66808377466/beware-of-canceling-the-ios-7-game-center-sandbox-login">Great advice and bug report</a> from Patrick McCarron. Ran into it several times. Disabled Game Center for debug builds because of that (and because of the annoyingly slow login dialog when switching simulators).</p>
|
||||
</description>
|
||||
<pubDate>Tue, 12 Nov 2013 17:38:49 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2013/11/12/ios-game-center-sandbox</link>
|
||||
<guid>cocoa-dom:ios-game-center-sandbox</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>objc.io</title>
|
||||
<description><p>If you haven&#39;t seen it yet, you have to check it out. <a href="http://www.objc.io/">objc.io</a> offers fantastic in depth articles about all things objc.</p>
|
||||
|
||||
<p><strong>Update:</strong> An <a href="http://www.objc.io/subscribe.html">iOS newsstand subscription</a> is now avaiable - I highly recommend supporting this publication by purchasing it!</p>
|
||||
</description>
|
||||
<pubDate>Sat, 19 Oct 2013 11:33:00 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2013/10/19/objc-io</link>
|
||||
<guid>cocoa-dom:objc-io</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>New thing I do in code</title>
|
||||
<description><p>Ever since I found out that a GCC C extension causes a code block to return a value if you enclose it in round brackets, I&#39;ve been using it in my code. What do you think?</p>
|
||||
<div class="highlight"><pre class="highlight objective_c"><code><span class="n">self</span><span class="p">.</span><span class="n">bounds</span> <span class="o">=</span> <span class="p">({</span>
|
||||
<span class="n">CGRect</span> <span class="n">bounds</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">bounds</span><span class="p">;</span>
|
||||
<span class="n">bounds</span><span class="p">.</span><span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">currentYPosition</span> <span class="o">+</span> <span class="n">SHEETINSETY</span><span class="p">;</span>
|
||||
<span class="n">bounds</span><span class="p">;</span>
|
||||
<span class="p">});</span>
|
||||
</code></pre></div>
|
||||
<p>I&#39;m also using this for frame. The advantage is that with this construct I never forget to set the frame after altering it, which I did far too often otherwise.</p>
|
||||
<div class="highlight"><pre class="highlight objective_c"><code><span class="n">self</span><span class="p">.</span><span class="n">helpButton</span> <span class="o">=</span> <span class="p">({</span> <span class="c1">// helpbutton</span>
|
||||
<span class="n">UIButton</span> <span class="o">*</span><span class="n">button</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIButton</span> <span class="nf">buttonWithType</span><span class="p">:</span><span class="n">UIButtonTypeCustom</span><span class="p">];</span>
|
||||
<span class="p">[</span><span class="n">button</span> <span class="nf">setImage</span><span class="p">:[</span><span class="n">UIImage</span> <span class="nf">imageNamed</span><span class="p">:</span><span class="s">@"ParentalControlQuestionMarkButton"</span><span class="p">]</span>
|
||||
<span class="nf">forState</span><span class="p">:</span><span class="n">UIControlStateNormal</span><span class="p">];</span>
|
||||
<span class="n">CGRect</span> <span class="n">buttonRect</span> <span class="o">=</span> <span class="n">innerBounds</span><span class="p">;</span>
|
||||
<span class="n">buttonRect</span><span class="p">.</span><span class="n">size</span> <span class="o">=</span> <span class="p">[</span><span class="n">button</span> <span class="nf">sizeThatFits</span><span class="p">:</span><span class="n">CGSizeMake</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">400</span><span class="p">)];</span>
|
||||
<span class="n">buttonRect</span><span class="p">.</span><span class="n">origin</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">CGRectGetMaxX</span><span class="p">(</span><span class="n">innerBounds</span><span class="p">)</span><span class="o">-</span><span class="n">CGRectGetWidth</span><span class="p">(</span><span class="n">buttonRect</span><span class="p">);</span>
|
||||
<span class="n">button</span><span class="p">.</span><span class="n">frame</span> <span class="o">=</span> <span class="n">UIEdgeInsetsInsetRect</span><span class="p">(</span><span class="n">buttonRect</span><span class="p">,</span> <span class="n">UIEdgeInsetsMake</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">10</span><span class="p">,</span> <span class="o">-</span><span class="mi">20</span><span class="p">,</span> <span class="o">-</span><span class="mi">10</span><span class="p">));</span>
|
||||
<span class="p">[</span><span class="n">button</span> <span class="nf">addTarget</span><span class="p">:</span><span class="n">self</span> <span class="nf">action</span><span class="p">:</span><span class="k">@selector</span><span class="p">(</span><span class="n">helpAction</span><span class="o">:</span><span class="p">)</span> <span class="n">forControlEvents</span><span class="o">:</span><span class="n">UIControlEventTouchUpInside</span><span class="p">];</span>
|
||||
<span class="p">[</span><span class="n">self</span> <span class="nf">addSubview</span><span class="p">:</span><span class="n">button</span><span class="p">];</span>
|
||||
<span class="n">button</span><span class="p">;</span>
|
||||
<span class="p">});</span>
|
||||
</code></pre></div>
|
||||
<p>The major benefits of this one are:</p>
|
||||
|
||||
<ul>
|
||||
<li>The instance variable in which I will store the generated object is in the first line, clearly showing what the next part of the code does. Prior to this, the assignment happend at the end.</li>
|
||||
<li>The stack variables declared and used don&#39;t pollute other code in the same function/method. I can feel free to use very generic names (view,frame,rect,button) and not get into conflict with other parts.</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Update:</strong> And it all works with CLANG due to the great <a href="http://clang.llvm.org/features.html#gcccompat">design policy of CLANG</a> to support most of the GCC extensions to maximise compatibility.</p>
|
||||
|
||||
<p><strong>Update 2:</strong> NSHipster featured this in its <a href="https://nshipster.com/new-years-2014/">New Year&#39;s 2014 post</a>. 😃🎉</p>
|
||||
</description>
|
||||
<pubDate>Fri, 26 Jul 2013 10:19:00 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2013/07/26/new-thing-i-do-in-code</link>
|
||||
<guid>cocoa-dom:new-thing-i-do-in-code</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>NSHipster</title>
|
||||
<description><p>If you have anything to do with Cocoa: Go ahead and go to <a href="http://nshipster.com">NSHipster</a> now, if you haven&#39;t done so. I can&#39;t stress enough how good this site is in revealing great Stuff about Objective-C and the Cocoa and UIKit frameworks. Just so well done.</p>
|
||||
</description>
|
||||
<pubDate>Wed, 30 Jan 2013 04:32:42 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2013/01/30/nshipster</link>
|
||||
<guid>cocoa-dom:nshipster</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>Turn Based Gaming - Game Center Style Part #1</title>
|
||||
<description><p>Some of you may have noticed that we released <a href="http://lostcitiesapp.com/">Lost Cities</a> in the end of August. Apart from being our first card game (we did <a href="http://carcassonneapp.com/">Carcassonne</a> prior to that), it is also our first time to use the Turn Based API that Game Center provides starting with iOS 5.0. This sadly has proven a very challenging thing to do, and I want to part with some of the major gripes we encountered in this multipart series.</p>
|
||||
|
||||
<h3>Game Center’s idea of Push Notifications</h3>
|
||||
|
||||
<p>Push notifications are something iOS users are pretty familiar with now. They also made the hop to the desktop in Mountain Lion. Every app can send push notifications to the user in appropriate moments, if the user gives their permission. Users can even customize them on a per app basis and set if they want them to appear as alerts, banners and/or in the lock screen, if they have sound or not or they should badge the App Icon.</p>
|
||||
|
||||
<p>All right, these are nice and understandable, already a little bit complicated – but okay. And if you are using the Game Center Turn Based API you get push notifications too. Isn’t that nice? Or so it seems. The Game Center Push Notifications sadly have some strangeness to them:</p>
|
||||
|
||||
<ul>
|
||||
<li><p><strong>They aren’t yours</strong> – meaning they do not belong to the app sending it. They appear with the Game Center Icon and your App’s Name. The push notifications appear in Quotes to discern them from other Game Center Notifications. If you can search the web for <a href="http://www.alexcurylo.com/blog/2012/01/02/tip-custom-game-center-sound/">undocumented features</a> you can change the built in fanfare to your app’s sound. That’s it. (rdar://<span>10177685)</p></li>
|
||||
<li><p><strong>They are all or nothing</strong> – your users can’t set the notification settings for your app, they have to set the one’s for game center. You read right. You can’t turn them on or off for specific games, or even discern them from the Game Center’s friend requests. You want a Sound for this game notification? Then you also have to live with the friend request fanfare. You have a game you play only on your iPhone and not on your iPad? Too bad for you, can’t tell the iPad not to show the notifications. (rdar://10177685)</p></li>
|
||||
<li><p><strong>Invitations to games appear during your game</strong> - yes that too. If you are invited to a new game, while already in that game’s app, you get the invite notification banner shown inside your app. Also: there is no sane way of showing the invite text to you other than show in notification center, as we the app devs don’t have access to it. We don’t even get a notification.</p></li>
|
||||
<li><p><strong>No extra payload and early access for you</strong> - with normal push notifications you, as an app, can add a little bit of extra payload to that notification which you get instantly when your app is moved into foreground or started. This way you can prepare the correct UI for the Notification the user swiped upon while you sneakily load the data you need in addition that in the background to give the fastest experience. Not so with Game Center. With Game Center you aren’t told at all that the user swiped on a notification to start your app. The earliest you can guess that the user swiped a notification is in the turn based delegate callback, which only fires after the Game Center Greeting Banner (you know the one) is shown in your app, which can be up to 30 seconds in bad networking conditions. And which also fires if you start the app and a turn is received during that time. So we more or less have to guess if the user swiped a notification instead of knowing. (rdar://10177254)</p></li>
|
||||
<li><p><strong>They aren’t localizable</strong> – yes you read right – there is no sane way of localizing the Notifications. There isn’t even a proper way to set the notification text directly. (rdar://9581651)</p>
|
||||
|
||||
<p>You can either chose to not set a custom text in which case the recipient(s) of the push notification get a message of “Your Turn” localized in their iOS Language. But then you have no additional information about the game in progress, no opponents, no turn order, no score, no whatsoever.</p>
|
||||
|
||||
<p>Or the participant that made the last move may set a text to be sent instead. This is the text that will be displayed in quotes in the notification. E.g. we chose to set “20 cards against Monkeydom”. This is the Status text of the game. Which poses a problem if you use the standard Game Center Turn based UI, as it appears in that list as status of the user that made the turn. So if you are using that you already have the issue of a strange display.</p>
|
||||
|
||||
<p>However if you want to go the extra mile and localize the Message then the only thing you can do is the following: The participant app that makes a move needs to anticipate the recipient of the push notification (e.g. the next player), localize the notification in their language, and set the text and make the move. This needs knowledge about the locale of the recipient, which in turn you have to transport in the game data. We chose that approach, but it was a serious pain.</p></li>
|
||||
</ul>
|
||||
|
||||
<p>Although I’m sure some of the issues are addressed in iOS 6, I already know of some that aren’t. Which in turn means one more year until we can hope for improvements again.</p>
|
||||
</description>
|
||||
<pubDate>Tue, 4 Sep 2012 16:00:20 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2012/09/04/turn-based-gaming-game-center-style-part</link>
|
||||
<guid>cocoa-dom:turn-based-gaming-game-center-style-part</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>New Objective-C Language features in Xcode 4.4</title>
|
||||
<description><blockquote>
|
||||
<p>“One of the reasons that you may want to upgrade to Xcode 4.4 is that it includes Apple LLVM compiler 4.0 that adds a number of new Objective-C Language features. Even better most of the new features are backwardly compatibile with older versions of iOS. To make is easier to understand which features are available for which tool and OS releases Apple has published a useful Objective-C Feature Availability Index.”</p>
|
||||
</blockquote>
|
||||
|
||||
<p><em>Great summary on useyourloaf.com about <a href="http://useyourloaf.com/blog/2012/08/01/property-synthesis-with-xcode-4-dot-4.html">Automatic Property Synthesis With Xcode 4.4</a></em></p>
|
||||
<p><a href="https://coding.monkeydom.de/posts/2012/08/01/new-objective-c-language-features-in-xcode-4-4">⚓</a></p></description>
|
||||
<pubDate>Wed, 1 Aug 2012 21:16:00 +0000</pubDate>
|
||||
<link>http://useyourloaf.com/blog/2012/08/01/property-synthesis-with-xcode-4-dot-4.html</link>
|
||||
<guid>cocoa-dom:new-objective-c-language-features-in-xcode-4-4</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>iPad Mini</title>
|
||||
<description><p>Just to go on the record: If and when an iPad mini comes I predict it will have the 1024x768 @1x resolution cramped into that smaller display area. It will have slightly higher DPI than the original iPad/iPhone, but it won&#39;t be Retina:</p>
|
||||
|
||||
<ul>
|
||||
<li>All iPad apps will just work. </li>
|
||||
<li>iPad apps that disregarded Human Interface Guidlines and made their buttons too small will have issues. </li>
|
||||
<li>All others will be a little bit more intricate to handle than on the iPad/iPad2, but be fine overall.</li>
|
||||
</ul>
|
||||
</description>
|
||||
<pubDate>Thu, 5 Jul 2012 03:05:35 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2012/07/05/ipad-mini</link>
|
||||
<guid>cocoa-dom:ipad-mini</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>Games, iCloud and Game Center</title>
|
||||
<description><p>There is a dilemma Game Developers face on iOS in terms of synced game state across multiple devices. This also applies to games that you deleted from the device once and want to continue playing some time in the future.</p>
|
||||
|
||||
<p>Apple&#39;s recommended solution is to use the iCloud. However the iCloud storage is not tied to your Game Center account. This causes some major problems:</p>
|
||||
|
||||
<ul>
|
||||
<li>Somebody logged into Game Center on a device that does not have his iCloud account simply doesn&#39;t get his/her save games / game state (little known fact: you can log into the game center on any device using your account)</li>
|
||||
<li>Since the iCloud account and the Game Center account are totally independent, there is no way of telling the user about this fact gracefully, so you end up just creating a new game state for this game center id on this iCloud storage, everyone is confused.</li>
|
||||
<li>You essentially have to design your app around the fact that the user might have an iCloud account or not, and might have a Game Center account and not, and any combination of iCloud account and Game Center account, and work fine and predictable in every case. This adds very unnecessary complexity to quite a simple problem: taking care of the users progress in the best possible way.</li>
|
||||
</ul>
|
||||
|
||||
<p>However, there would be a simple solution to this dilemma: make Game Center provide storage that is tied to the Game Center account. That is the natural fit. Users would get what they expect. If you think so too, please file a duplicate of rdar://11263793 so we get this rather sooner than later.</p>
|
||||
|
||||
<p><em>Sadly, this is just one of the little details Apple did not figure out right with Game Center.</em></p>
|
||||
</description>
|
||||
<pubDate>Tue, 17 Apr 2012 11:31:00 +0000</pubDate>
|
||||
<link>https://coding.monkeydom.de/posts/2012/04/17/games-icloud-and-gamecenter</link>
|
||||
<guid>cocoa-dom:games-icloud-and-gamecenter</guid>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
1066
Parser/Tests/ParserTests/Resources/natasha.xml
Normal file
1066
Parser/Tests/ParserTests/Resources/natasha.xml
Normal file
File diff suppressed because it is too large
Load Diff
216
Parser/Tests/ParserTests/Resources/phpxml.rss
Normal file
216
Parser/Tests/ParserTests/Resources/phpxml.rss
Normal file
@ -0,0 +1,216 @@
|
||||
<?phpxml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<copyright>Copyright 2021 www.fcutrecht.net</copyright>
|
||||
<pubDate>Tue, 07 Sep 2021 02:47:57 +0200</pubDate>
|
||||
<lastBuildDate>Tue, 07 Sep 2021 02:47:57 +0200</lastBuildDate>
|
||||
<description>...</description>
|
||||
<category>sport</category>
|
||||
<language>nl-nl</language>
|
||||
<link>https://www.fcutrecht.net/</link>
|
||||
<title>www.fcutrecht.net voetbalnieuws</title>
|
||||
<item><title>Serdar Gözübüyük scheidsrechter bij FC Twente - FC Utrecht
|
||||
</title><link>https://www.fcutrecht.net/nieuws/serdar-g-z-b-y-k-scheidsrechter-bij-fc-twente-fc-utrecht~8805</link><description><![CDATA[<img src="/pictures/100/scheids.jpg" class="nieuws" alt="" />1️⃣ Serdar Gözübüyük
|
||||
|
||||
🚩 Joost van Zuilen
|
||||
🚩 Johan Balder
|
||||
4️⃣ Martin Pérez
|
||||
📺 Laurens Gerrets
|
||||
📺 Halis Küçükerbir
|
||||
|
||||
Fcutrecht.net houdt al enige tijd de scheidsrechterstatistieken bij op <a href="http://www.fcutrecht.net/wedstrijden/scheidsrechters" title="Scheidsrechter Statisitieken" target="_blank">Scheidsrechter-statistieken</a>
|
||||
|
||||
Hier kan je terug vinden hoe FC Utrecht onder deze scheidsrechter heeft gepresteerd (incl. strafschoppen, gele en rode kaarten)]]></description><author>webmaster@fcutrecht.net</author><pubDate>Mon, 06 Sep 2021 12:17:00 +0200</pubDate></item><item><title>Quinten Timber traint mee met het Nederlands Elftal
|
||||
</title><link>https://www.fcutrecht.net/nieuws/quinten-timber-traint-mee-met-het-nederlands-elftal~8803</link><description><![CDATA[<img src="/img/spelers/timber_tn.jpg" class="nieuws" alt="" />Quinten Timber mocht zondagochtend meetrainen met het grote Oranje. De speler van Jong Oranje werd voor eventjes overgeheveld om de juiste aantallen te krijgen. Bondscoach Louis van Gaal vond Quinten Timber de beste speler van de Jong Oranje-training en daarom koos hij hem.
|
||||
|
||||
|
||||
Een mooi moment voor de speler van FC Utrecht, ook omdat zijn tweelingbroer Jurriën ook bij de selectie zit. De 2 jongens voetbalden lang samen in de jeugd van Ajax, tot dit seizoen. Quinten ging van Ajax naar Utrecht, Jurriën bleef in Amsterdam. Zondagochtend stonden ze dus weer even samen op het veld.
|
||||
|
||||
Overigens waren de basisspelers van Oranje niet op het veld te zien. Zij zaten in de gym, een rustige training na de zege op Montenegro (4-0) zaterdagavond.]]></description><author>webmaster@fcutrecht.net</author><pubDate>Sun, 05 Sep 2021 12:53:00 +0200</pubDate></item><item><title>Nieuw forum op fcutrecht.net: vervoer gezocht / aangeboden
|
||||
</title><link>https://www.fcutrecht.net/nieuws/nieuw-forum-op-fcutrecht-net-vervoer-gezocht-aangeboden~8801</link><description><![CDATA[<img src="/img/pictures/auto_tn.jpg" class="nieuws" alt="" />FC Twente – FC Utrecht, maar 50 parkeer kaarten. Meestal zijn deze auto’s maar amper gevuld en zijn er vaak lege plekken. Mee rijden vanaf een parkeerplaats in de buurt lijkt een prima optie voor de mensen zonder een parkeerkaart.
|
||||
|
||||
Een paar FC Utrecht supporters zijn een initiatief gestart om een vrije plek in hun auto bekend te maken. Op deze manier worden uitwedstrijden nog gezelliger en gaan er meer supporters.
|
||||
|
||||
Geen parkeerkaart en wil je perse niet met de bus?
|
||||
Maak een afspraak in het nieuwe forum ‘vervoer gezocht / aangeboden’
|
||||
|
||||
Heb je een plekje over:
|
||||
Geef dit aan op het nieuwe forum ‘vervoer gezocht / aangeboden’
|
||||
]]></description><author>webmaster@fcutrecht.net</author><pubDate>Fri, 03 Sep 2021 19:53:00 +0200</pubDate></item><item><title>LIVESTREAM Jong FC Utrecht - La Louvière Centre
|
||||
</title><link>https://www.fcutrecht.net/nieuws/livestream-jong-fc-utrecht-la-louvi-re-centre~8802</link><description><![CDATA[<iframe width="560" height="315" src="https://www.youtube.com/embed/YLTOSZYCOx8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></description><author>webmaster@fcutrecht.net</author><pubDate>Fri, 03 Sep 2021 16:12:00 +0200</pubDate></item><item><title>Kaartverkoop FC Twente - FC Utrecht
|
||||
</title><link>https://www.fcutrecht.net/nieuws/kaartverkoop-fc-twente-fc-utrecht~8790</link><description><![CDATA[<img src="/img/pictures/kaartjes_tn.jpg" class="nieuws" alt="" />
|
||||
Het is voor supporters niet meer mogelijk om een parkeerkaart te bestellen.
|
||||
|
||||
Wel kan je een kaart kopen voor de autocombi, als je meerijdt met iemand die al een geldige parkeerkaart in zijn bezit heeft.
|
||||
|
||||
Er zijn nog voldoende buscombi kaarten (24,50 euro) te verkrijgen
|
||||
|
||||
Om de wedstrijd te bezoeken dienen alle bezoekers vanaf 13 jaar en ouder in het bezit te zijn van een geldige QR code (n.a.v. een negatieve test of vaccinatiebewijs). Zorg ervoor dat je ook een legimitatie bewijs bij je hebt.]]></description><author>webmaster@fcutrecht.net</author><pubDate>Thu, 02 Sep 2021 20:02:00 +0200</pubDate></item><item><title>FC Utrecht wint met 3-1 van Cercle Brugge
|
||||
</title><link>https://www.fcutrecht.net/nieuws/fc-utrecht-wint-met-3-1-van-cercle-brugge~8799</link><description><![CDATA[<img src="/img/pictures/galgenwaard_tn.jpg" class="nieuws" alt="" />Mike van der Hoorn heeft zijn eerste officieuze doelpunt voor FC Utrecht binnen sinds hij deze zomer terugkeerde in de Domstad. Tegen Cercle Brugge kopte hij de openingstreffer binnen in wat een zorgeloze oefenwedstrijd zou worden voor de formatie van trainer René Hake.
|
||||
|
||||
Hake gebruikte het duel met de nummer zestien van de Jupiler Pro League om spelers die hun basisplek kwijt zijn minuten te geven, zoals Eric Oelschlägel, Joris van Overeem en Adrián Dalmau. Enkele spelers die afgelopen weekend een basisplek hadden tegen Feyenoord (3-1), kwamen een helft in actie. Van der Hoorn was één van die spelers en hij zorgde met een rake kopbal voor de 1-0.
|
||||
|
||||
Na rust liep Utrecht verder uit, met Sander van de Streek en Mimoun Mahi - beiden net hersteld van blessures - een helft binnen de lijnen. Benaissa Benamar, die Willem Janssen verving, zorgde uit een voorzet voor de tweede Utrechtse treffer. Mohamed Mallahi maakte na een goede balverovering van Van Overeem zelfs 3-0. Tien minuten voor tijd scoorden de bezoekers nog eenmaal via Thibo Somers.
|
||||
|
||||
FC Utrecht hervat volgende week zaterdag, 11 september om 16.30 uur, de eredivisie met een uitwedstrijd bij FC Twente. De ploeg van Hake staat momenteel derde na twee overwinningen en een gelijkspel.
|
||||
|
||||
Opstelling FC Utrecht: Oelschlägel; Ter Avest (46' Mokono), Van der Hoorn (46' Van der Maarel), Janssen (46' Benamar), Warmerdam (46' Zagre); Van Overeem, Gustafson (46' Van de Streek), Van den Berg; Sylla (46' Mallahi), Dalmau, Ramselaar (46' Mahi).]]></description><author>webmaster@fcutrecht.net</author><pubDate>Thu, 02 Sep 2021 16:16:00 +0200</pubDate></item><item><title>LIVESTREAM FC Utrecht - Cercle Brugge
|
||||
</title><link>https://www.fcutrecht.net/nieuws/livestream-fc-utrecht-cercle-brugge~8798</link><description><![CDATA[<iframe width="560" height="315" src="https://www.youtube.com/embed/6o8G0M-tPfQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></description><author>webmaster@fcutrecht.net</author><pubDate>Thu, 02 Sep 2021 13:58:00 +0200</pubDate></item><item><title>MATCHDAY: FC Utrecht - Cercle Brugge
|
||||
</title><link>https://www.fcutrecht.net/nieuws/matchday-fc-utrecht-cercle-brugge~8794</link><description><![CDATA[<img src="/img/pictures/cercle-brugge_tn.png" class="nieuws" alt="" />Tickets voor oefenduel met Belgische ploeg online en in Fanshop verkrijgbaar.
|
||||
FC Utrecht speelt donderdag een vriendschappelijke wedstrijd tegen Cercle Brugge. Het duel met de ploeg uit de hoogste divisie van België begint in Stadion Galgenwaard die dag om 14.00 uur. Tickets voor het oefenduel zijn online en in de Fanshop verkrijgbaar.
|
||||
|
||||
Gouden Seizoenkaarthouders kunnen online gratis een ticket bestellen. Let op: je Gouden Seizoenkaart is géén entreebewijs die dag; dat is het kaartje dat je als Gouden Seizoenkaarthouder kosteloos kunt bestellen.
|
||||
|
||||
Wie geen Gouden Seizoenkaart van FC Utrecht heeft, kan á 5 euro een kaart aanschaffen, online of in de Fanshop.
|
||||
|
||||
De Noordtribune van Stadion Galgenwaard is geopend voor de oefenpartij tegen Cercle Brugge. Het stadion gaat donderdag om 13.00 uur open. In de promenades van de Noordtribune zijn cateringpunten geopend.
|
||||
|
||||
Voor het bezoeken van FC Utrecht – Cercle Brugge dien je naast een geldig wedstrijdticket ook in het bezit te zijn van een geldig legitimatiebewijs en een coronatoegangsbewijs. Dat laatste kan in de vorm van een vaccinatiebewijs (geldig vanaf 14 dagen na volledige vaccinatie), een herstelbewijs (maximaal 6 maanden oud) of een negatieve testuitslag, via Testen voor Toegang, van niet meer dan 24 uur oud.
|
||||
|
||||
Een coronatoegangsbewijs kun je aanmaken via de CoronaCheckApp of www.coronacheck.nl. Voor supporters tot en met 12 jaar volstaat een wedstrijdticket om toegang te krijgen tot Stadion Galgenwaard.
|
||||
|
||||
Let op: zonder een geldig wedstrijdticket, legitimatiebewijs en coronatoegangsbewijs heb je géén toegang tot Stadion Galgenwaard.
|
||||
|
||||
Parkeerkaarten zijn voor deze oefenwedstrijd niet geldig.]]></description><author>webmaster@fcutrecht.net</author><pubDate>Thu, 02 Sep 2021 12:01:00 +0200</pubDate></item><item><title>Directie donderdag:
|
||||
</title><link>https://www.fcutrecht.net/nieuws/directie-donderdag~8797</link><description><![CDATA[<iframe width="560" height="315" src="https://www.youtube.com/embed/QXySam2Fqak" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></description><author>webmaster@fcutrecht.net</author><pubDate>Thu, 02 Sep 2021 10:12:11 +0200</pubDate></item><item><title>Gyrano bedankt !
|
||||
</title><link>https://www.fcutrecht.net/nieuws/gyrano-bedankt~8796</link><description><![CDATA[<iframe width="560" height="315" src="https://www.youtube.com/embed/bVQOgFApqWQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></description><author>webmaster@fcutrecht.net</author><pubDate>Wed, 01 Sep 2021 13:18:09 +0200</pubDate></item><item><title>BREAKING Gyrano Kerk vertrekt naar Lokomotiv Moskou
|
||||
</title><link>https://www.fcutrecht.net/nieuws/breaking-gyrano-kerk-vertrekt-naar-lokomotiv-moskou~8795</link><description><![CDATA[<img src="/img/spelers/kerk_tn.jpg" class="nieuws" alt="" />Gyrano Kerk is definitief speler van Lokomotiv Moskou. De aanvaller komt over van FC Utrecht en tekent voor vier jaar in Rusland. Hij gaat, ook in Moskou, met rugnummer 7 spelen.
|
||||
|
||||
In Rusland zullen ze smakelijk gelachen hebben om de complete Deadline Day-stress in de rest van Europa, met bijvoorbeeld een deal over Luuk de Jong (naar FC Barcelona) die pas ver in de nacht bevestigd werd. Lokomotiv Moskou had beduidend minder haast met het afronden van de transfer van Gyrano Kerk. Immers loopt de transferwindow daar door tot 7 september. Een akkoord met Utrecht lag er gisteren al, maar verder namen alle partijen de tijd voor het papierwerk en de keuring.
|
||||
|
||||
Hoewel Kerk al sinds maandag in Moskou is en alle signalen voor een lucratieve transfer al een tijd op groen staan, was vanochtend pas het moment dat er witte rook kwam. Kerk tekent voor vier jaar en komt over van FC Utrecht, dat zes à zeven miljoen euro vangt voor de zelfopgeleide aanvaller, een bedrag dat door bonussen nog kan oplopen.
|
||||
|
||||
Daarmee komt er een einde aan een tijdperk met Kerk in de gelederen bij Utrecht. In 2012 stroomde hij in in de academie en in 2014 maakte hij zijn debuut in het eerste elftal. Tot vandaag was hij altijd onderdeel van Utrecht, op een jaar op huurbasis bij Helmond Sport na. Kerk speelde 175 officiële duels voor het eerste elftal van de club en kwam tot 42 doelpunten en 43 assists.]]></description><author>webmaster@fcutrecht.net</author><pubDate>Wed, 01 Sep 2021 11:52:00 +0200</pubDate></item><item><title>Hier moet een sporttas aan voldoen
|
||||
</title><link>https://www.fcutrecht.net/nieuws/hier-moet-een-sporttas-aan-voldoen~8804</link><description><![CDATA[<img src="/img/pictures/tas_tn.jpg" class="nieuws" alt="" />Wie regelmatig sport heeft behoefte aan goede en comfortabele sportspullen zoals kleding en sportschoenen, maar ook een sporttas. Die laatste moet aan bepaalde criteria voldoen. We nemen er een aantal met je door.
|
||||
We sporten wat af met z’n allen. In coronatijd zijn veel mensen gaan wandelen en hardlopen. De racefietsen en mountainbikes zijn niet aan te slepen. En dan is er nog een enorme groep die al sportte en dat alleen maar vaker is gaan doen. Een goeie zaak, want wie beweegt is gewoonweg een stuk gezonder dan wie op de bank blijft zitten.
|
||||
|
||||
Al deze (amateur)sporters gaan gekleed in de meest sportieve outfits. Het kleurrijke resultaat zien we dagelijks in de bossen, op fietspaden en in parken waar grote groepen een workout doen. Wat vaak vergeten wordt is dat een sporttas een belangrijk en functioneel onderdeel van de sportuitrusting is. Zweterige spullen in een willekeurig tasje stoppen is gewoonweg geen goed idee.
|
||||
Een goeie sporttas moet aan een aantal criteria voldoen en die noemen we hier.
|
||||
|
||||
Stevig materiaal
|
||||
Een sporttas moet vooral stevig zijn. Vaak wordt er niet zachtzinnig mee omgegaan en wordt er van alles ingepropt. De <a href="https://www.thebagstore.nl">tassen</a> worden van hot naar her geslingerd en vinden een plek in de vrije natuur, in kleedkamers, kantines en achter op fietsen. Die moeten een stootje kunnen hebben. In dit geval kies je voor materiaal als leer, ecoleer, polyester of PolyUrethaan (PU). Materialen die niet terugdeinzen voor stoten en gesleep met als gevolg een paar butsen en krassen. Het gaat in dit geval vooral om de functionaliteit.
|
||||
|
||||
Comfortabel te dragen
|
||||
Zo'n tas wordt veel gedragen en dat moet vooral comfortabel zijn. Een <a href="https://www.thebagstore.nl/tassen/laptoptassen">laptoptas</a> is een prima mogelijkheid mits er goede handvaten aan zitten en er het liefst ook een lange schouderband bij geleverd wordt. Dat geeft de mogelijkheid de tas ook over de schouder te dragen. Bovendien lukt het dan vaak ook om de tas crossbody te dragen. Dan heb je als sporter de handen vrij. Een andere mogelijkheid is de rugzak. Deze wordt veel als sporttas ingezet omdat dat ook nog eens makkelijk draagt voor wie naar de sportclub of de sportschool fietst. Een voorbeeld? Een <a href="https://www.thebagstore.nl/tassen/cowboysbag">Cowboysbag</a> rugzak is een goede mogelijkheid. Maar er zijn meer merken die prachtige sportieve rugzakken bieden. Daar kun je rustig een avondje voor gaan zitten. Online is er van alles te vinden.
|
||||
|
||||
Waterdicht en waterproof
|
||||
Kies voor een waterproof exemplaar die wat vocht en een buitje regen hebben kan. Een stap verder is de waterdichte tas, die echte plensbuien kan weerstaan. Wel zo fijn als jouw tas een tijdje langs de lijn ligt of waarmee je iedere zaterdag naar de club moet fietsen. Je spullen zitten veilig opgeborgen in een sporttas die stevig en droog blijft in natte en regenachtige weersomstandigheden.
|
||||
|
||||
Veel ruimte
|
||||
Een sporttas moet ruimte bieden, het liefst door middel van veel vakken. Een apart vak voor sportschoenen of voor vochtige kleding is natuurlijk helemaal handig. Maar ook een apart kleiner vak voor sleutels en een smartphone zorgen ervoor dat je spullen droog en schoon blijven. Vakken die goed kunnen worden afgesloten met een rits of klittenband zijn het meest handig.
|
||||
|
||||
Sportief uiterlijk
|
||||
Het oog wil natuurlijk ook wat. De sporttassen zijn er in alle mogelijke uitvoeringen. Van minimalistisch uitgevoerde tassen met een Scandinavische look, tot frivole tassen in trendy kleuren. En alles daar tussenin. Kies de tas die bij jou past, die makkelijk draagt en comfortabel is. Het is de investering waard en regelmatig kun je er eentje scoren in een tassen sale. Dan heb je voor een mooie prijs een prachtige tas te pakken waar je veel plezier aan zal beleven. Iedereen weet dat sporten nog lekkerder gaat als je leuke en stijlvolle spullen draagt! Daar hoort de sporttas ook bij.
|
||||
]]></description><author>webmaster@fcutrecht.net</author><pubDate>Wed, 01 Sep 2021 01:03:00 +0200</pubDate></item><item><title>Beginnen met het gokken op voetbal? Gebruik deze tips!
|
||||
</title><link>https://www.fcutrecht.net/nieuws/beginnen-met-het-gokken-op-voetbal-gebruik-deze-tips~8800</link><description><![CDATA[<img src="/img/pictures/veld_tn.jpg" class="nieuws" alt="" />In de afgelopen jaren zijn sportweddenschappen uit de schaduw naar de mainstream gekomen. Het anti-gokstigma dat al tientallen jaren bestond, is inmiddels voor een groot gedeelte al vervaagd. Dit zal de komende jaren alsmaar beter geworden aangezien online casino’s binnenkort eindelijk ook een Nederlandse goklicentie aan kunnen vragen.
|
||||
|
||||
Desondanks kun je op het moment ook al gokken op voetbal op het internet, wat bijvoorbeeld mogelijk is via <a href="https://www.bemybet.com/nl/">Bemybet</a> Heb je dit nog niet eerder gedaan? Gebruik dan de onderstaande tips om een succes te maken van je eerste weddenschappen!
|
||||
|
||||
<strong>Favorieten versus underdogs</strong>
|
||||
Wanneer bookmakers een inzetregel, wat ook wel een odd wordt genoemd, op een spel vrijgeven, is het eerste wat ze doen beslissen welk team de favoriet en welke de underdog moet zijn. De favoriet is het team dat naar verwachting de wedstrijd wint en hierdoor kun je weinig geld winnen als je op dit team inzet. De underdog verliest juist naar verwachting, wat ervoor zorgt dat je wel flink wat geld kunt winnen als je gokt dat deze club gaat winnen. Als er weinig verschil zit tussen de teams, zullen boekmakers het openen als een "pick" of "pick'em".
|
||||
|
||||
<strong>Over/under</strong>
|
||||
Je kunt dus onder meer geld inzetten op welke voetbalclub de wedstrijd gaat winnen. Toch zijn er nog veel meer mogelijkheden waar je gebruik van kunt maken bij het gokken op voetbalwedstrijden. Zo kun je bijvoorbeeld ook een bet plaatsen op de over/under. Dit kun je onder andere doen met het aantal doelpunten dat gescoord gaat worden tijdens de wedstrijd. In Nederland worden over het algemeen veel goals gemaakt, dus je kunt dan dus bijvoorbeeld op over 2,5 goals zetten. Dit betekent dat er minstens drie doelpunten gescoord moeten worden tijdens de wedstrijd, anders zal je bet dus verliezen.
|
||||
|
||||
<strong>Geldlijnen</strong>
|
||||
De tweede manier om te wedden op een favoriet of een underdog is op de geldlijn. Dit is uitsluitend gebaseerd op welk team het spel zal winnen en gebruikt Europese odds om de uitbetaling te berekenen. Er zijn overigens nog meer manieren waarop dit getoond kan worden, zo wordt in de Verenigde Staten en het Verenigd Koninkrijk een andere methode gebruikt. Desondanks wordt de Europese manier door de meeste Nederlanders als het handigst gezien. Hierbij krijgt een favoriet bijvoorbeeld een geldlijn van 1,09, wat betekent dat je als je een euro gokt op een overwinning van de favoriet je 1,09 euro krijgt als dit team daadwerkelijk wint.
|
||||
]]></description><author>webmaster@fcutrecht.net</author><pubDate>Tue, 31 Aug 2021 21:48:00 +0200</pubDate></item><item><title>Column: Wat een wedstrijd, wat heb ik genoten!
|
||||
</title><link>https://www.fcutrecht.net/nieuws/column-wat-een-wedstrijd-wat-heb-ik-genoten~8793</link><description><![CDATA[<img src="/img/pictures/fans_tn.jpg" class="nieuws" alt="" />De passie spatte ervan af. Feijenoord had vanaf de 5e minuut niets meer te vertellen en de uitslag had nog veel hoger kunnen zijn in ons voordeel.
|
||||
Hoe zou dat nou toch komen, Feijenoord is meestal gelijkwaardig en komt normaal gesproken toch minstens met een gelijkspel bij ons weg.
|
||||
Wanneer je naar de individuele kwaliteiten van de spelers kijkt kan dat ook niet anders, ze betalen in de maasstad aan salaris grofweg het dubbele van wat wij kunnen betalen dus als ze een leuke speler zien kunnen ze hem vaak “gewoon” ophalen.
|
||||
|
||||
Wat is hier dan gebeurd?
|
||||
Een wedstrijd wordt niet alleen gewonnen op individuele kwaliteit, er komen ook andere dingen bij kijken, ik zie dat zo;
|
||||
Om een wedstrijd te winnen heb je 100% nodig.
|
||||
Voor het gemak verdeel ik het maar even in 4 x 25%, je kunt er ook iets andere waarden aan hangen.
|
||||
|
||||
De eerste 25% is individuele kwaliteit, reeds genoemd.
|
||||
De tweede 25% is de samenhang in het team, die kan verschillen bijvoorbeeld door onderlinge afgunst of wantrouwen wat bijvoorbeeld kan leiden tot vervelende handgebaartjes in het veld en dergelijk maar andersom kan men ook voor elkaar door het vuur willen gaan.
|
||||
|
||||
De derde 25% is het tactische plan.
|
||||
En de laatste 25% is inzet en passie.
|
||||
Wanneer je wat van bovenstaande punten mist wordt het lastig, we kennen allemaal de voorbeelden wel.
|
||||
|
||||
Mijn punt is nu het volgende; de voorbereiding tegen tamelijk sterke tegenstanders liet zien dat er kwaliteit genoeg was. En in die potjes werd er op een enkel moment na niet tot op het het bot gegaan, het was immers vriendschappelijk.
|
||||
|
||||
In Groningen werden we echter geconfronteerd met de Sirmione van Nederland, Danny Buijs, die nu aan zijn elftal heeft kunnen bouwen en op basis van de laatste 25%, inzet en passie, zijn strijdplan heeft bedacht. Hij heeft niet zoveel kwaliteit dus heeft hij van de inzet en passie een groter percentage gemaakt, prachtig! Utrecht schrok, liep constant achter de bal aan en liet zich overbluffen door de strijdlust van de tegenstander. We hielden stand want gelukkig ontwaakten de warriors in ons team en konden we een (verdiende?) nederlaag afwenden.
|
||||
|
||||
Maar het was wel een grote wake-up call!
|
||||
|
||||
En daar zit wat mij betreft de winst. De spelers en de staf beseften dat het niet goed was gegaan. En blijkbaar is er in de week naar Feijenoord toe het besef ontstaan dat het anders moest.
|
||||
|
||||
Onze strijders met om er toch maar een paar te noemen Willem Janssen, Adam Maher en ook de weer thuisgekomen Mike van der Hoorn voorop pakten de rotterdammers bij de strot en lieten niet meer los. Maar hiermee doe ik de rest van het team tekort.
|
||||
Het hele team, niet ėėn uitgezonderd, heeft alles gegeven!
|
||||
|
||||
Met een klinkende overwinning tot gevolg, de feijenoorders in verwarring achterlatend. De uitslag had nog hoger kunnen zijn.
|
||||
|
||||
Toornstra snapte het nog niet zo goed maar Til zei het al wel; Utrecht was klaar voor het gevecht en wij niet.
|
||||
Wat zal bijvoorbeeld een Janssen net na zijn prachtige goal in de rust hebben geschreeuwd naar zijn team? We kunnen het wel raden. Hoe de tweede helft werd begonnen was overweldigend!
|
||||
|
||||
En hoe daarna de invallers die moegestreden vechters kwamen vervangen datzelfde ook weer konden brengen was een topclub waardig.
|
||||
Ik ben trots supporter te zijn van deze club, deze aanpak is ònze aanpak!
|
||||
Als de staf en de spelers dit vuur erin kunnen houden gaan we nog mooie tijden beleven. Is deze beleving vol te houden, ook tegen de mindere clubs?
|
||||
|
||||
Dat is de sleutel naar succes!
|
||||
|
||||
Wij, de supporters, zijn er klaar voor!
|
||||
]]></description><author>webmaster@fcutrecht.net</author><pubDate>Tue, 31 Aug 2021 09:38:00 +0200</pubDate></item><item><title>Fotoverslag FC Utrecht - Feyenoord, nog even nagenieten
|
||||
</title><link>https://www.fcutrecht.net/nieuws/fotoverslag-fc-utrecht-feyenoord-nog-even-nagenieten~8792</link><description><![CDATA[<img src="/img/pictures/037_tn.jpg" class="nieuws" alt="" />Fcutrecht.net probeert steeds vaker te beschikken over de wedstrijd foto’s. Of een wedstrijd nou gewonnen wordt of verloren, een wedstrijd goed was of heel slecht, het is altijd leuk om nog even de foto’s terug te kijken van de afgelopen wedstrijd.
|
||||
|
||||
Daarom hebben we ook deze keer weer een fotograaf langs de lijn kunnen vinden die zijn foto’s ter beschikking stelt en de moeite neem om zijn foto’s te uploaden naar onze website.
|
||||
|
||||
Deze keer gaat onze dank uit naar Jan van Lent die de foto's ter beschikking stelt aan fcutrecht.net. <br>
|
||||
Klik op <a href="https://www.fcutrecht.net/fotoalbum/436">deze link</a> voor het fotoverslag]]></description><author>webmaster@fcutrecht.net</author><pubDate>Mon, 30 Aug 2021 14:04:00 +0200</pubDate></item><item><title>Samenvatting FC Utrecht - Feyenoord
|
||||
</title><link>https://www.fcutrecht.net/nieuws/samenvatting-fc-utrecht-feyenoord~8791</link><description><![CDATA[<iframe width="560" height="315" src="https://www.youtube.com/embed/RfxkqZmPZcM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></description><author>webmaster@fcutrecht.net</author><pubDate>Mon, 30 Aug 2021 11:29:19 +0200</pubDate></item><item><title>MATCHDAY FC Utrecht – Feyenoord #utrfey
|
||||
</title><link>https://www.fcutrecht.net/nieuws/matchday-fc-utrecht-feyenoord-utrfey~8780</link><description><![CDATA[<img src="/img/pictures/dsgb_tn.jpg" class="nieuws" alt="" />
|
||||
MATCHDAY FC Utrecht – Feyenoord, 12.15 uur
|
||||
|
||||
Vandaag staat de Eredivisie wedstrijd tegen Feyenoord op het programma. De wedstrijd staat gepland om 12.30 uur in Stadion Galgenwaard.
|
||||
|
||||
Verkoop wedstrijddag:
|
||||
Op de wedstrijddag zijn de kassa's aan de Cityside GESLOTEN, er is op de wedstrijddag geen (online) kaartverkoop.
|
||||
|
||||
Om de wedstrijd te bezoeken dienen alle bezoekers vanaf 12 jaar en ouder in het bezit te zijn van een geldige QR code (n.a.v. een negatieve test of vaccinatiebewijs).
|
||||
|
||||
Veel plezier bij deze wedstrijd, FORZA Utrecht !
|
||||
|
||||
]]></description><author>webmaster@fcutrecht.net</author><pubDate>Sun, 29 Aug 2021 00:01:00 +0200</pubDate></item><item><title>Kaartverkoop FC Utrecht - Feyenoord
|
||||
</title><link>https://www.fcutrecht.net/nieuws/kaartverkoop-fc-utrecht-feyenoord~8781</link><description><![CDATA[<img src="/img/pictures/feyenoord.gif" class="nieuws" alt="" />Voor deze wedstrijd geldt een clubkaartverplichting.
|
||||
|
||||
Seizoenkaarthouders kunnen online en in de fanshop maximaal 3 kaarten per seizoenkaart kopen.
|
||||
Clubkaarthouders kunnen online en in de fanshop maximaal 1 kaart per clubkaart kopen.
|
||||
|
||||
Op de wedstrijddag zijn de kassa's aan de Cityside GESLOTEN, er is op de wedstrijddag geen (online) kaartverkoop.
|
||||
|
||||
Internetverkoop:
|
||||
Einde verkoop 28 augustus, 23.59 uur
|
||||
|
||||
Verkoop in FC Utrecht Fanshop:
|
||||
Einde verkoop 28 augustus, 17.00 uur
|
||||
|
||||
Om de wedstrijd te bezoeken dienen alle bezoekers vanaf 13 jaar en ouder in het bezit te zijn van een geldige QR code (n.a.v. een negatieve test of vaccinatiebewijs).]]></description><author>webmaster@fcutrecht.net</author><pubDate>Sat, 28 Aug 2021 01:00:00 +0200</pubDate></item><item><title>MATCHDAY Jong FC Utrecht – Telstar
|
||||
</title><link>https://www.fcutrecht.net/nieuws/matchday-jong-fc-utrecht-telstar~8779</link><description><![CDATA[<img src="/pictures/100/jutr_thuis.jpg" class="nieuws" alt="" />
|
||||
MATCHDAY Jong FC Utrecht – Telstar - 20.00 uur
|
||||
|
||||
Vandaag staat de Keuken Kampioen Divisie wedstrijd tegen Telstar op het programma. De wedstrijd staat gepland om 20.00 uur op Sportpark Zoudenbalch.
|
||||
|
||||
De kaartverkoop is inmiddels gestopt. De kassa's op het sportcomplex Zoudenbalch zijn gesloten.
|
||||
|
||||
Wil je de wedstrijd toch volgen; luister dan naar Radio M of bekijken de wedstrijd op ESPN.
|
||||
|
||||
Veel plezier bij deze wedstrijd, FORZA Utrecht !
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
]]></description><author>webmaster@fcutrecht.net</author><pubDate>Fri, 27 Aug 2021 21:01:00 +0200</pubDate></item><item><title>Tijdstip FC Utrecht - Feyenoord blijft op 12.15 uur
|
||||
</title><link>https://www.fcutrecht.net/nieuws/tijdstip-fc-utrecht-feyenoord-blijft-op-12-15-uur~8786</link><description><![CDATA[<img src="/img/pictures/feyenoord.gif" class="nieuws" alt="" />Feyenoord heeft geprobeerd om het duel met FC Utrecht van komende zondag te verplaatsen. De Rotterdammers moeten al om 12.15 uur aantreden in de Eredivisie, terwijl het donderdagavond in de Conference League nog de verre uitwedstrijd in Zweden speelde tegen IF Elfsborg. Toch is er geen groen licht gekomen om de wedstrijd uit te stellen.
|
||||
|
||||
Dat schrijft RTV Rijnmond althans. 'Het verzoek later af te trappen of de wedstrijd te verschuiven is afgewezen, omdat de gemeente Utrecht zegt dat dit qua politie-inzet niet te regelen was', weet het regionale medium. Welke rol de KNVB gespeeld heeft in de keuzes om de wedstrijd op dit tijdstip te houden, is onduidelijk. Vorig seizoen was er in de play-offs ook al ontzettend veel te doen over het moment van voetballen tussen beide clubs.
|
||||
|
||||
De Nederlandse voetbalbond had aangegeven dat er in principe meegewerkt wordt bij dergelijke verzoeken van Europees spelende clubs. Eerder in deze periode werd een wedstrijd van AZ geschrapt om de Alkmaarders een goede voorbereiding te geven op het duel in de Europa League. Enkele weken geleden gaf Slot aan dat hij graag later op de dag zou spelen en ervanuit ging dat het zou lukken, maar het plaatje is uiteindelijk niet rondgekregen.]]></description><author>webmaster@fcutrecht.net</author><pubDate>Fri, 27 Aug 2021 09:25:00 +0200</pubDate></item>
|
||||
</channel>
|
||||
</rss>
|
249
Parser/Tests/ParserTests/Resources/pxlnv.json
Normal file
249
Parser/Tests/ParserTests/Resources/pxlnv.json
Normal file
File diff suppressed because one or more lines are too long
1189
Parser/Tests/ParserTests/Resources/rose.json
Normal file
1189
Parser/Tests/ParserTests/Resources/rose.json
Normal file
File diff suppressed because one or more lines are too long
182
Parser/Tests/ParserTests/Resources/russcox.atom
Normal file
182
Parser/Tests/ParserTests/Resources/russcox.atom
Normal file
File diff suppressed because one or more lines are too long
443
Parser/Tests/ParserTests/Resources/scriptingNews.rss
Normal file
443
Parser/Tests/ParserTests/Resources/scriptingNews.rss
Normal file
@ -0,0 +1,443 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- RSS generated by oldSchool v0.42c on Mon, 26 Jun 2017 19:41:48 GMT -->
|
||||
<rss version="2.0" xmlns:source="http://source.scripting.com/">
|
||||
<channel>
|
||||
<title>Scripting News</title>
|
||||
<link>http://scripting.com/</link>
|
||||
<description>Scripting News, the weblog started in 1997 that bootstrapped the blogging revolution.</description>
|
||||
<pubDate>Mon, 26 Jun 2017 19:40:58 GMT</pubDate>
|
||||
<language>en-us</language>
|
||||
<generator>oldSchool v0.42c</generator>
|
||||
<copyright>&copy; 1994-2017 <a href="http://davewiner.com/">Dave Winer</a>.</copyright>
|
||||
<docs>http://cyber.law.harvard.edu/rss/rss.html</docs>
|
||||
<lastBuildDate>Mon, 26 Jun 2017 19:41:48 GMT</lastBuildDate>
|
||||
<cloud domain="rpc.rsscloud.io" port="5337" path="/pleaseNotify" registerProcedure="" protocol="http-post" />
|
||||
<source:account service="twitter">davewiner</source:account>
|
||||
<source:account service="facebook">dave.winer.12</source:account>
|
||||
<source:account service="github">scripting</source:account>
|
||||
<source:account service="linkedin">scripting</source:account>
|
||||
<source:localTime>Mon, June 26, 2017 3:41 PM EDT</source:localTime>
|
||||
<item>
|
||||
<description>Good morning students and teachers! 🍏</description>
|
||||
<pubDate>Mon, 26 Jun 2017 12:20:05 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/26.html#a080605</link>
|
||||
<guid>http://scripting.com/2017/06/26.html#a080605</guid>
|
||||
<source:outline text="Good morning students and teachers! :green_apple:" type="outline" created="Mon, 26 Jun 2017 12:20:05 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>This is the <a href="https://twitter.com/kennethn/status/879367091953868801">human side</a> of <a href="http://scripting.com/2017/03/08/theWorldIsSocialistPartIi.html">Health care is socialist</a>.</description>
|
||||
<pubDate>Mon, 26 Jun 2017 19:40:58 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/26.html#a030658</link>
|
||||
<guid>http://scripting.com/2017/06/26.html#a030658</guid>
|
||||
<source:outline text="This is the <a href="https://twitter.com/kennethn/status/879367091953868801">human side</a> of <a href="http://scripting.com/2017/03/08/theWorldIsSocialistPartIi.html">Health care is socialist</a>." type="outline" created="Mon, 26 Jun 2017 19:40:58 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Interesting Politico <a href="http://www.politico.com/magazine/story/2017/06/26/trump-president-style-mayor-215294">piece</a> posits that Trump acts as if he's mayor of the United States. If <a href="https://en.wikipedia.org/wiki/Mayor_of_New_York_City">NYC</a> is his model, that mayor is esp powerless, because the governor of the state also has a lot of power over the city. It's approx 1/2 of the population of the state, and probably much more than 1/2 of the money. For example, the <a href="https://en.wikipedia.org/wiki/Metropolitan_Transportation_Authority">MTA</a>, which runs the buses and subway, is run by the state, not the city.</description>
|
||||
<pubDate>Mon, 26 Jun 2017 18:10:04 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/26.html#a020604</link>
|
||||
<guid>http://scripting.com/2017/06/26.html#a020604</guid>
|
||||
<source:outline text="Interesting Politico <a href="http://www.politico.com/magazine/story/2017/06/26/trump-president-style-mayor-215294">piece</a> posits that Trump acts as if he's mayor of the United States. If <a href="https://en.wikipedia.org/wiki/Mayor_of_New_York_City">NYC</a> is his model, that mayor is esp powerless, because the governor of the state also has a lot of power over the city. It's approx 1/2 of the population of the state, and probably much more than 1/2 of the money. For example, the <a href="https://en.wikipedia.org/wiki/Metropolitan_Transportation_Authority">MTA</a>, which runs the buses and subway, is run by the state, not the city." type="outline" created="Mon, 26 Jun 2017 18:10:04 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description><a href="https://github.com/scripting/Scripting-News/issues/12">Brent asks</a> if the <i>length</i> in enclosures in <a href="https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md">RSS-in-JSON</a> is a number or string. That's what the test podcast <a href="http://scripting.com/2017/06/26.html#a010633">below</a> is for.</description>
|
||||
<pubDate>Mon, 26 Jun 2017 18:05:02 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/26.html#a020602</link>
|
||||
<guid>http://scripting.com/2017/06/26.html#a020602</guid>
|
||||
<source:outline text="<a href="https://github.com/scripting/Scripting-News/issues/12">Brent asks</a> if the <i>length</i> in enclosures in "RSS-in-JSON" is a number or string. That's what the test podcast <a href="http://scripting.com/2017/06/26.html#a010633">below</a> is for." type="outline" created="Mon, 26 Jun 2017 18:05:02 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>From time to time I have to do a podcast to test things out. This is one of those times. Let's see what happens.</description>
|
||||
<pubDate>Mon, 26 Jun 2017 17:29:33 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/26.html#a010633</link>
|
||||
<guid>http://scripting.com/2017/06/26.html#a010633</guid>
|
||||
<enclosure url="http://scripting.com/2017/06/26/yetAnotherTestPodcast.m4a" type="audio/mpeg" length="277413"/>
|
||||
<source:outline text="From time to time I have to do a podcast to test things out. This is one of those times. Let's see what happens." type="outline" created="Mon, 26 Jun 2017 17:29:33 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Body shaming is wrong no matter who you're using as the example. Someone is being hurt by <a href="https://twitter.com/xeni/status/879354857374732288">this</a>. No, I don't care how much you have suffered.</description>
|
||||
<pubDate>Mon, 26 Jun 2017 15:26:03 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/26.html#a110603</link>
|
||||
<guid>http://scripting.com/2017/06/26.html#a110603</guid>
|
||||
<source:outline text="Body shaming is wrong no matter who you're using as the example. Someone is being hurt by <a href="https://twitter.com/xeni/status/879354857374732288">this</a>. No, I don't care how much you have suffered." type="outline" created="Mon, 26 Jun 2017 15:26:03 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>I need <a href="https://github.com/scripting/xmlViewer/blob/master/xmlviewer.js">an app</a> to <a href="http://xmlviewer.scripting.com/?url=http://scripting.com/rss.xml">view</a> RSS feeds in the browser because Chrome and Safari refuse to let me do that. I'd love to hear the reason why.</description>
|
||||
<pubDate>Mon, 26 Jun 2017 13:24:16 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/26.html#a090616</link>
|
||||
<guid>http://scripting.com/2017/06/26.html#a090616</guid>
|
||||
<source:outline text="I need <a href="https://github.com/scripting/xmlViewer/blob/master/xmlviewer.js">an app</a> to <a href="http://xmlviewer.scripting.com/?url=http://scripting.com/rss.xml">view</a> RSS feeds in the browser because Chrome and Safari refuse to let me do that. I'd love to hear the reason why." type="outline" created="Mon, 26 Jun 2017 13:24:16 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<title>Subscribable feed lists give power to users</title>
|
||||
<description><p>An interesting <a href="https://github.com/kylewm/woodwind/issues/7#issuecomment-311000922">comment</a> from <a href="https://github.com/chrisaldrich">Chris Aldrich</a> about subscribing to lists of feeds in a thread on the <a href="https://github.com/kylewm/woodwind">Woodwind</a> app site on GitHub. </p> <p>Here's the basic idea. There's a difference between importing OPML into a reader and subscribing to it. The latter is very powerful, for the user, but a lot of RSS reader devs may not want their users to have that much power. It's not a very hard feature to implement. </p> <p>The idea has been <a href="https://duckduckgo.com/?q=site%3Ascripting.com+%22reading+list%22&t=hz&ia=web">much-discussed</a> here. We call them reading lists. Michael Arrington even wrote a <a href="https://techcrunch.com/2005/10/16/my-thoughts-on-reading-lists/">TechCrunch piece</a> about it in 2005. </p> <p>Subscribable OPML is something all my readers have been able to do through an OPML feature called <a href="http://dev.opml.org/spec2.html#inclusion">inclusion</a>. I wrote a <a href="https://github.com/scripting/river5/blob/master/docs/DROPBOXSUBSCRIPTIONLISTS.md">howto</a> for a <a href="https://github.com/scripting/river5">River5</a> user re inclusion just last week.</p> <p><a href="https://techcrunch.com/2006/05/07/share-your-opml/">Share Your OPML</a> was a service I operated for a while. It made it possible to manage your OPML separate from the reader you used. It was meant to encourage readers to support subscribable OPML. I'm looking for an excuse to bring it back, but first we need a base of shared feed lists. </p> <p>A lot of good stuff can be done if feed readers are willing to delegate list management to other services. IMHO the only reason a reader developer <i>wouldn't</i> do it is because they want to lock users in. If I let you edit your feed list elsewhere that means you could give the list to another vendor and have a choice which to use. It's really something users should demand, esp if you're paying for the service. </p> </description>
|
||||
<pubDate>Mon, 26 Jun 2017 12:24:36 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/26.html#a080636</link>
|
||||
<guid>http://scripting.com/2017/06/26.html#a080636</guid>
|
||||
<source:outline text="Subscribable feed lists give power to users" type="outline" created="Mon, 26 Jun 2017 12:24:36 GMT">
|
||||
<source:outline text="An interesting <a href="https://github.com/kylewm/woodwind/issues/7#issuecomment-311000922">comment</a> from <a href="https://github.com/chrisaldrich">Chris Aldrich</a> about subscribing to lists of feeds in a thread on the <a href="https://github.com/kylewm/woodwind">Woodwind</a> app site on GitHub." created="Mon, 26 Jun 2017 12:24:49 GMT"/>
|
||||
<source:outline text="Here's the basic idea. There's a difference between importing OPML into a reader and subscribing to it. The latter is very powerful, for the user, but a lot of RSS reader devs may not want their users to have that much power. It's not a very hard feature to implement." created="Mon, 26 Jun 2017 12:40:32 GMT"/>
|
||||
<source:outline text="The idea has been <a href="https://duckduckgo.com/?q=site%3Ascripting.com+%22reading+list%22&t=hz&ia=web">much-discussed</a> here. We call them reading lists. Michael Arrington even wrote a <a href="https://techcrunch.com/2005/10/16/my-thoughts-on-reading-lists/">TechCrunch piece</a> about it in 2005." created="Mon, 26 Jun 2017 12:26:01 GMT"/>
|
||||
<source:outline text="Subscribable OPML is something all my readers have been able to do through an OPML feature called <a href="http://dev.opml.org/spec2.html#inclusion">inclusion</a>. I wrote a <a href="https://github.com/scripting/river5/blob/master/docs/DROPBOXSUBSCRIPTIONLISTS.md">howto</a> for a "River5" user re inclusion just last week." created="Mon, 26 Jun 2017 12:27:15 GMT"/>
|
||||
<source:outline text="<a href="https://techcrunch.com/2006/05/07/share-your-opml/">Share Your OPML</a> was a service I operated for a while. It made it possible to manage your OPML separate from the reader you used. It was meant to encourage readers to support subscribable OPML. I'm looking for an excuse to bring it back, but first we need a base of shared feed lists." created="Mon, 26 Jun 2017 12:33:10 GMT"/>
|
||||
<source:outline text="A lot of good stuff can be done if feed readers are willing to delegate list management to other services. IMHO the only reason a reader developer <i>wouldn't</i> do it is because they want to lock users in. If I let you edit your feed list elsewhere that means you could give the list to another vendor and have a choice which to use. It's really something users should demand, esp if you're paying for the service." created="Mon, 26 Jun 2017 12:47:13 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<description>Good morning Internet guzzlers! 🍺</description>
|
||||
<pubDate>Sun, 25 Jun 2017 12:27:31 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/25.html#a080631</link>
|
||||
<guid>http://scripting.com/2017/06/25.html#a080631</guid>
|
||||
<source:outline text="Good morning Internet guzzlers! :beer:" type="outline" created="Sun, 25 Jun 2017 12:27:31 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>So glad I stopped worrying <a href="https://duckduckgo.com/?q=site%3Ascripting.com+facebook&t=hz&ia=web">about</a> Facebook and am now blogging <i>Old School</i> style on <a href="http://scripting.com/">scripting.com</a>. I've found my <a href="https://en.wikipedia.org/wiki/Sea_legs">sea legs</a> once again.</description>
|
||||
<pubDate>Sun, 25 Jun 2017 12:43:06 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/25.html#a080606</link>
|
||||
<guid>http://scripting.com/2017/06/25.html#a080606</guid>
|
||||
<source:outline text="So glad I stopped worrying <a href="https://duckduckgo.com/?q=site%3Ascripting.com+facebook&t=hz&ia=web">about</a> Facebook and am now blogging <i>Old School</i> style on <a href="http://scripting.com/">scripting.com</a>. I've found my <a href="https://en.wikipedia.org/wiki/Sea_legs">sea legs</a> once again." type="tweet" created="Sun, 25 Jun 2017 12:43:06 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Dan Shafer <a href="https://www.facebook.com/vicky.elder.77/posts/10211645108716478?pnref=story">died</a>. I knew Dan from the Mac developer community in the 80s, hired <a href="https://en.wikipedia.org/wiki/Dan_Shafer">him</a> to write the first docs for <a href="http://en.wikipedia.org/wiki/UserLand_Software">Frontier</a>. Bon voyage mi amigo! 💥</description>
|
||||
<pubDate>Sun, 25 Jun 2017 12:56:23 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/25.html#a080623</link>
|
||||
<guid>http://scripting.com/2017/06/25.html#a080623</guid>
|
||||
<source:outline text="Dan Shafer <a href="https://www.facebook.com/vicky.elder.77/posts/10211645108716478?pnref=story">died</a>. I knew Dan from the Mac developer community in the 80s, hired <a href="https://en.wikipedia.org/wiki/Dan_Shafer">him</a> to write the first docs for "Frontier". Bon voyage mi amigo! :boom:" type="outline" created="Sun, 25 Jun 2017 12:56:23 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Money was a big issue yesterday in the nascent tech blogosphere. First, you do this because you love it, not because it pays well. (It doesn't pay at all.) Now I'd like to take you back to a discsussion that was had many years ago that resulted in this conclusion. You don't make money from this work, but it leads to opportunities where you can make money. Ideas and information make their way to you and if you are so-inclined you can make money by investing in those ideas. No sure things, but some bloggers have made billions, and others have made millions. And others have made a decent living. Not <i>from</i> their blog but <i>because</i> they blog.</description>
|
||||
<pubDate>Sun, 25 Jun 2017 12:29:24 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/25.html#a080624</link>
|
||||
<guid>http://scripting.com/2017/06/25.html#a080624</guid>
|
||||
<source:outline text="Money was a big issue yesterday in the nascent tech blogosphere. First, you do this because you love it, not because it pays well. (It doesn't pay at all.) Now I'd like to take you back to a discsussion that was had many years ago that resulted in this conclusion. You don't make money from this work, but it leads to opportunities where you can make money. Ideas and information make their way to you and if you are so-inclined you can make money by investing in those ideas. No sure things, but some bloggers have made billions, and others have made millions. And others have made a decent living. Not <i>from</i> their blog but <i>because</i> they blog." type="outline" created="Sun, 25 Jun 2017 12:29:24 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>In 2015 I <a href="http://scripting.com/2015/06/25/dropboxCouldBeKingOfTheOnepageApp.html">wrote</a> that Dropbox could be the king of the one-page app. Because storage is the thing the web doesn't, on its own, do, and storage is the thing Dropbox does best. And they have an API, and they understood the connection to one-page-apps earlier than anyone. But it didn't happen. I've emailed with people at Dropbox from time to time and the best explanation I can come up with is that they are focused in different areas. It seems to me, from my outside perch, that they are trying to become a competitor to <a href="https://www.google.com/docs/about/">Google's</a> and <a href="https://en.wikipedia.org/wiki/Microsoft_Office">Microsoft's</a> Office products. I was hoping they'd become a platform, focusing on distribution and investment in startups.</description>
|
||||
<pubDate>Sun, 25 Jun 2017 12:32:31 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/25.html#a080631</link>
|
||||
<guid>http://scripting.com/2017/06/25.html#a080631</guid>
|
||||
<source:outline text="In 2015 I <a href="http://scripting.com/2015/06/25/dropboxCouldBeKingOfTheOnepageApp.html">wrote</a> that Dropbox could be the king of the one-page app. Because storage is the thing the web doesn't, on its own, do, and storage is the thing Dropbox does best. And they have an API, and they understood the connection to one-page-apps earlier than anyone. But it didn't happen. I've emailed with people at Dropbox from time to time and the best explanation I can come up with is that they are focused in different areas. It seems to me, from my outside perch, that they are trying to become a competitor to <a href="https://www.google.com/docs/about/">Google's</a> and <a href="https://en.wikipedia.org/wiki/Microsoft_Office">Microsoft's</a> Office products. I was hoping they'd become a platform, focusing on distribution and investment in startups." type="outline" created="Sun, 25 Jun 2017 12:32:31 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<title>Code mode is for real</title>
|
||||
<description><p><a href="http://donthitsave.com/comic/2016/03/25/code-mode"><img src="http://scripting.com/images/2017/06/25/humor.png" width="300" height="285" border="0" alt="Code mode is a real thing."></a></p> </description>
|
||||
<pubDate>Sun, 25 Jun 2017 15:53:13 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/25.html#a110613</link>
|
||||
<guid>http://scripting.com/2017/06/25.html#a110613</guid>
|
||||
<source:outline text="Code mode is for real" type="outline" created="Sun, 25 Jun 2017 15:53:13 GMT">
|
||||
<source:outline text="<a href="http://donthitsave.com/comic/2016/03/25/code-mode"><img src="http://scripting.com/images/2017/06/25/humor.png" width="300" height="285" border="0" alt="Code mode is a real thing."></a>" created="Sun, 25 Jun 2017 15:53:18 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<description>Good morning sports fans! 🏈</description>
|
||||
<pubDate>Sat, 24 Jun 2017 14:57:01 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a100601</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a100601</guid>
|
||||
<source:outline text="Good morning sports fans! :football:" type="outline" created="Sat, 24 Jun 2017 14:57:01 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description><a href="http://scripting.com/2017/03/08/theWorldIsSocialistPartIi.html">Health care is socialist</a> is getting a bunch of new reads today thanks to some powerful RTs.</description>
|
||||
<pubDate>Sat, 24 Jun 2017 20:50:06 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a040606</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a040606</guid>
|
||||
<source:outline text="<a href="http://scripting.com/2017/03/08/theWorldIsSocialistPartIi.html">Health care is socialist</a> is getting a bunch of new reads today thanks to some powerful RTs." type="outline" created="Sat, 24 Jun 2017 20:50:06 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Today is emoji day ⭐️ on Scripting News. We have all the ⭐️ best emoji. And they're free, for you, the best ⭐️ people in the universe, the readers of ⭐️ this humble blog. ⭐️</description>
|
||||
<pubDate>Sat, 24 Jun 2017 18:34:44 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a020644</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a020644</guid>
|
||||
<source:outline text="Today is emoji day :star: on Scripting News. We have all the :star: best emoji. And they're free, for you, the best :star: people in the universe, the readers of :star: this humble blog. :star:" type="outline" created="Sat, 24 Jun 2017 18:34:44 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>It makes sense that because of <a href="https://en.wikipedia.org/wiki/Reconciliation_(United_States_Congress)">reconcilliation</a> Repubs have a hard time repealing ObamaCare. It seems fair that you should have to have 60 votes to repeal something that required 60 votes to pass. How will they explain it to the voters they've been lying to about "repeal and replace." Double-talk. Swamp-talk. 👍</description>
|
||||
<pubDate>Sat, 24 Jun 2017 18:20:20 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a020620</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a020620</guid>
|
||||
<source:outline text="It makes sense that because of <a href="https://en.wikipedia.org/wiki/Reconciliation_(United_States_Congress)">reconcilliation</a> Repubs have a hard time repealing ObamaCare. It seems fair that you should have to have 60 votes to repeal something that required 60 votes to pass. How will they explain it to the voters they've been lying to about "repeal and replace." Double-talk. Swamp-talk. :+1:" type="outline" created="Sat, 24 Jun 2017 18:20:20 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>I've started a <a href="https://gitter.im/scriptingnews/Lobby">chatroom</a> on Gitter. Not sure what I'll use it for. Also not sure if it's open for anyone to join. I want it to be. The community <a href="http://guidelines.scripting.com/">guidelines</a> apply. Keep it short, respectful and on-topic, and <i>no spam. </i>⚾️</description>
|
||||
<pubDate>Sat, 24 Jun 2017 18:05:49 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a020649</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a020649</guid>
|
||||
<source:outline text="I've started a <a href="https://gitter.im/scriptingnews/Lobby">chatroom</a> on Gitter. Not sure what I'll use it for. Also not sure if it's open for anyone to join. I want it to be. The community <a href="http://guidelines.scripting.com/">guidelines</a> apply. Keep it short, respectful and on-topic, and <i>no spam. </i>:baseball:" type="outline" created="Sat, 24 Jun 2017 18:05:49 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>BTW, don't ask me to pitch RSS, I won't do it, because the request is based on a misunderstanding. RSS is not a product, it's a format. I have never made a dime from it. You have as much at stake in its success as I do. So I always turn it around and ask the person who asked me for a pitch to instead pitch <i>me</i> on it. I don't budge on this. Ever. 🏀</description>
|
||||
<pubDate>Sat, 24 Jun 2017 15:19:17 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a110617</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a110617</guid>
|
||||
<source:outline text="BTW, don't ask me to pitch RSS, I won't do it, because the request is based on a misunderstanding. RSS is not a product, it's a format. I have never made a dime from it. You have as much at stake in its success as I do. So I always turn it around and ask the person who asked me for a pitch to instead pitch <i>me</i> on it. I don't budge on this. Ever. :basketball:" type="outline" created="Sat, 24 Jun 2017 15:19:17 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Is anyone using the <a href="http://scripting.com/rss.json">JSON version</a> of the Scripting News feed in their feed reader? It's a bit of a trick question, in a way, because as far as I know, only <a href="https://github.com/scripting/river5">River5</a> and <a href="http://this.how/electricRiver">Electric River</a> support this format. If so, send me an email at my address, on the About page <a href="http://scripting.com/about.html">here</a>. 🍰</description>
|
||||
<pubDate>Sat, 24 Jun 2017 15:01:10 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a110610</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a110610</guid>
|
||||
<source:outline text="Is anyone using the <a href="http://scripting.com/rss.json">JSON version</a> of the Scripting News feed in their feed reader? It's a bit of a trick question, in a way, because as far as I know, only "River5" and "Electric River" support this format. If so, send me an email at my address, on the About page <a href="http://scripting.com/about.html">here</a>. :cake:" type="outline" created="Sat, 24 Jun 2017 15:01:10 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Journalists keep making a serious technology error -- assuming the only damage Russia can do to our government is during elections. 🇺🇸</description>
|
||||
<pubDate>Sat, 24 Jun 2017 05:50:50 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a010650</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a010650</guid>
|
||||
<source:outline text="Journalists keep making a serious technology error -- assuming the only damage Russia can do to our government is during elections. :us:" type="outline" created="Sat, 24 Jun 2017 05:50:50 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>I get my health insurance through the <a href="https://en.wikipedia.org/wiki/Patient_Protection_and_Affordable_Care_Act">ACA</a>. It's very much alive, <a href="http://www.politico.com/story/2017/06/23/sean-spicer-obamacare-repeal-bill-239895">Spicer</a>. Without it I would not have health insurance. 🍋</description>
|
||||
<pubDate>Sat, 24 Jun 2017 14:26:32 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a100632</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a100632</guid>
|
||||
<source:outline text="I get my health insurance through the <a href="https://en.wikipedia.org/wiki/Patient_Protection_and_Affordable_Care_Act">ACA</a>. It's very much alive, <a href="http://www.politico.com/story/2017/06/23/sean-spicer-obamacare-repeal-bill-239895">Spicer</a>. Without it I would not have health insurance. :lemon:" type="outline" created="Sat, 24 Jun 2017 14:26:32 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<title>The sad state of tech news in 2017</title>
|
||||
<description><p>It won't take much to reboot the tech blogosphere, just a few bloggers with ideas who listen to each other and want to work with each other. That was the <a href="http://scripting.com/davenet/1996/02/08/holdinghandsincyberspace.html">idea</a> behind blogrolls, to visibly show the relationships. </p> <ul> <li>I'm still thinking about how to integrate a blogroll with the new design of <a href="http://scripting.com/">Scripting News</a>. I pushed everything aside to get a clean look for the new site. I want to avoid bringing it all back. </li> </ul> <p>So, we have a few people who are writing and listening. That's a needed first step. Next we need a way to announce and hear about new tech products. Not just ones that get VC backing or come from big companies. We already hear about those products through <a href="http://techmeme.com/">TechMeme</a> and the pubs that contribute to it. We also have platform-specific news about tech products, it's more limited, but it's there.</p> <p>It will likely start with word of mouth among the bloggers. If <a href="http://altplatform.org/2017/06/20/building-a-blogroll-in-2017/">Richard</a> is using a product and speaks highly of it, I'm likely to take a look. Especially if he says it fits into what I'm doing through the open formats my software already supports. </p> <p>Then I want a river, a place where I can go to find out quickly what's new, in the way of products, not BigCo bluster or another $250 million VC deal. I want to know what my peers are doing. So I can learn from them, and so we can make our products work with theirs. </p> <p><i>Work together</i> is a phrase you'll hear me use a lot. It's the potential of tech, but it often isn't the attitude of tech. Even the smallest most independent developers dream of dominating. You can't work with people who dominate, even if they win. </p> <p>I want to hear about products that are open to connecting to mine. </p> <p>There have been times, often defined by news sources, that have created huge swells of compatible technology. To name a few: InfoWorld, PC WEEK, MacWEEK, TechCrunch. Very fond memories of the communities that gathered around each of those. </p> <p>It's time for another. The opportunity is there. It's been a long time since we had an open development community that worked to create great new user experience without lockin. It's like riding a bicycle or swimming, you don't forget how to do it. And like tennis or baseball, you can't play without partners and competition. </p> </description>
|
||||
<pubDate>Sat, 24 Jun 2017 13:43:48 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a090648</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a090648</guid>
|
||||
<source:outline text="The sad state of tech news in 2017" type="outline" created="Sat, 24 Jun 2017 13:43:48 GMT">
|
||||
<source:outline text="It won't take much to reboot the tech blogosphere, just a few bloggers with ideas who listen to each other and want to work with each other. That was the <a href="http://scripting.com/davenet/1996/02/08/holdinghandsincyberspace.html">idea</a> behind blogrolls, to visibly show the relationships." created="Sat, 24 Jun 2017 13:43:55 GMT">
|
||||
<source:outline text="I'm still thinking about how to integrate a blogroll with the new design of "Scripting News". I pushed everything aside to get a clean look for the new site. I want to avoid bringing it all back." created="Sat, 24 Jun 2017 14:32:17 GMT"/>
|
||||
</source:outline>
|
||||
<source:outline text="So, we have a few people who are writing and listening. That's a needed first step. Next we need a way to announce and hear about new tech products. Not just ones that get VC backing or come from big companies. We already hear about those products through "TechMeme" and the pubs that contribute to it. We also have platform-specific news about tech products, it's more limited, but it's there." created="Sat, 24 Jun 2017 13:44:09 GMT"/>
|
||||
<source:outline text="It will likely start with word of mouth among the bloggers. If <a href="http://altplatform.org/2017/06/20/building-a-blogroll-in-2017/">Richard</a> is using a product and speaks highly of it, I'm likely to take a look. Especially if he says it fits into what I'm doing through the open formats my software already supports." created="Sat, 24 Jun 2017 13:52:04 GMT"/>
|
||||
<source:outline text="Then I want a river, a place where I can go to find out quickly what's new, in the way of products, not BigCo bluster or another $250 million VC deal. I want to know what my peers are doing. So I can learn from them, and so we can make our products work with theirs." created="Sat, 24 Jun 2017 13:52:04 GMT"/>
|
||||
<source:outline text="<i>Work together</i> is a phrase you'll hear me use a lot. It's the potential of tech, but it often isn't the attitude of tech. Even the smallest most independent developers dream of dominating. You can't work with people who dominate, even if they win." created="Sat, 24 Jun 2017 13:54:33 GMT"/>
|
||||
<source:outline text="I want to hear about products that are open to connecting to mine." created="Sat, 24 Jun 2017 20:53:41 GMT"/>
|
||||
<source:outline text="There have been times, often defined by news sources, that have created huge swells of compatible technology. To name a few: InfoWorld, PC WEEK, MacWEEK, TechCrunch. Very fond memories of the communities that gathered around each of those." created="Sat, 24 Jun 2017 13:48:02 GMT"/>
|
||||
<source:outline text="It's time for another. The opportunity is there. It's been a long time since we had an open development community that worked to create great new user experience without lockin. It's like riding a bicycle or swimming, you don't forget how to do it. And like tennis or baseball, you can't play without partners and competition." created="Sat, 24 Jun 2017 13:57:02 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<title>Republican-inspired art</title>
|
||||
<description><p><a href="http://boingboing.net/2017/06/23/the-white-house-is-having-off.html"><img src="http://scripting.com/images/2017/06/24/spicer.png" width="502" height="339" border="0" alt="A picture named spicer.png"></a></p> </description>
|
||||
<pubDate>Sat, 24 Jun 2017 14:52:32 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/24.html#a100632</link>
|
||||
<guid>http://scripting.com/2017/06/24.html#a100632</guid>
|
||||
<source:outline text="Republican-inspired art" type="outline" created="Sat, 24 Jun 2017 14:52:32 GMT">
|
||||
<source:outline text="<a href="http://boingboing.net/2017/06/23/the-white-house-is-having-off.html"><img src="http://scripting.com/images/2017/06/24/spicer.png" width="502" height="339" border="0" alt="A picture named spicer.png"></a>" created="Sat, 24 Jun 2017 14:52:37 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<description>Thank you <a href="https://twitter.com/om/status/878075081204711424">Om</a> for the tweet-love. ❤️</description>
|
||||
<pubDate>Fri, 23 Jun 2017 11:50:48 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a070648</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a070648</guid>
|
||||
<source:outline text="Thank you <a href="https://twitter.com/om/status/878075081204711424">Om</a> for the tweet-love. :heart:" type="outline" created="Fri, 23 Jun 2017 11:50:48 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description><a href="https://twitter.com/davewiner/status/878279505453826049">An idea worth RT'ing</a>: "A site people could go to, fill in some info about themselves, and find out how much they would lose under the Repub plan."</description>
|
||||
<pubDate>Fri, 23 Jun 2017 16:02:34 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a120634</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a120634</guid>
|
||||
<source:outline text="<a href="https://twitter.com/davewiner/status/878279505453826049">An idea worth RT'ing</a>: "A site people could go to, fill in some info about themselves, and find out how much they would lose under the Repub plan."" type="outline" created="Fri, 23 Jun 2017 16:02:34 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description><a href="https://github.com/scripting/githubpub/blob/master/README.md">githubpub</a> is a Node app that serves from GitHub repositories.</description>
|
||||
<pubDate>Fri, 23 Jun 2017 13:52:38 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a090638</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a090638</guid>
|
||||
<source:outline text=""githubpub" is a Node app that serves from GitHub repositories." type="outline" created="Fri, 23 Jun 2017 13:52:38 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Fix for the Scripting News <a href="http://scripting.com/rss.xml">RSS</a> <a href="http://scripting.com/rss.json">feed</a>: we now process glossary entries and emoji short codes. The net effect is that text shortcuts like <a href="http://cyber.law.harvard.edu/rss/rss.html">RSS</a> will be expanded as well as 👏 emoji 👏, in the feeds. As they say, <i>still diggin!</i></description>
|
||||
<pubDate>Fri, 23 Jun 2017 14:52:02 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a100602</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a100602</guid>
|
||||
<source:outline text="Fix for the Scripting News <a href="http://scripting.com/rss.xml">RSS</a> <a href="http://scripting.com/rss.json">feed</a>: we now process glossary entries and emoji short codes. The net effect is that text shortcuts like "rss" will be expanded as well as :clap: emoji :clap:, in the feeds. As they say, <i>still diggin!</i>" type="outline" created="Fri, 23 Jun 2017 14:52:02 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>So many thoughts today begin with -- If the Dems only had their shit together.</description>
|
||||
<pubDate>Fri, 23 Jun 2017 11:52:59 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a070659</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a070659</guid>
|
||||
<source:outline text="So many thoughts today begin with -- If the Dems only had their shit together." type="tweet" created="Fri, 23 Jun 2017 11:52:59 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>If the Dems only had their shit together, we would be mobleizable to knock on neighbor's doors this weekend with pre-written talking points. "Did you know that you <s>could</s> will lose your health insurance if the Republicans have their way?"</description>
|
||||
<pubDate>Fri, 23 Jun 2017 11:53:24 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a070624</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a070624</guid>
|
||||
<source:outline text="If the Dems only had their shit together, we would be mobleizable to knock on neighbor's doors this weekend with pre-written talking points. "Did you know that you <s>could</s> will lose your health insurance if the Republicans have their way?"" type="outline" created="Fri, 23 Jun 2017 11:53:24 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Repubs who consider voting for the repeal of Medicaid and the ACA should fear the <a href="https://en.wikipedia.org/wiki/Hell">hellfire</a> they will face when they run for re-election. <i>This weekend</i> is the time to make your feelings felt.</description>
|
||||
<pubDate>Fri, 23 Jun 2017 11:47:33 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a070633</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a070633</guid>
|
||||
<source:outline text="Repubs who consider voting for the repeal of Medicaid and the ACA should fear the <a href="https://en.wikipedia.org/wiki/Hell">hellfire</a> they will face when they run for re-election. <i>This weekend</i> is the time to make your feelings felt." type="outline" created="Fri, 23 Jun 2017 11:47:33 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>BTW, if someone expresses frustration it doesn't follow that they blame you, esp on Twitter which is a length-impaired medium.</description>
|
||||
<pubDate>Fri, 23 Jun 2017 11:45:41 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a070641</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a070641</guid>
|
||||
<source:outline text="BTW, if someone expresses frustration it doesn't follow that they blame you, esp on Twitter which is a length-impaired medium." type="outline" created="Fri, 23 Jun 2017 11:45:41 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<title>Thin servers</title>
|
||||
<description><p>Two friends, <a href="https://twitter.com/judell">Jon Udell</a> and <a href="https://twitter.com/holden">Mike Caulfield</a>, are talking about "dumb" servers. I call the same things "thin." Also fractional-horsepower servers. They go by a bunch of names, but the idea and motivation is the same. </p> <p>The idea: We move functionality from the server to the edge (desktop, mobile device), repeating until someday there's nothing left on the server. We could go all the way, but it needs a strong operational backend, something a big company is good at, not so much individuals. (With the caveat that some think this problem is distributable, notably the fictional <a href="https://www.crunchbase.com/person/richard-hendriks#/entity">CEO</a> of <a href="http://www.piedpiper.com/">Pied Piper</a>.)</p> <p>The key thing is identity. Once you have that solved, it all becomes relatively easy. I've factored out identity into a layer I call <a href="https://github.com/scripting/nodeStorage">nodeStorage</a>. It associates storage with a user's Twitter identity. Twitter is a good service to use, unlike some others, because they have a liberal policy of who gets to <a href="https://apps.twitter.com/app/new">create</a> apps. Faceook has an extensive vetting process. Twitter <a href="https://apps.twitter.com/app/new">is</a> "let a thousand flowers bloom." I know some people have problems with Twitter, but I've learned over many years that all corporate vendors are imperfect. If you're waiting for <a href="http://this.how/standards#1497798834000">perfection</a> you'll wait forever. And you build the software so that if Twitter should again become draconian, a new service can be filled in with as little disruption as possible.</p> <p>Another place I've looked is Dropbox. There, with one simple feature, the ability to associate a domain with a folder, they would solve the problem. I know there are external services that provide something "like" this, but fundamentally Dropbox doesn't provide enough flexibility in the API to do this in a reasonable way. (Lack of granularity in permissions, an app gets access to one folder or everything.)</p> <p>Or Amazon, if their identity system for AWS were simpler for end users, or if their end-user storage system could be accessed through the S3 API. I'm sure they've thought of it. There must be a reason they don't do it. </p> <p>And Twitter could completely eliminate the need for nodeStorage, by offering users a few gigabytes of storage attached to their Twitter account, accessible through the API. The first person who described the feature to me was Jack Dorsey, about eight years ago, when we met for coffee in SF. So he understands why this idea is so powerful. I'm not sure what the holdup is. </p> <p>In the meantime, <a href="https://github.com/scripting/nodeStorage">nodeStorage</a> works. I build the kind of apps I want. Open the sidebar on Scripting News (left margin) and have a look at the apps. Some even have <a href="https://github.com/scripting/macwrite/blob/master/macwrite.js">source code</a> so you can see for yourself. </p> </description>
|
||||
<pubDate>Fri, 23 Jun 2017 14:03:20 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a100620</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a100620</guid>
|
||||
<source:outline text="Thin servers" type="outline" created="Fri, 23 Jun 2017 14:03:20 GMT">
|
||||
<source:outline text="Two friends, <a href="https://twitter.com/judell">Jon Udell</a> and <a href="https://twitter.com/holden">Mike Caulfield</a>, are talking about "dumb" servers. I call the same things "thin." Also fractional-horsepower servers. They go by a bunch of names, but the idea and motivation is the same." created="Fri, 23 Jun 2017 14:05:30 GMT"/>
|
||||
<source:outline text="The idea: We move functionality from the server to the edge (desktop, mobile device), repeating until someday there's nothing left on the server. We could go all the way, but it needs a strong operational backend, something a big company is good at, not so much individuals. (With the caveat that some think this problem is distributable, notably the fictional <a href="https://www.crunchbase.com/person/richard-hendriks#/entity">CEO</a> of <a href="http://www.piedpiper.com/">Pied Piper</a>.)" created="Fri, 23 Jun 2017 14:24:35 GMT"/>
|
||||
<source:outline text="The key thing is identity. Once you have that solved, it all becomes relatively easy. I've factored out identity into a layer I call "nodeStorage". It associates storage with a user's Twitter identity. Twitter is a good service to use, unlike some others, because they have a liberal policy of who gets to <a href="https://apps.twitter.com/app/new">create</a> apps. Faceook has an extensive vetting process. Twitter <a href="https://apps.twitter.com/app/new">is</a> "let a thousand flowers bloom." I know some people have problems with Twitter, but I've learned over many years that all corporate vendors are imperfect. If you're waiting for <a href="http://this.how/standards#1497798834000">perfection</a> you'll wait forever. And you build the software so that if Twitter should again become draconian, a new service can be filled in with as little disruption as possible." created="Fri, 23 Jun 2017 14:05:43 GMT"/>
|
||||
<source:outline text="Another place I've looked is Dropbox. There, with one simple feature, the ability to associate a domain with a folder, they would solve the problem. I know there are external services that provide something "like" this, but fundamentally Dropbox doesn't provide enough flexibility in the API to do this in a reasonable way. (Lack of granularity in permissions, an app gets access to one folder or everything.)" created="Fri, 23 Jun 2017 14:08:41 GMT"/>
|
||||
<source:outline text="Or Amazon, if their identity system for AWS were simpler for end users, or if their end-user storage system could be accessed through the S3 API. I'm sure they've thought of it. There must be a reason they don't do it." created="Fri, 23 Jun 2017 14:09:10 GMT"/>
|
||||
<source:outline text="And Twitter could completely eliminate the need for nodeStorage, by offering users a few gigabytes of storage attached to their Twitter account, accessible through the API. The first person who described the feature to me was Jack Dorsey, about eight years ago, when we met for coffee in SF. So he understands why this idea is so powerful. I'm not sure what the holdup is." created="Fri, 23 Jun 2017 14:09:55 GMT"/>
|
||||
<source:outline text="In the meantime, "nodeStorage" works. I build the kind of apps I want. Open the sidebar on Scripting News (left margin) and have a look at the apps. Some even have <a href="https://github.com/scripting/macwrite/blob/master/macwrite.js">source code</a> so you can see for yourself." created="Fri, 23 Jun 2017 14:33:10 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<title>RicMac, part II</title>
|
||||
<description><p><a href="https://twitter.com/ricmac">Richard MacManus</a> keeps on truckin. There's nothing more powerful than a persistent and curious user who's relatively fearless. </p> <p>In a follow-up <a href="https://richardmacmanus.com/2017/06/22/openness-rivers-indieweb/">post</a> I learned that there is an IndieWeb-approved feed reader called <a href="https://github.com/kylewm/woodwind">Woodwind</a>. That's good news. <a href="http://cyber.law.harvard.edu/rss/rss.html">RSS</a> and related technolgies, <a href="https://github.com/kylewm/woodwind/issues/66">including</a> <a href="https://github.com/kylewm/woodwind/issues/7">OPML</a> import and export, are essential components of the open web. </p> <p>BTW, to Richard, I wrote up my <a href="http://this.how/standards">rules for standards-makers</a>, based on experience re what (imho) is important and what works and doesn't. Another item for your consideration. </p> </description>
|
||||
<pubDate>Fri, 23 Jun 2017 11:14:43 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a070643</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a070643</guid>
|
||||
<source:outline text="RicMac, part II" type="outline" created="Fri, 23 Jun 2017 11:14:43 GMT">
|
||||
<source:outline text="<a href="https://twitter.com/ricmac">Richard MacManus</a> keeps on truckin. There's nothing more powerful than a persistent and curious user who's relatively fearless." created="Fri, 23 Jun 2017 11:20:38 GMT"/>
|
||||
<source:outline text="In a follow-up <a href="https://richardmacmanus.com/2017/06/22/openness-rivers-indieweb/">post</a> I learned that there is an IndieWeb-approved feed reader called <a href="https://github.com/kylewm/woodwind">Woodwind</a>. That's good news. "RSS" and related technolgies, <a href="https://github.com/kylewm/woodwind/issues/66">including</a> <a href="https://github.com/kylewm/woodwind/issues/7">OPML</a> import and export, are essential components of the open web." created="Fri, 23 Jun 2017 11:15:54 GMT"/>
|
||||
<source:outline text="BTW, to Richard, I wrote up my <a href="http://this.how/standards">rules for standards-makers</a>, based on experience re what (imho) is important and what works and doesn't. Another item for your consideration." created="Fri, 23 Jun 2017 11:19:38 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<title>Test post</title>
|
||||
<description><p>Here's a list with four items</p> <ul> <li>one</li> <li>two</li> <li>three</li> <li>four</li> </ul> </description>
|
||||
<pubDate>Fri, 23 Jun 2017 19:48:21 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/23.html#a030621</link>
|
||||
<guid>http://scripting.com/2017/06/23.html#a030621</guid>
|
||||
<source:outline text="Test post" type="outline" created="Fri, 23 Jun 2017 19:48:21 GMT">
|
||||
<source:outline text="Here's a list with four items" created="Fri, 23 Jun 2017 19:49:21 GMT">
|
||||
<source:outline text="one" created="Fri, 23 Jun 2017 19:48:43 GMT"/>
|
||||
<source:outline text="two" created="Fri, 23 Jun 2017 19:48:44 GMT"/>
|
||||
<source:outline text="three" created="Fri, 23 Jun 2017 19:48:45 GMT"/>
|
||||
<source:outline text="four" created="Fri, 23 Jun 2017 19:48:46 GMT"/>
|
||||
</source:outline>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<description>I'm working on a new Node web server that serves out of GitHub repos. It's a very sweet very small piece of software.</description>
|
||||
<pubDate>Thu, 22 Jun 2017 16:28:33 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/22.html#a120633</link>
|
||||
<guid>http://scripting.com/2017/06/22.html#a120633</guid>
|
||||
<source:outline text="I'm working on a new Node web server that serves out of GitHub repos. It's a very sweet very small piece of software." type="tweet" created="Thu, 22 Jun 2017 16:28:33 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>An epiphany. Mark Zuckerberg is his generation's <a href="https://en.wikipedia.org/wiki/Ray_Kroc">Ray Kroc</a>, and Facebook is <a href="https://www.mcdonalds.com/us/en-us.html">McDonald's</a>. I aspire to be <a href="https://en.wikipedia.org/wiki/Alice_Waters">Alice Waters</a>.</description>
|
||||
<pubDate>Thu, 22 Jun 2017 13:29:16 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/22.html#a090616</link>
|
||||
<guid>http://scripting.com/2017/06/22.html#a090616</guid>
|
||||
<source:outline text="An epiphany. Mark Zuckerberg is his generation's <a href="https://en.wikipedia.org/wiki/Ray_Kroc">Ray Kroc</a>, and Facebook is <a href="https://www.mcdonalds.com/us/en-us.html">McDonald's</a>. I aspire to be <a href="https://en.wikipedia.org/wiki/Alice_Waters">Alice Waters</a>." type="tweet" created="Thu, 22 Jun 2017 13:29:16 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Future-of-journalism conferences that ignore blogging are not about the future of journalism.</description>
|
||||
<pubDate>Thu, 22 Jun 2017 13:29:44 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/22.html#a090644</link>
|
||||
<guid>http://scripting.com/2017/06/22.html#a090644</guid>
|
||||
<source:outline text="Future-of-journalism conferences that ignore blogging are not about the future of journalism." type="outline" created="Thu, 22 Jun 2017 13:29:44 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>News will be interesting tonight. They've got the Repub health care <a href="https://www.axios.com/on-health-care-moderates-quiet-while-conservatives-put-their-foot-down-2446375738.html">bill</a> to rip apart, and it's also <a href="http://nbariver.com/">NBA Draft</a> night.</description>
|
||||
<pubDate>Thu, 22 Jun 2017 16:27:29 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/22.html#a120629</link>
|
||||
<guid>http://scripting.com/2017/06/22.html#a120629</guid>
|
||||
<source:outline text="News will be interesting tonight. They've got the Repub health care <a href="https://www.axios.com/on-health-care-moderates-quiet-while-conservatives-put-their-foot-down-2446375738.html">bill</a> to rip apart, and it's also <a href="http://nbariver.com/">NBA Draft</a> night." type="tweet" created="Thu, 22 Jun 2017 16:27:29 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<title>Fargo puzzler</title>
|
||||
<description><p>The last two episodes of <a href="https://en.wikipedia.org/wiki/Fargo_(season_3)">season 3</a> of Fargo were fantastic. But, the opening scene of episode 1, which takes place in a police office in East Germany during the Cold War, is without explanation. </p> <p>All through the season, I was wondering how it was going to be connected up with the story that takes place in Minnesota in 2011, but as far as I know it never was.</p> <p>Maybe that was VM Varga as the accused? Or the police guy?</p> <p>This is kind of bothering me! :-)</p> <p>Okay then...</p> <p>Update: In the <a href="https://en.wikipedia.org/wiki/Fargo_(season_3)#Episodes">episode guide</a> on Wikipedia they describe the opening scene as follows: "In 1988 East Berlin, Jacob Ungerleider is questioned in the death of a woman, which he claims is a case of mistaken identity." So it's not VM Varga in the hot seat. Who is Jacob Ungerleider? I have no idea! ;-)</p> </description>
|
||||
<pubDate>Thu, 22 Jun 2017 19:26:10 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/22.html#a030610</link>
|
||||
<guid>http://scripting.com/2017/06/22.html#a030610</guid>
|
||||
<source:outline text="Fargo puzzler" type="outline" created="Thu, 22 Jun 2017 19:26:10 GMT">
|
||||
<source:outline text="The last two episodes of <a href="https://en.wikipedia.org/wiki/Fargo_(season_3)">season 3</a> of Fargo were fantastic. But, the opening scene of episode 1, which takes place in a police office in East Germany during the Cold War, is without explanation." created="Thu, 22 Jun 2017 19:26:18 GMT"/>
|
||||
<source:outline text="All through the season, I was wondering how it was going to be connected up with the story that takes place in Minnesota in 2011, but as far as I know it never was." created="Thu, 22 Jun 2017 19:27:20 GMT"/>
|
||||
<source:outline text="Maybe that was VM Varga as the accused? Or the police guy?" created="Thu, 22 Jun 2017 19:27:54 GMT"/>
|
||||
<source:outline text="This is kind of bothering me! :-)" created="Thu, 22 Jun 2017 19:28:15 GMT"/>
|
||||
<source:outline text="Okay then..." created="Thu, 22 Jun 2017 19:28:27 GMT"/>
|
||||
<source:outline text="Update: In the <a href="https://en.wikipedia.org/wiki/Fargo_(season_3)#Episodes">episode guide</a> on Wikipedia they describe the opening scene as follows: "In 1988 East Berlin, Jacob Ungerleider is questioned in the death of a woman, which he claims is a case of mistaken identity." So it's not VM Varga in the hot seat. Who is Jacob Ungerleider? I have no idea! ;-)" created="Thu, 22 Jun 2017 19:38:25 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<description>Good morning summer solstice fans!</description>
|
||||
<pubDate>Wed, 21 Jun 2017 10:21:47 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/21.html#a060647</link>
|
||||
<guid>http://scripting.com/2017/06/21.html#a060647</guid>
|
||||
<source:outline text="Good morning summer solstice fans!" type="outline" created="Wed, 21 Jun 2017 10:21:47 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>It was a boring NBA postseason, for the most. But the excitement of next season is already starting, with the draft tomorrow, and deal season in full swing. The place to find all the news is <a href="http://nbariver.com/">nbariver.com</a>. It's one of many rivers maintained by my <a href="https://github.com/scripting/river5">River5</a> installation.</description>
|
||||
<pubDate>Wed, 21 Jun 2017 10:22:11 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/21.html#a060611</link>
|
||||
<guid>http://scripting.com/2017/06/21.html#a060611</guid>
|
||||
<source:outline text="It was a boring NBA postseason, for the most. But the excitement of next season is already starting, with the draft tomorrow, and deal season in full swing. The place to find all the news is <a href="http://nbariver.com/">nbariver.com</a>. It's one of many rivers maintained by my "River5" installation." type="outline" created="Wed, 21 Jun 2017 10:22:11 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>BTW, I hate the term "eating the dogfood." As much as I love dogs, it says that our users are pets, not sentient human beings, our equals. It also says our software is dog food. I think as a kid, as an experiment, a few of us kids actually ate dog food. It's a vague memory, that must have some basis in reality. It makes me nauseous to think about it. And that's what I think about when I hear the term. Please, let's find another way of saying "My software is good because I use it, and vice versa."</description>
|
||||
<pubDate>Wed, 21 Jun 2017 10:57:32 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/21.html#a060632</link>
|
||||
<guid>http://scripting.com/2017/06/21.html#a060632</guid>
|
||||
<source:outline text="BTW, I hate the term "eating the dogfood." As much as I love dogs, it says that our users are pets, not sentient human beings, our equals. It also says our software is dog food. I think as a kid, as an experiment, a few of us kids actually ate dog food. It's a vague memory, that must have some basis in reality. It makes me nauseous to think about it. And that's what I think about when I hear the term. Please, let's find another way of saying "My software is good because I use it, and vice versa."" type="outline" created="Wed, 21 Jun 2017 10:57:32 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>Yesterday I posted a <a href="http://scripting.com/images/2017/06/21/frozenBlogPost.png">screen shot</a> of one of my posts on Facebook, to accolades from friends on Facebook. I deleted the post. I won't be doing it again. Facebook is not a place for blog posts. Not as long as they disable linking, styles, titles and podcasts. If you want to help Facebook destroy the open web, go for it. But I will not participate in that awful adventure.</description>
|
||||
<pubDate>Wed, 21 Jun 2017 11:01:33 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/21.html#a070633</link>
|
||||
<guid>http://scripting.com/2017/06/21.html#a070633</guid>
|
||||
<source:outline text="Yesterday I posted a <a href="http://scripting.com/images/2017/06/21/frozenBlogPost.png">screen shot</a> of one of my posts on Facebook, to accolades from friends on Facebook. I deleted the post. I won't be doing it again. Facebook is not a place for blog posts. Not as long as they disable linking, styles, titles and podcasts. If you want to help Facebook destroy the open web, go for it. But I will not participate in that awful adventure." type="outline" created="Wed, 21 Jun 2017 11:01:33 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<description>On Facebook you are who the algorithm says you are.</description>
|
||||
<pubDate>Wed, 21 Jun 2017 10:53:00 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/21.html#a060600</link>
|
||||
<guid>http://scripting.com/2017/06/21.html#a060600</guid>
|
||||
<source:outline text="On Facebook you are who the algorithm says you are." type="outline" created="Wed, 21 Jun 2017 10:53:00 GMT"/>
|
||||
</item>
|
||||
<item>
|
||||
<title>GitHub API example app</title>
|
||||
<description><p>A simple <a href="http://scripting.com/misc/code/githubapi/directory.html">web app</a> that travels through the <a href="https://github.com/scripting/river5">River5 repository</a> in my GitHub account, producing a directory that reflects the structure of the repo.</p> <p>I couldn't <a href="https://duckduckgo.com/?q=github+api+example+app+directory+repository&t=h_&ia=web">find</a> sample code that does this simple thing. Now I won't have to hunt for it, and neither will you. ;-) </p> <p>Here's the <a href="https://gist.github.com/scripting/f5e5b3a175265f47fda098cb5bddca2f">source code</a>. </p> </description>
|
||||
<pubDate>Wed, 21 Jun 2017 15:18:14 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/21.html#a110614</link>
|
||||
<guid>http://scripting.com/2017/06/21.html#a110614</guid>
|
||||
<source:outline text="GitHub API example app" type="outline" created="Wed, 21 Jun 2017 15:18:14 GMT">
|
||||
<source:outline text="A simple <a href="http://scripting.com/misc/code/githubapi/directory.html">web app</a> that travels through the <a href="https://github.com/scripting/river5">River5 repository</a> in my GitHub account, producing a directory that reflects the structure of the repo." created="Wed, 21 Jun 2017 15:18:24 GMT"/>
|
||||
<source:outline text="I couldn't <a href="https://duckduckgo.com/?q=github+api+example+app+directory+repository&t=h_&ia=web">find</a> sample code that does this simple thing. Now I won't have to hunt for it, and neither will you. ;-)" created="Wed, 21 Jun 2017 15:18:36 GMT"/>
|
||||
<source:outline text="Here's the <a href="https://gist.github.com/scripting/f5e5b3a175265f47fda098cb5bddca2f">source code</a>." created="Wed, 21 Jun 2017 15:19:39 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<title>Bike video from two years ago</title>
|
||||
<description><p><iframe width="560" height="315" src="https://www.youtube.com/embed/iWvdXdW7GiI" frameborder="0" allowfullscreen></iframe></p> </description>
|
||||
<pubDate>Wed, 21 Jun 2017 18:47:37 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/21.html#a020637</link>
|
||||
<guid>http://scripting.com/2017/06/21.html#a020637</guid>
|
||||
<source:outline text="Bike video from two years ago" type="outline" created="Wed, 21 Jun 2017 18:47:37 GMT">
|
||||
<source:outline text="<iframe width="560" height="315" src="https://www.youtube.com/embed/iWvdXdW7GiI" frameborder="0" allowfullscreen></iframe>" created="Wed, 21 Jun 2017 18:47:50 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<title>An old friend: Richard MacManus</title>
|
||||
<description><p>Richard is one of the old school bloggers. He started <a href="https://web.archive.org/web/20031014183603/http://readwriteweb.com">ReadWriteWeb</a> in 2003. It started as a Radio UserLand project and grew into a leading tech publication, something which I'm personally proud of. </p> <p>He has a <a href="http://altplatform.org/2017/06/20/building-a-blogroll-in-2017/">new blog</a> up and running. I've added it to my <a href="http://scripting.com/river.html">personal river</a> here on Scripting News. He asks about where the blogrolls have gone, a topic I <a href="http://scripting.com/2017/06/18.html#a110612">wrote about</a> a couple of days ago. Richard would certainly be in my blogroll.</p> <p>Maybe the subscription list <a href="http://scripting.com/2016/09/23/otherBlogsLikeScriptingNews.html">for</a> my <a href="http://bloggers.scripting.com/">blogger's river</a> would make a good start for my blogroll, or vice versa? Something we didn't do in the first iteration is make our rivers public. Nowadays I'm doing that routinely. A few examples are in the left sidebar here on <a href="http://scripting.com/">Scripting News</a>. </p> <p>Richard has turned to <a href="https://indieweb.org/">IndieWeb</a> for the latest on open web tech. That's fine, but you have to look elsewhere too, because as he's discovered, they only embrace part of the open web. It's too bad they chose such an inclusive name, but have an exclusive approach. For example, they have avoided <a href="http://cyber.law.harvard.edu/rss/rss.html">RSS</a>, for reasons I'm sure I don't understand (I've listened, so no need to repeat the reasoning). We need all the advantages we can get because there are <a href="http://scripting.com/2017/06/18.html#a090658">serious</a> <a href="http://scripting.com/2016/10/03/turnsOutLessFacebookIsOk.html">headwinds</a> these days for blogging. RSS is serious open web technology. To not build on it is unthinkable, for me at least. </p> <p>Re integration between writing and reading, another topic of interest to Richard, all my rivers <a href="http://scripting.com/images/2017/06/21/postingFromARiver.png">hook</a> into <a href="http://radio3.io/">Radio3</a>, which is my latest <a href="http://scripting.com/links.html">linkblogging</a> tool. For reading, I encouraged Richard to look at <a href="http://this.how/electricRiver">Electric River</a>, it's the closest to what Radio UserLand did with aggregation in 2002. It runs on your Mac desktop, as the original did. When he wants to go all-in with rivers, nothing can take the place of <a href="https://github.com/scripting/river5">River5</a>, which is getting both modular and deep. I'm doing more work on that. Rivers have not finished evolving as far as I'm concerned. </p> </description>
|
||||
<pubDate>Wed, 21 Jun 2017 10:29:51 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/21.html#a060651</link>
|
||||
<guid>http://scripting.com/2017/06/21.html#a060651</guid>
|
||||
<source:outline text="An old friend: Richard MacManus" type="outline" created="Wed, 21 Jun 2017 10:29:51 GMT">
|
||||
<source:outline text="Richard is one of the old school bloggers. He started <a href="https://web.archive.org/web/20031014183603/http://readwriteweb.com">ReadWriteWeb</a> in 2003. It started as a Radio UserLand project and grew into a leading tech publication, something which I'm personally proud of." created="Wed, 21 Jun 2017 10:33:59 GMT"/>
|
||||
<source:outline text="He has a <a href="http://altplatform.org/2017/06/20/building-a-blogroll-in-2017/">new blog</a> up and running. I've added it to my <a href="http://scripting.com/river.html">personal river</a> here on Scripting News. He asks about where the blogrolls have gone, a topic I <a href="http://scripting.com/2017/06/18.html#a110612">wrote about</a> a couple of days ago. Richard would certainly be in my blogroll." created="Wed, 21 Jun 2017 10:25:16 GMT"/>
|
||||
<source:outline text="Maybe the subscription list <a href="http://scripting.com/2016/09/23/otherBlogsLikeScriptingNews.html">for</a> my <a href="http://bloggers.scripting.com/">blogger's river</a> would make a good start for my blogroll, or vice versa? Something we didn't do in the first iteration is make our rivers public. Nowadays I'm doing that routinely. A few examples are in the left sidebar here on "Scripting News"." created="Wed, 21 Jun 2017 10:40:31 GMT"/>
|
||||
<source:outline text="Richard has turned to <a href="https://indieweb.org/">IndieWeb</a> for the latest on open web tech. That's fine, but you have to look elsewhere too, because as he's discovered, they only embrace part of the open web. It's too bad they chose such an inclusive name, but have an exclusive approach. For example, they have avoided "RSS", for reasons I'm sure I don't understand (I've listened, so no need to repeat the reasoning). We need all the advantages we can get because there are <a href="http://scripting.com/2017/06/18.html#a090658">serious</a> <a href="http://scripting.com/2016/10/03/turnsOutLessFacebookIsOk.html">headwinds</a> these days for blogging. RSS is serious open web technology. To not build on it is unthinkable, for me at least." created="Wed, 21 Jun 2017 10:30:33 GMT"/>
|
||||
<source:outline text="Re integration between writing and reading, another topic of interest to Richard, all my rivers <a href="http://scripting.com/images/2017/06/21/postingFromARiver.png">hook</a> into "Radio3", which is my latest <a href="http://scripting.com/links.html">linkblogging</a> tool. For reading, I encouraged Richard to look at "Electric River", it's the closest to what Radio UserLand did with aggregation in 2002. It runs on your Mac desktop, as the original did. When he wants to go all-in with rivers, nothing can take the place of "River5", which is getting both modular and deep. I'm doing more work on that. Rivers have not finished evolving as far as I'm concerned." created="Wed, 21 Jun 2017 10:32:27 GMT"/>
|
||||
</source:outline>
|
||||
</item>
|
||||
<item>
|
||||
<description>Anyone want to blog-debate about XML vs JSON? I've spent years using both, I think I have an objective view of the strengths of each. Imho, they are almost the same thing. XML has attributes and values, and that does make it more complex. Slightly. But you don't have to use the extra features. Look at <a href="http://dev.opml.org/spec2.html">OPML</a> for an idea of a simple very JSON-like application of XML. Beyond that, there's really no difference. If you disagree, write a post, link to this and send me the link. I will read what you wrote, and respond, on <a href="http://scripting.com/">my blog</a>, if I have something to say. There's been so much bullshit flying around. I'd like to cut through that.</description>
|
||||
<pubDate>Wed, 21 Jun 2017 02:56:47 GMT</pubDate>
|
||||
<link>http://scripting.com/2017/06/20.html#a100647</link>
|
||||
<guid>http://scripting.com/2017/06/20.html#a100647</guid>
|
||||
<source:outline text="Anyone want to blog-debate about XML vs JSON? I've spent years using both, I think I have an objective view of the strengths of each. Imho, they are almost the same thing. XML has attributes and values, and that does make it more complex. Slightly. But you don't have to use the extra features. Look at "OPML" for an idea of a simple very JSON-like application of XML. Beyond that, there's really no difference. If you disagree, write a post, link to this and send me the link. I will read what you wrote, and respond, on <a href="http://scripting.com/">my blog</a>, if I have something to say. There's been so much bullshit flying around. I'd like to cut through that." type="outline" created="Wed, 21 Jun 2017 02:56:47 GMT"/>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
1102
Parser/Tests/ParserTests/Resources/sixcolors.html
Executable file
1102
Parser/Tests/ParserTests/Resources/sixcolors.html
Executable file
File diff suppressed because it is too large
Load Diff
135
Parser/Tests/ParserTests/Resources/theomnishow.rss
Normal file
135
Parser/Tests/ParserTests/Resources/theomnishow.rss
Normal file
@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0"><channel><title>The Omni Show</title><link>https://theomnishow.omnigroup.com/</link><atom:link href="https://theomnishow.omnigroup.com/rss/" rel="self" type="application/rss+xml" /><language>en-us</language><copyright>℗ & © 2017 The Omni Group</copyright><itunes:subtitle>Get to know the people and stories behind Omni’s award-winning productivity apps for Mac and iOS.</itunes:subtitle><itunes:author>The Omni Group</itunes:author><itunes:summary>Get to know the people and stories behind Omni’s award-winning productivity apps for Mac and iOS.</itunes:summary><description>Get to know the people and stories behind Omni’s award-winning productivity apps for Mac and iOS.</description><itunes:owner><itunes:name>The Omni Group</itunes:name><itunes:email>theomnishow@omnigroup.com</itunes:email></itunes:owner><itunes:image href="https://theomnishow.omnigroup.com/assets/img/omnishow-cover-1x3000.jpg" /><itunes:category text="Technology"><itunes:category text="Tech News" /></itunes:category><itunes:explicit>no</itunes:explicit><item><title>Andrea McVittie, User Experience Designer</title><itunes:author>The Omni Group</itunes:author><link>https://theomnishow.omnigroup.com/episode/andrea-mcvittie-user-experience-designer/</link><description>Andrea studied fine art at <a href="https://msu.edu/">Michigan State</a> and human-computer interaction at the <a href="https://www.umich.edu/">University of Michigan</a>. One day, while working at the school library, it was discovered that she could make websites — which launched her career as a UX designer, and since 2012 she’s been designing apps at Omni.</description><itunes:image href="https://theomnishow.omnigroup.com/assets/img/omnishow-cover-1x3000.jpg" /><enclosure length="24388318" type="audio/mpeg" url="https://d2vstsfo8knv0p.cloudfront.net/audio/the-omni-show-004.mp3" /><guid>https://theomnishow.omnigroup.com/episode/andrea-mcvittie-user-experience-designer/</guid><pubDate>Wed, 06 Dec 2017 14:00:00 +0000</pubDate><itunes:duration>28:49</itunes:duration><content:encoded>Full transcript and show notes at <a href='https://theomnishow.omnigroup.com/episode/andrea-mcvittie-user-experience-designer/'>theomnishow.omnigroup.com</a>
|
||||
|
||||
<p>Andrea studied fine art at <a href="https://msu.edu/">Michigan State</a> and human-computer interaction at the <a href="https://www.umich.edu/">University of Michigan</a>. One day, while working at the school library, it was discovered that she could make websites — which launched her career as a UX designer, and since 2012 she’s been designing apps at Omni.</p>
|
||||
|
||||
<p>Andrea, it must be told, is firmly against stealing money from puppies — or maybe she’s against treating puppies as a particularly cuddly form of currency and then stealing <em>them</em>.</p>
|
||||
<p>Nobody knows which. Don’t do either!</p>
|
||||
<p>You can find Andrea <a href="https://twitter.com/amcvittie">@amcvittie</a> on Twitter. And you can find her work in every single Omni app.</p>
|
||||
<p>Some other people, places, and things mentioned:</p>
|
||||
<ul>
|
||||
<li><a href="https://www.nngroup.com/articles/definition-user-experience/">User Experience (UX)</a></li>
|
||||
<li><a href="https://usabilitygeek.com/ethics-in-user-experience-design/">User Experience and Ethics</a></li>
|
||||
<li><a href="https://twitter.com/monteiro">Mike Monteiro</a></li>
|
||||
<li><a href="https://www.omnigroup.com/blog/omnifocus-now-supports-end-to-end-encryption">OmniFocus Sync Encryption</a></li>
|
||||
<li><a href="https://twitter.com/timothyekl">Tim Ekl</a></li>
|
||||
<li><a href="https://www.omnigroup.com/omnigraffle">OmniGraffle</a></li>
|
||||
<li><a href="https://www.apple.com/apple-pencil/">Apple Pencil</a></li>
|
||||
<li><a href="https://www.omnigroup.com/blog/omni-roadmap-2017-q4-update">OmniFocus Tags</a></li>
|
||||
<li><a href="http://vesperapp.co/">Vesper</a></li>
|
||||
<li><a href="https://theomnishow.omnigroup.com/episode/curt-clifton-omnifocus-engineer">Curt Clifton</a></li>
|
||||
<li><a href="https://twitter.com/jimcorreia">Jim Correia</a></li>
|
||||
<li><a href="https://darkpatterns.org/">darkpatterns.org</a></li>
|
||||
<li><a href="http://www.seattleartmuseum.org/">Seattle Art Museum</a></li>
|
||||
<li><a href="https://twitter.com/OmniGroup/status/921132286527004672">Omni Slack Group</a></li>
|
||||
<li><a href="http://www.rosemaryorchard.com/en">Rosemary Orchard</a></li>
|
||||
<li><a href="https://support.omnigroup.com/">Omni Support</a></li>
|
||||
<li><a href="https://twitter.com/KelStewart84/status/928377769561546752">Listener question from Twitter</a></li>
|
||||
<li><a href="https://lib.msu.edu/">Michigan State Library</a></li>
|
||||
<li><a href="https://twitter.com/Photoshop">Adobe Photoshop</a></li>
|
||||
<li><a href="http://annarborchronicle.com/2012/05/03/in-the-archives-poison-pages/">Shadows from the Walls of Death</a></li>
|
||||
<li><a href="https://www.si.umich.edu/">University of Michigan School of Information</a></li>
|
||||
<li><a href="https://www.instagram.com/p/Baztg5FF7wJ/">Oliver, who’s a good dog</a></li>
|
||||
</ul></content:encoded><itunes:explicit>no</itunes:explicit></item><item><title>Brian Covey, Support Manager</title><itunes:author>The Omni Group</itunes:author><link>https://theomnishow.omnigroup.com/episode/brian-covey-support-manager/</link><description>Brian Covey studied computer science at <a href="http://www.evergreen.edu/">The Evergreen State College</a> — which your host also attended, but back before computers were a thing. After some normal young-person-bouncing-around a little, Brian came to shore at Omni in 2001, where he became the leader, dear and fearless, of Omni’s Support Humans.</description><itunes:image href="https://theomnishow.omnigroup.com/assets/img/omnishow-cover-1x3000.jpg" /><enclosure length="24985882" type="audio/mpeg" url="https://d2vstsfo8knv0p.cloudfront.net/audio/the-omni-show-003.mp3" /><guid>https://theomnishow.omnigroup.com/episode/brian-covey-support-manager/</guid><pubDate>Wed, 22 Nov 2017 14:00:00 +0000</pubDate><itunes:duration>29:32</itunes:duration><content:encoded>Full transcript and show notes at <a href='https://theomnishow.omnigroup.com/episode/brian-covey-support-manager/'>theomnishow.omnigroup.com</a>
|
||||
|
||||
<p>Brian Covey studied computer science at <a href="http://www.evergreen.edu/">The Evergreen State College</a> — which your host also attended, but back before computers were a thing. After some normal young-person-bouncing-around a little, Brian came to shore at Omni in 2001, where he became the leader, dear and fearless, of Omni’s Support Humans.</p>
|
||||
|
||||
<p>You can find Brian <a href="https://twitter.com/hidvorak">@hidvorak</a> on Twitter. (You can contact Omni support at <a href="https://support.omnigroup.com/">support.omnigroup.com</a>.)</p>
|
||||
<p>You can also find a <a href="https://www.omnigroup.com/blog/Yes_-_The_Omni_Video">video of Brian doing the worm</a> in an Omni commercial from 2006. Absolutely not to be missed!</p>
|
||||
<p>Some other people, places, and things mentioned:</p>
|
||||
<ul>
|
||||
<li><a href="https://cherof.bandcamp.com/">The Music of Aaron Cherof</a> (Aaron works at Omni and does the music for this show)</li>
|
||||
<li><a href="http://elevatesummit.co/">Elevate Support conference</a></li>
|
||||
<li><a href="https://twitter.com/kcase">Ken Case</a></li>
|
||||
<li><a href="https://twitter.com/chuckdude">Chuck Toporek</a></li>
|
||||
<li><a href="https://www.omnigroup.com/more">OmniWeb</a></li>
|
||||
<li><a href="https://www2.gov.bc.ca/">British Columbia</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Ninja">Ninjas</a></li>
|
||||
<li><a href="http://starwars.wikia.com/wiki/Droid">Droids</a></li>
|
||||
<li><a href="https://twitter.com/curtclifton">Curt Clifton</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Pair_programming">Pair programming</a></li>
|
||||
<li><a href="https://www.omnigroup.com/omniplan">OmniPlan</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Babylon_5">Babylon 5</a></li>
|
||||
<li><a href="https://www.seattle.gov/visiting-seattle">Seattle</a></li>
|
||||
<li><a href="http://www.netzero.net/">NetZero</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/MacOS">Mac OS X</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Boomerang">Boomerangs</a></li>
|
||||
<li><a href="http://www.nirvana.com/">Nirvana</a></li>
|
||||
<li><a href="https://www.youtube.com/watch?v=o_hngjZUnOw">K Dorm</a></li>
|
||||
<li><a href="https://www.mopop.org/">Experience Music Project</a> (now MoPOP)</li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Dave_Grohl">Dave Grohl</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Nomeansno">Nomeansno</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Operant_conditioning_chamber">Skinner Box</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/The_Mythical_Man-Month">Mythical Man Month</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Spokane,_Washington">Spokane</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Anton%C3%ADn_Dvořák">Antonín Dvořák</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Dvorak_Simplified_Keyboard">Dvorak keyboard</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Repetitive_strain_injury">Repetitive Strain Injury (RSI)</a></li>
|
||||
</ul></content:encoded><itunes:explicit>no</itunes:explicit></item><item><title>Curt Clifton, OmniFocus Engineer</title><itunes:author>The Omni Group</itunes:author><link>https://theomnishow.omnigroup.com/episode/curt-clifton-omnifocus-engineer/</link><description>Curt Clifton came to Omni while on sabbatical from his computer science professorship at <a href="http://www.rose-hulman.edu/">Rose-Hulman</a> — and never went back. He’s spent much of his six years at Omni on the <a href="https://www.omnigroup.com/omnifocus">OmniFocus</a> team, where he clicks checkboxes for a living.</description><itunes:image href="https://theomnishow.omnigroup.com/assets/img/omnishow-cover-1x3000.jpg" /><enclosure length="24354550" type="audio/mpeg" url="https://d2vstsfo8knv0p.cloudfront.net/audio/the-omni-show-002.mp3" /><guid>https://theomnishow.omnigroup.com/episode/curt-clifton-omnifocus-engineer/</guid><pubDate>Mon, 06 Nov 2017 17:00:00 +0000</pubDate><itunes:duration>28:47</itunes:duration><content:encoded>Full transcript and show notes at <a href='https://theomnishow.omnigroup.com/episode/curt-clifton-omnifocus-engineer/'>theomnishow.omnigroup.com</a>
|
||||
|
||||
<p>Curt Clifton came to Omni while on sabbatical from his computer science professorship at <a href="http://www.rose-hulman.edu/">Rose-Hulman</a> — and never went back. He’s spent much of his six years at Omni on the <a href="https://www.omnigroup.com/omnifocus">OmniFocus</a> team, where he clicks checkboxes for a living.</p>
|
||||
|
||||
<p>Curt’s a fan of the local <a href="https://www.soundersfc.com/">association football team</a>. He also likes woodworking and distance-running, though he has yet to explore the synergistic possibilities of doing both at the same time.</p>
|
||||
<p>You can find Curt <a href="https://twitter.com/curtclifton">@curtclifton</a> on Twitter and at <a href="http://curtclifton.net/">curtclifton.net</a>.</p>
|
||||
<p>Some other people, places, and things mentioned:</p>
|
||||
<ul>
|
||||
<li><a href="https://developer.apple.com/ios/drag-and-drop/">iOS 11 drag and drop</a></li>
|
||||
<li><a href="https://www.omnigroup.com/blog/omni-roadmap-2017-q4-update">OmniFocus 3: tags</a></li>
|
||||
<li><a href="https://support.apple.com/en-us/HT206481">Files app</a></li>
|
||||
<li><a href="https://developer.apple.com/wwdc/">World Wide Developer’s Conference (WWDC) (aka Dub Dub)</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Lake_Union">Lake Union</a></li>
|
||||
<li><a href="https://arstechnica.com/gadgets/2013/09/ios-7-thoroughly-reviewed/">iOS 7</a></li>
|
||||
<li><a href="https://www.apple.com/iphone-x/">The notch</a></li>
|
||||
<li><a href="https://macosxautomation.com/applescript/">AppleScript</a></li>
|
||||
<li><a href="https://www.omnigroup.com/omnioutliner">OmniOutliner</a></li>
|
||||
<li><a href="http://ethanschoonover.com/">Ethan Schoonover</a></li>
|
||||
<li><a href="http://shawnblanc.net/2014/03/history-of-omnifocus-for-mac/">Kinkless GTD</a></li>
|
||||
<li><a href="http://gettingthingsdone.com/">Getting Things Done, by David Allen</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Indiana">Indiana</a></li>
|
||||
<li><a href="https://discourse.omnigroup.com/">Omni Forums</a></li>
|
||||
<li><a href="https://twitter.com/kcase">Ken Case</a></li>
|
||||
<li><a href="https://twitter.com/tjw">Tim Wood</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Amish">The Amish</a></li>
|
||||
<li><a href="https://twitter.com/vanillaice">Vanilla Ice</a></li>
|
||||
<li><a href="http://www.imdb.com/title/tt0090555/">Crocodile Dundee</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Pennsylvania_Dutch_Country">Pennsylvania Dutch Country</a></li>
|
||||
<li><a href="http://www.goodnplenty.com">Good ’N Plenty Restaurant</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Amana_Colonies">Amana Colonies</a></li>
|
||||
<li><a href="https://twitter.com/timothyekl">Tim Ekl</a></li>
|
||||
<li><a href="https://twitter.com/jimcorreia">Jim Correia</a></li>
|
||||
<li><a href="https://www.pomona.edu/">Pomona College</a></li>
|
||||
<li><a href="https://www.hmc.edu/">Harvey Mudd College</a></li>
|
||||
<li><a href="https://twitter.com/reid_callan">Reid Callan</a></li>
|
||||
<li><a href="https://twitter.com/tbunch">Tom Bunch</a></li>
|
||||
<li><a href="https://www.mlb.com/cubs">Cubs</a></li>
|
||||
<li><a href="https://www.mlssoccer.com/">Major League Soccer Cup</a></li>
|
||||
<li><a href="http://www.baa.org/races/boston-marathon.aspx">Boston Marathon</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Battle_of_Marathon">Marathon</a></li>
|
||||
<li><a href="https://support.omnigroup.com/">Support Humans</a></li>
|
||||
</ul></content:encoded><itunes:explicit>no</itunes:explicit></item><item><title>Kristina Sontag, Software Test Manager</title><itunes:author>The Omni Group</itunes:author><link>https://theomnishow.omnigroup.com/episode/kristina-sontag-software-test-manager/</link><description>For our first episode we interview Kristina Sontag, who runs Omni’s test department — a group tireless, fearless, and strikingly effective in its hunt for bugs.</description><itunes:image href="https://theomnishow.omnigroup.com/assets/img/omnishow-cover-1x3000.jpg" /><enclosure length="22275279" type="audio/mpeg" url="https://d2vstsfo8knv0p.cloudfront.net/audio/the-omni-show-001.mp3" /><guid>https://theomnishow.omnigroup.com/episode/kristina-sontag-software-test-manager/</guid><pubDate>Wed, 01 Nov 2017 16:00:00 +0000</pubDate><itunes:duration>26:18</itunes:duration><content:encoded>Full transcript and show notes at <a href='https://theomnishow.omnigroup.com/episode/kristina-sontag-software-test-manager/'>theomnishow.omnigroup.com</a>
|
||||
|
||||
<p>For our first episode we interview Kristina Sontag, who runs Omni’s test department — a group tireless, fearless, and strikingly effective in its hunt for bugs.</p>
|
||||
|
||||
<p>Kristina got her start playing games for a living at <a href="http://lucasfilm.com/games">LucasArts</a>. Between there and today she’s also worked on <a href="https://web.archive.org/web/19970105135152/http://www.edmark.com/">educational software</a>, gotten a pilot’s license, volunteered for <a href="http://appcamp4girls.com/">App Camp for Girls</a>, and helped organize the <a href="https://www.weareecs.com/">Emerald City Supporters</a>.</p>
|
||||
<p>When she’s not doing everything else, she’s playing <a href="https://www.destinythegame.com/">Destiny</a>. She may even be thinking about Destiny at this very moment.</p>
|
||||
<p>You can find Kristina on Twitter <a href="https://twitter.com/knsontag">@knsontag</a>.</p>
|
||||
<p>Some other people and places mentioned:</p>
|
||||
<ul>
|
||||
<li><a href="https://developer.apple.com/wwdc/">WWDC</a></li>
|
||||
<li><a href="https://twitter.com/macgenie">Jean McDonald</a></li>
|
||||
<li><a href="https://twitter.com/emarley">Liz Marley</a></li>
|
||||
<li><a href="https://twitter.com/uipoptart">Grey Osten</a></li>
|
||||
<li><a href="https://itunes.apple.com/us/developer/app-camp-for-girls/id942207153">App Camp App</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Santa_Rosa,_California">Santa Rosa, CA</a></li>
|
||||
<li><a href="http://www.ilm.com/">Industrial Light &amp; Magic (ILM)</a></li>
|
||||
<li><a href="http://lucasfilm.com/skywalker-ranch-marin">Skywalker Ranch</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/George_Lucas">George Lucas</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Ron_Gilbert">Ron Gilbert</a></li>
|
||||
<li><a href="http://humongous.com/">Humongous Entertainment</a></li>
|
||||
<li><a href="https://www.omnigroup.com/omnifocus">OmniFocus for iPhone</a></li>
|
||||
<li><a href="https://twitter.com/hidvorak">Brian Covey</a></li>
|
||||
<li><a href="https://twitter.com/kcase">Ken Case</a></li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Floatplane">Float plane</a></li>
|
||||
<li><a href="https://www.kenmoreair.com/">Kenmore Air</a></li>
|
||||
<li><a href="https://www.soundersfc.com/">Seattle Sounders</a></li>
|
||||
<li><a href="https://www.mlssoccer.com/">Major League Soccer (MLS)</a></li>
|
||||
</ul></content:encoded><itunes:explicit>no</itunes:explicit></item></channel></rss>
|
531
Parser/Tests/ParserTests/Resources/vincode.rss
Normal file
531
Parser/Tests/ParserTests/Resources/vincode.rss
Normal file
@ -0,0 +1,531 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>Maurice Parker</title>
|
||||
<link>https://vincode.io/</link>
|
||||
<description></description>
|
||||
|
||||
<language>en</language>
|
||||
|
||||
<lastBuildDate>Thu, 19 Dec 2019 18:12:02 -0500</lastBuildDate>
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2023/03/09/i-updated-a.html</link>
|
||||
<pubDate>Thu, 09 Mar 2023 20:04:10 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2023/03/09/i-updated-a.html</guid>
|
||||
<description><p>I updated a couple of my minor apps today so that they look correct on the latest macOS releases. The first, <a href="https://apps.apple.com/us/app/feed-compass/id1458648487?mt=12">Feed Compass</a> which is an app to help you find, preview, and subscribe to blogs. I also updated <a href="https://apps.apple.com/us/app/feed-curator/id1458647758?mt=12">Feed Curator</a> which is an OPML feed list editor. I also added multiple select for a Reddit user who was needing it. It felt good to help someone out and to finally update the appearance of these apps.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Drag Boat Race in Parker, AZ</title>
|
||||
<link>https://vincode.io/2023/03/07/drag-boat-race.html</link>
|
||||
<pubDate>Tue, 07 Mar 2023 15:15:29 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2023/03/07/drag-boat-race.html</guid>
|
||||
<description><p>About a month ago Nic and I went to drag boat races in Parker, AZ. The start of them is right outside the bar at <a href="https://piratesdenresort.com">Pirates Den</a>. It was pretty fun. We got to drink beer all day and watch the races, then crawl to van at the end of the night to sleep it off. Mike Finnegan from <a href="https://www.motortrend.com/roadkill/">Roadkill</a> was there to race his new boat. That was pretty great for me. I&rsquo;m a huge Roadkill fan. Finnegan basically made a movie about it and put it on Youtube if you are curious as to what the experience there was like.</p>
|
||||
|
||||
<p><center><iframe width="560" height="315" src="https://www.youtube.com/embed/RsvOE2vPUzw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe></center></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2023/03/07/if-someone-has.html</link>
|
||||
<pubDate>Tue, 07 Mar 2023 01:42:31 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2023/03/07/if-someone-has.html</guid>
|
||||
<description><p>If someone has to tell you how good they are at something, their work doesn’t stand on its own. If someone won’t explain why they made a decision, it is because they know that it was a bad one. If someone tells you that you aren’t worth listening to, its because they are afraid of what you will say.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2023/03/06/the-main-project.html</link>
|
||||
<pubDate>Mon, 06 Mar 2023 15:46:42 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2023/03/06/the-main-project.html</guid>
|
||||
<description><p>The main project I work on, <a href="https://micro.blog/NetNewsWire">@NetNewsWire</a> got a shout out in the Atlantic today. <a href="https://www.theatlantic.com/ideas/archive/2023/03/social-media-algorithms-twitter-meta-rss-reader/673282/?utm_source=feed">How to Take Back Control of What You Read on the Internet</a></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2023/03/06/i-messed-up.html</link>
|
||||
<pubDate>Mon, 06 Mar 2023 14:54:42 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2023/03/06/i-messed-up.html</guid>
|
||||
<description><p>I messed up a <a href="https://micro.blog/NetNewsWire">@NetNewsWire</a> merge recently. I left some Git merge conflict markers in a couple files. <a href="https://micro.blog/danielpunkass">@danielpunkass</a> suggested that a pre-commit <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git Hook</a> could prevent this in the future. <a href="https://gist.github.com/vincode-io/48f3394f379bac2351127dbe630f2233">So I made one.</a>.
|
||||
Daniel has added this to his <a href="https://coderwall.com/p/jp7d5q/create-a-global-git-commit-hook">global Git templates</a>. You should too.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2023/02/03/just-another-day.html</link>
|
||||
<pubDate>Fri, 03 Feb 2023 13:15:33 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2023/02/03/just-another-day.html</guid>
|
||||
<description><p>Just another day at the office.</p>
|
||||
|
||||
<p><img src="uploads/2023/869fb8af1f.jpg" width="600" height="450" alt=""></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2023/02/01/i-got-new.html</link>
|
||||
<pubDate>Wed, 01 Feb 2023 13:50:13 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2023/02/01/i-got-new.html</guid>
|
||||
<description><p>I got new wheels and tires on the van. They look great, but the larger size puts more strain on my little 4.6 V8. I completely blew a spark plug out of the engine from pushing it to hard coming up a mountain pass. Life on the road…</p>
|
||||
|
||||
<p><img src="uploads/2023/e39e028e03.jpg" width="600" height="450" alt=""></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2022/12/25/christmas-dinner-this.html</link>
|
||||
<pubDate>Sun, 25 Dec 2022 17:09:16 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/12/25/christmas-dinner-this.html</guid>
|
||||
<description><p>Christmas dinner this year is Ramen on Sunset Blvd.</p>
|
||||
|
||||
<p><img src="uploads/2022/89cdd3889c.jpg" width="600" height="600" alt=""></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2022/11/14/i-really-like.html</link>
|
||||
<pubDate>Mon, 14 Nov 2022 13:46:25 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/11/14/i-really-like.html</guid>
|
||||
<description><p>I really like Dan Rather&rsquo;s Substack newsletter, Steady. It has some great social commentary on it. Dan Rather is a journalist from a time when news wasn&rsquo;t entertainment. <a href="https://steady.substack.com">steady.substack.com</a></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2022/11/14/it-would-be.html</link>
|
||||
<pubDate>Mon, 14 Nov 2022 13:43:08 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/11/14/it-would-be.html</guid>
|
||||
<description><p>It would be great if Substack had RSS feeds for its Inbox and Discover timelines. I understand why they don&rsquo;t. They really need to get users into paid subscriptions to keep the site going. I guess we should be glad that they at least have RSS feeds for individual newsletters.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2022/11/07/yeah-its-one.html</link>
|
||||
<pubDate>Mon, 07 Nov 2022 16:03:47 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/11/07/yeah-its-one.html</guid>
|
||||
<description><p>Yeah. It’s one of <em>those</em> days.</p>
|
||||
|
||||
<p><img src="uploads/2022/001e80fb82.jpg" width="600" height="450" alt=""></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2022/11/01/id-love-to.html</link>
|
||||
<pubDate>Tue, 01 Nov 2022 12:41:24 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/11/01/id-love-to.html</guid>
|
||||
<description><p>I&rsquo;d love to have <a href="https://developer.apple.com/ios/universal-links/">Universal Links</a> on the Micro.blog Apple apps. Since they are Open Source, I&rsquo;d implement myself. I can&rsquo;t see how it can be done. <a href="https://micro.blog/manton">@manton</a> rightly encourages everyone have a domain. That makes it impossible to do consistently.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Ventura’s System Settings</title>
|
||||
<link>https://vincode.io/2022/10/30/venturas-system-settings.html</link>
|
||||
<pubDate>Sun, 30 Oct 2022 11:58:26 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/10/30/venturas-system-settings.html</guid>
|
||||
<description><p>I have to admit, when I saw screenshots of Ventura’s new System Settings stuff, I was very unimpressed. It looked too much like you would see on Windows or Linux for me.</p>
|
||||
|
||||
<p><img src="https://vincode.io/uploads/2022/c5a5977b4c.png" alt="" /></p>
|
||||
|
||||
<p>Now that I’ve actually used it, I appreciate the design decision that was made. I know a lot of people were critical, myself included, about basing the design of the System Settings on the iOS ones. But making it like the iOS ones reduces cognitive load when switching back and forth a lot more than I thought it would. I can find the setting that I want much easier now.</p>
|
||||
|
||||
<p>There is a lot to criticize though. It feels clunky, like it is missing some animations. For example, if you have the right pointing chevron, it should animate you that direction. I know that style of navigation sounds very iOS like, but the column view in Finder works that way too.</p>
|
||||
|
||||
<p>It also doesn’t feel like it fits in quite right with the rest of macOS. I haven’t read Apple’s HIG yet this year so that could be it. My guess is that we will see the rest of the move more this direction again next year.</p>
|
||||
|
||||
<p>Because so many more people own an iPhone than a Mac, making Macs easier for iPhone owners to use is a good strategy. Lots of the same people who are critical of recent macOS changes are also the same ones who are frustrated that Apple isn’t doing enough to grow the Mac user base. It looks like Apple is working to leverage the popularity of the iPhone and the iPad to win over users. Between that and the new Mac hardware, it looks to me like they are very focused on the success of the Mac.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2022/02/10/i-wrote-a.html</link>
|
||||
<pubDate>Thu, 10 Feb 2022 15:20:03 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/02/10/i-wrote-a.html</guid>
|
||||
<description><p><a href="https://zavala.vincode.io/2022/02/10/Zavala_and_Micro.blog.html">I wrote a blog post </a>about how I blog to Micro.blog using <a href="https://zavala.vincode.io">Zavala</a> and <a href="https://apps.apple.com/us/app/humboldt/id1592768206">Humboldt</a>. Blogging this way probably isn&rsquo;t for you unless you are big into outliners. Still it is neat to see how these two computer systems that weren&rsquo;t designed to work together could be made to.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Software Development And Vanlife</title>
|
||||
<link>https://vincode.io/2022/02/03/software-development-and.html</link>
|
||||
<pubDate>Thu, 03 Feb 2022 18:35:00 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/02/03/software-development-and.html</guid>
|
||||
<description>
|
||||
|
||||
<p>Part of the year I live out of my self-converted E-250 Campervan. My wife and our 2 cats are also traveling with me. That doesn’t leave a lot of space for an office to do software development. This is how I do it.</p>
|
||||
|
||||
<h2 id="my-portable-office">My Portable Office</h2>
|
||||
|
||||
<p>Wouldn’t it be great if you had a full office with full-size keyboard, monitor, printer, and the works while living in a van? No chance.</p>
|
||||
|
||||
<h3 id="desk">Desk</h3>
|
||||
|
||||
<p>I wish I had a desk. My lap is my desk and when sometimes a picnic table. The picnic table is a rare treat since we mostly do what is called boondocking. That is where you don’t have any hookups for electric or water. Boondocking is typically done on public lands and is free.</p>
|
||||
|
||||
<p>Sometimes I sit in the passenger seat of the van. Right now, I’m sitting in the back of the van on my couch which converts into my bed at night. My favorite is sitting in a chair outside as long as the glare isn’t too bad.</p>
|
||||
|
||||
<p><img src="https://cdn.uploads.micro.blog/6481/2022/c709bf0ed5.png" alt="" /></p>
|
||||
|
||||
<h3 id="computer">Computer</h3>
|
||||
|
||||
<p>I have one of the new 16” M1 MacBook Pro’s. It compiles code fast. Like, really fast. It makes working on big projects much less painful. The battery life is unbelievable. This is really important in a van when off the grid. Electricity is at a premium. I’m usually able to charge the laptop after the van’s batteries have been topped off and don’t need any more power. The MacBook will last with me using it as much as I want until the next day.</p>
|
||||
|
||||
<p>The speakers are also something that needs to be heard to be believed. We stayed at a friend’s condo in Redondo Beach for 3 weeks over the holidays. The whole time we used my computer as a television and the incredible sound capabilities made this much nicer.</p>
|
||||
|
||||
<h3 id="purse">Purse</h3>
|
||||
|
||||
<p>Man purse? Computer bag? I’m not sure what to call it. It was a purse that I bought at REI.</p>
|
||||
|
||||
<p><img src="https://cdn.uploads.micro.blog/6481/2022/cbc7a80991.png" alt="" /></p>
|
||||
|
||||
<p>I don’t have a conventional computer bag. A conventional computer bag is too bulky to fit in my limited van space. I have a sleeve for the MacBook and this little bag to hold my cables, headphones, computer glasses, and charging brick.</p>
|
||||
|
||||
<p>It works out pretty well. If I want to work in a coffee shop, I just through the purse over my shoulder and carry the computer in its case. The rest of the time everything fits into my clothing drawer with my clothes.</p>
|
||||
|
||||
<h3 id="internet">Internet</h3>
|
||||
|
||||
<p>I use cellular data for most everything I do. I have 1GB per month of data on a Verizon plan. I never come close to using all of it.</p>
|
||||
|
||||
<p>The real tricky part is getting data while away from civilization. Most of the time I can just tether my computer to my phone and it is fine. Sometimes if I’m just out of reach of good cellular service I use my cellular booster in the van. I have a Weboost Drive 4g-X with a marine antenna. This will boost a weak, unusable signal into one that is very serviceable.</p>
|
||||
|
||||
<p><img src="https://cdn.uploads.micro.blog/6481/2022/d8be3913d2.png" alt="" /></p>
|
||||
|
||||
<h2 id="recent-project-developments">Recent Project Developments</h2>
|
||||
|
||||
<p>I’m always working on something.</p>
|
||||
|
||||
<h3 id="netnewswire-6-1">NetNewsWire 6.1</h3>
|
||||
|
||||
<p>We’ve released development versions of NetNewsWire 6.1 for both iOS and the Mac. The <a href="https://nnw.ranchero.com/2022/01/17/netnewswire-b-for.html">blog post for the 6.1b2 version for the Mac</a> has good info in it. The tentpole feature for this release is Themes.</p>
|
||||
|
||||
<p>Themes are the ability change how the Article is rendered in NetNewsWire. This can be a matter of personal taste and sometimes a matter of accessibility. For example if a font is pretty, but difficult to read, you can change that font so that to one that is easier to read. You can also change Article colors, sizes, and key field placements. All this is pretty easy to do if you have basic Web development skills.</p>
|
||||
|
||||
<h3 id="netnewswire-6-2">NetNewsWire 6.2</h3>
|
||||
|
||||
<p>The NetNewsWire team will occasionally work on multiple releases at the same time. This time we are testing version 6.1 while developing version 6.2. For version 6.2 we plan to finally add FeedWrangler syncing for macOS and iOS.</p>
|
||||
|
||||
<p>On iOS, we’re also updating the user interface to look more at home on recent version of iOS. My NetNewsWire teammate, <a href="https://mynameisstuart.com/">Stuart</a>, and I have been hard at work on the iOS UI refresh. I’m pretty happy with the progress thus far. Stuart has also contributed a Notification manager that will make working with Notifications much easier.</p>
|
||||
|
||||
<h3 id="zavala-2-0-release">Zavala 2.0 Release</h3>
|
||||
|
||||
<p>I managed the <a href="https://zavala.vincode.io/2022/01/31/2.0_Release.html">second major release of Zavala</a> while working out of the van in the desert close to Holtzville, CA. This release is a big deal to me. Thanks to <a href="https://twitter.com/bradellis?s=21">Brad Ellis</a> and some elbow grease from me, this release really feels professional. I mean, it feels like there is a real budget with several team members from a good company kind of professional.</p>
|
||||
|
||||
<p>The big deal with this release is <a href="https://support.apple.com/guide/shortcuts/welcome/ios">Shortcuts</a> support. This really makes Zavala much more customizable and powerful. I hope the users end up finding this useful. I use it all the time, from backing up my outline database, to posting to different blogging systems.</p>
|
||||
|
||||
<h2 id="vanlife-is-often-boring">Vanlife is Often Boring</h2>
|
||||
|
||||
<p>What you don’t often get from Instagram #vanlife stuff is that there are lots of times that you have nothing to do. If you are off the grid for 4 or more days at a time with nothing but scrub brush and sand, what do you do? I’m fortunate enough that my hobby, software development, is still possible even in those conditions.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2022/01/31/zavala-is-a.html</link>
|
||||
<pubDate>Mon, 31 Jan 2022 19:08:50 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2022/01/31/zavala-is-a.html</guid>
|
||||
<description><p>Zavala is a modern outliner for Mac, iPhone, and iPad. Version 2.0 has just been release with a refined user interface and support for Shortcuts. <a href="https://zavala.vincode.io/2022/01/31/2.0_Release.html">Read all about the 2.0 release.</a></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2021/12/17/this-is-awesome.html</link>
|
||||
<pubDate>Fri, 17 Dec 2021 14:27:30 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/12/17/this-is-awesome.html</guid>
|
||||
<description><p>This is awesome: <a href="https://blog.iconfactory.com/2021/12/notches-gone-wild/">blog.iconfactory.com/2021/12/n&hellip;</a></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Radford Racing School</title>
|
||||
<link>https://vincode.io/2021/12/10/radford-racing-school.html</link>
|
||||
<pubDate>Fri, 10 Dec 2021 18:47:31 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/12/10/radford-racing-school.html</guid>
|
||||
<description>
|
||||
|
||||
<h2 id="cars">Cars</h2>
|
||||
|
||||
<p>I’ve always loved fast cars. Most of my adult life has been focused on being the best software developer that I can be, so my other hobbies, like cars, suffered. Now that I am retired, I did some life reassessing. One of the things that I feel like I missed out on was enjoying cars more. I decided that I wanted to do more than just zip around on the street. I wanted to race cars on a track.</p>
|
||||
|
||||
<p>I don’t have any professional aspirations to become a race car driver. I just want to do my best to win and enjoy the process. I decided that I would race the car I have rather than buy or build another one. I have a 1971 Corvette that has had the suspension, brakes, engine, and transmission all updated. There is a little more work that I need to do to it to get it ready for autocross or road racing, but I think it will be a good starter car.</p>
|
||||
|
||||
<p><img src="https://vincode.io/uploads/2021/1dca90c8be.png" alt="" /></p>
|
||||
|
||||
<h2 id="racing-school">Racing School</h2>
|
||||
|
||||
<p>Since I know very little about road racing, I figured that I should go to a school to learn more. <a href="https://www.radfordracingschool.com">Radford Racing School</a> came up immediately when I began searching for a racing school. I had a bit of good luck. They were having a Black Friday sale on classes and they were located in Arizona, which is where we were headed for the winter. I booked a 3 day high performance driving course almost immediately.</p>
|
||||
|
||||
<p>I probably wouldn’t have gone if not for the Black Friday sale, just due to the cost alone. After going to Radford, I get why it is so expensive. They have two full-size race tracks, a big autocross course and a couple skid pads. There is a large full-time staff of instructors, administrative people, and mechanics. Their fleet of cars is massive and requires constant maintenance. They go through gas and rubber like nobody’s business. I think the school is actually reasonably priced for what you get.</p>
|
||||
|
||||
<p><img src="https://vincode.io/uploads/2021/63f566ed36.png" alt="" /></p>
|
||||
|
||||
<h2 id="classes">Classes</h2>
|
||||
|
||||
<p>We had some class room time where fundamentals were taught us. Things like where to look and how weight transfer affected traction were some of the subject we were taught. There was surprisingly, little time spent behind a desk. Most of our time was behind the wheel.</p>
|
||||
|
||||
<p>One of the first and most important things you learn is accident avoidance. We talk about it in class and they run you through a series of drills to make sure you can pull off the maneuvers. Some of the maneuvers were pretty hard. Braking at 65 MPH while swerving to miss an obstacle was quite challenging in the distance they gave you.</p>
|
||||
|
||||
<p>We did a lot of autocross to hone our skills before hitting the track. I didn’t think I would enjoy the autocross stuff as much as I did. Racing your car through cones just didn’t seem like a lot of fun, but I was wrong. It might be something that I try and get the Corvette out to do next summer.</p>
|
||||
|
||||
<p>We also got a a lot of driving time on the full size race tracks. I had the time of my life. We also got some good one-on-one time with the instructors who would both take you for ride alongs and would ride with you. There is nothing better for learning than to be in the same car with someone who really knows how to move that machine around.<img src="https://vincode.io/uploads/2021/a2c7033b13.png" alt="" /></p>
|
||||
|
||||
<h2 id="was-it-worth-it">Was it worth it?</h2>
|
||||
|
||||
<p>Yes, in fact I probably will go again. They offer more advanced courses that go beyond what we did. I was learning new things right up till the very last minute and know that I have more to learn. First though, I’m going to take what I learned and refine it. Next summer I hope to get to do some road racing where I have a chance to develop myself and maybe then I can get more out of a follow up course.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Vanlife 2021</title>
|
||||
<link>https://vincode.io/2021/12/04/vanlife.html</link>
|
||||
<pubDate>Sat, 04 Dec 2021 18:50:49 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/12/04/vanlife.html</guid>
|
||||
<description>
|
||||
|
||||
<h2 id="2020-sucked-for-everyone">2020 Sucked (For Everyone)</h2>
|
||||
|
||||
<p>We got back to Centerville, IA just as the world was entering lockdown in March of 2020. We were pretty strick with out protocols, so we didn’t even much leave the house until vaccines became widely available. Needless to say, there wasn’t any van adventuring happening, but Nic got really good at making sourdough bread and I wrote an <a href="https://zavala.vincode.io">outliner application</a>.</p>
|
||||
|
||||
<p>I’ve got no regrets about how we spent 2020 and most of 2021. It was hard, but my parents are getting up there in age and Nicole is an ex-smoker. Covid could have taken anyone of us or left us with a permanent disability. Thus far it hasn’t and I will do what I can to keep it that way.</p>
|
||||
|
||||
<h2 id="covid-and-vanlife">Covid and Vanlife</h2>
|
||||
|
||||
<p>We spend a lot of time isolated and outside while living out of the van. I think most people focus on that and don’t realize how much time we spend in public places while traveling. The most common public place we go to is the gym. That is our main place to shower.</p>
|
||||
|
||||
<p>We decided that since we were vaccinated and boostered that it would be safe enough for us to get back to using the gym again. But just in case we got some variant that is vaccine resistant, I installed a shower on the van so that we could avoid the gym if we needed to.</p>
|
||||
|
||||
<p><img src="https://vincode.io/uploads/2021/e45fc0c09a.png" alt="" /></p>
|
||||
|
||||
<p>It holds 7 gallons of water and is black so that the sun can heat it. You pressurize it using compressed air.</p>
|
||||
|
||||
<p>We used it for the first time yesterday. I used my onboard air compressor to give it pressure. Thankfully the shower has a pressure release valve. I over compressed it (after only a minute) and it shot water two feet above the top of the van as I frantically tried to shut it down. Afterwards Nicole successfully used it to wash her hair. The campground we are currently at in Kearney, AZ doesn’t have any showers and we are a long way from a gym, so the new shower immediately proved itself.</p>
|
||||
|
||||
<h2 id="vintage-campers">Vintage Campers</h2>
|
||||
|
||||
<p>Backtracking a bit, we stopped at a really nice campsite that had restored, vintage campers and some cars in Albuquerque, NM on our way to Arizona. After getting settled there, we immediately grabbed a couple beers and toured it.</p>
|
||||
|
||||
<p><img src="https://vincode.io/uploads/2021/afc84d5964.png" alt="" /><img src="https://vincode.io/uploads/2021/6d90da3059.png" alt="" /><img src="https://vincode.io/uploads/2021/dde8d04125.png" alt="" /></p>
|
||||
|
||||
<h2 id="radford-racing-school">Radford Racing School</h2>
|
||||
|
||||
<p>Our next stop is in Chandler, AZ and the <a href="https://www.radfordracingschool.com">Redford Racing School.</a> They have a 3 day performance driving course that Nicole and I are going to take. We are really looking forward to the adventure. I’m very much a car guy and Nic just likes to drive fast. We’ll be driving Challenger Redeye’s that have almost 800 HP. Those cars have two different keys. You have to have a special one to unlock the full engine potential. I doubt we get that key right away, but hopefully by the end of the course we’ll get to open those Mopar’s up.</p>
|
||||
|
||||
<h2 id="feels-good">Feels Good</h2>
|
||||
|
||||
<p>We’ve been out for about a week now and are finally getting back in the groove and it feels good. Everything is a lot more work, but the routine becomes the norm, and soon we notice it less and less. Eventually, it will seem like this is how we’ve always lived and we will have to work to remember what it is like to live in a normal home.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Shortcuts for Micro.blog</title>
|
||||
<link>https://vincode.io/2021/11/01/shortcuts-for-microblog.html</link>
|
||||
<pubDate>Mon, 01 Nov 2021 20:16:12 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/11/01/shortcuts-for-microblog.html</guid>
|
||||
<description>
|
||||
|
||||
<h2 id="announcing-humboldt">Announcing Humboldt</h2>
|
||||
|
||||
<p><a href="https://github.com/vincode-io/Humboldt">Humboldt</a> is a new Open Source project I put together so that I could use <a href="https://support.apple.com/guide/shortcuts/welcome/ios">Shortcuts</a> to post to <a href="https://micro.blog">Micro.blog</a>. Humboldt is built on top of <a href="https://github.com/microdotblog/snippets">Snippets</a> from Micro.blog, which does the heavy lifting. Humboldt only exposes a small portion of Snippets, so there is room for Humboldt to grow in the future if there is demand for more functionality.</p>
|
||||
|
||||
<p><a href="https://apps.apple.com/us/app/humboldt/id1592768206">Humboldt is available for both macOS and iOS in the App Store</a>.</p>
|
||||
|
||||
<h2 id="the-main-humboldt-app">The Main Humboldt App</h2>
|
||||
|
||||
<p>The main app walks you though the usual Micro.blog sign-on flow. You just enter the email address you use for Micro.blog and wait for your sign-on email to arrive. When it does you just click a link in the email and you will be routed back to Humboldt which completes the sign-on for you. That’s all the main app does. You are now ready to begin building Shortcuts that communicate to Micro.blog.</p>
|
||||
|
||||
<h2 id="post-html-to-micro-blog">Post HTML to Micro.blog</h2>
|
||||
|
||||
<p>The main shortcut to use is the Post HTML one. You can submit just a short HTML snippet or a full blog post. The Title is an optional parameter, since lots of Micro.blog posts are short and don’t require them.</p>
|
||||
|
||||
<p><a href="https://www.icloud.com/shortcuts/8d0c34942cee4770a86a79e76def1aeb">Here</a> is a simple Shortcut that just asks for what you want to post and then does it. You can use this Shortcut completely hands free to post to Micro.blog.</p>
|
||||
|
||||
<p><img src="https://vincode.io/uploads/2021/6aa652fd04.png" alt="" /></p>
|
||||
|
||||
<h2 id="upload-image-to-micro-blog">Upload Image to Micro.blog</h2>
|
||||
|
||||
<p>You can also upload image resources to Micro.blog. <a href="https://www.icloud.com/shortcuts/fedaf0ef3ded419196c37347e7575ae3">This Shortcut example</a> shows it in action.
|
||||
<img src="https://vincode.io/uploads/2021/b5e3db9ee5.png" alt="" /></p>
|
||||
|
||||
<h2 id="get-blog-id-from-micro-blog">Get Blog ID from Micro.blog</h2>
|
||||
|
||||
<p>This is useful if you have multiple Micro.blog blogs associated with your account. You can pass the result of this action to either Post HTML for Upload Image to work with the blog you want. Otherwise, those actions work on the currently selected default blog. You can change the current default blog using Micro.blog on the web.</p>
|
||||
|
||||
<h2 id="putting-them-together">Putting Them Together</h2>
|
||||
|
||||
<p>You can upload blog posts with images embedded in them. In fact, that is how this blog post was uploaded to Micro.blog!</p>
|
||||
|
||||
<p>The Upload Image action has an output parameter. It is the published URL of the image. You need to save this for each image you upload and pass it to a step that manipulates the HTML of the post you will upload.</p>
|
||||
|
||||
<p>Your HTML needs to have placeholder text, unique to each image, that can be searched for. This placeholder text needs to be substituted for the image’s published URL. You can use the Replace Text built-in Shortcut action to do this. Now you can use Post HTML to post your updated blog HTML.</p>
|
||||
|
||||
<h2 id="zavala-integration">Zavala Integration</h2>
|
||||
|
||||
<p>I wanted the ability to build a blog post in <a href="https://zavala.vincode.io">Zavala</a> and then post it to Micro.blog. This is the original reason why I built Humboldt. Shortcut support for Zavala is “coming soon”, which is needed to tie everything together. When it is released to testing, I will post more about how to make Humboldt work with Zavala.</p>
|
||||
|
||||
<h2 id="future-updates">Future Updates</h2>
|
||||
|
||||
<p>For now, Humboldt does everything I need it to do. It just posts to Micro.blog and that is about it. It can’t edit or delete posts or manipulate image uploads. You can use the Micro.blog website for that or a full featured blog editor like <a href="https://redsweater.com/marsedit/">MarsEdit</a>.</p>
|
||||
|
||||
<p>If there is community interest in expanding what Humboldt can do, I’m interested in hearing about it. Let me know what you would like to see on the <a href="https://github.com/vincode-io/Humboldt/discussions">Humboldt’s GitHub Discussions site</a>.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Drummer</title>
|
||||
<link>https://vincode.io/2021/10/17/drummer.html</link>
|
||||
<pubDate>Sun, 17 Oct 2021 13:49:00 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/10/17/drummer.html</guid>
|
||||
<description>
|
||||
|
||||
<h2 id="the-oldest-and-newest-thing-in-blogging">The Oldest and Newest Thing in Blogging</h2>
|
||||
|
||||
<p><a href="http://scripting.com">Dave Winer</a> just release a new project called <a href="http://docserver.scripting.com/drummer/about.opml">Drummer</a>. Drummer is an <a href="https://en.wikipedia.org/wiki/Outliner">outliner</a> that has been especially adapted to doing blogging. If you are into either outliners or blogs, this is an interesting development. Dave is probably the oldest name in outline, blogging, and podcasting applications. You can check out his <a href="https://en.wikipedia.org/wiki/Dave_Winer">Wikipedia page</a> for a full history.</p>
|
||||
|
||||
<p>I’m not very familiar with the history of outliners and blogging, but I do know in certain circles, especially the Mac community, early blogs were based on outlines. Over the years it seems like those two have drifted apart into their own applications and communities.</p>
|
||||
|
||||
<h2 id="personal-interest">Personal Interest</h2>
|
||||
|
||||
<p>I stumbled into the blogging world long before I had heard of blogs. In the very early 2000’s personal blogs weren’t very well known, but sites like <a href="https://slashdot.org">Slashdot</a> were. Slashdot was what we would now refer to as a link blog. A link blog is one that links to external articles and provides commentary about them. I decided I wanted my own link blog and so I built one based on the (at the time) open source Jive forum software. The site itself never turned into anything, but it got me my first Java consulting gig.</p>
|
||||
|
||||
<p>Like many people I eventually drifted away from blogs into the world of social platforms. I eventually got interested in doing Apple platform development and started looking for a good open source application to work on. I found <a href="https://netnewswire.com">NetNewsWire</a>. I originally began work on NetNewsWire because the code was a good model to learn advanced techniques from. Along the way I rediscovered how amazing blogging is.</p>
|
||||
|
||||
<p>Through <a href="https://inessential.com">Brent Simmons</a>, the founder of NetNewsWire, I learned about how useful outliners could be. I was familiar with outliners, but hadn’t used them to their full potential. I got more and more interested in outliners and eventually began work on <a href="https://zavala.vincode.io">Zavala</a>. Through Brent, I also learned about publishing outlines as blogs.</p>
|
||||
|
||||
<h2 id="outlines-as-blogs">Outlines as Blogs</h2>
|
||||
|
||||
<p>I’ve always used outlines to do my writing since I learned about them. The hierarchal structure in writing just made sense to me. As a software consultant, I alway wrote using Word’s outline view. To me it was just natural to want to use something like that for blogs. When I heard about the history of using outlines for blogging, things just clicked.</p>
|
||||
|
||||
<p>As part of Zavala 2.0, I wanted to satisfy the blog using an outline use case. In fact, this blog post is written using Zavala.
|
||||
<img src="https://vincode.io/uploads/2021/a9c6cbe926.png" alt="" />
|
||||
To publish this outline, I run a Shortcut that uploads images and HTML to my <a href="https://micro.blog">Micro.blog</a> blog. Drummer works somewhat differently. Zavala considers an outline a document that gets uploaded as a post. Drummer is designed to group multiple entries under a published date. Each entry is a separate blog post, but are all grouped together by date. It also has a publish step, but what happens, happens behind the scenes.</p>
|
||||
|
||||
<p><img src="https://vincode.io/uploads/2021/328aa1ad88.png" alt="" />
|
||||
It is important here to point out some terminology differences. A “note” in Zavala is associated with a row. It provides additional detail for the topic. When publishing docs or blog posts from Zavala a topic is a group heading and a note is paragraph text.</p>
|
||||
|
||||
<p>A “note” in Drummer is a row that is a blog entry. I’m not sure what makes a “note” row different than a regular row under a date. They seem to publish the same, but the icon is different some how.</p>
|
||||
|
||||
<p>Drummer has the concept of a microblog post, which Zavala doesn’t have. This is similar to what you see on Micro.blog or Twitter. It is a short post that doesn’t have a title. If you add a child row under a row that is under a date, you get a full blog post where the child rows are paragraphs and the row is the blog title.</p>
|
||||
|
||||
<p>Drummer’s take on blogging is a little bit different different than the common definition. I think Drummer is set up well for a prolific writer or someone who posts a lot in smaller chunks through out the day. It scales up well for long posts or quick posts just to express an opinion as well through.</p>
|
||||
|
||||
<h2 id="what-s-next">What’s next</h2>
|
||||
|
||||
<p>If I’ve sold you on blogging using outlines, check out this <a href="https://www.amitgawande.com/2021/10/16/getting-started-blogging.html">nice article on blogging with Drummer</a>. You will eventually be able to do blogging on some platforms with Zavala 2.0, but that won’t be ready until early 2022. In the meantime, Drummer is something to try.</p>
|
||||
|
||||
<p>Some caveats about Drummer though. You will have to think differently if you are used to common forms of blogging. Using a single outline for all your blog entries is very different than publishing different documents as blog entries.</p>
|
||||
|
||||
<p>Drummer is also complicated in some ways. For example, adding an inline image requires you to edit a rows attributes, enter a special key, and link out to a site that is hosting your image. Drummer is new though and it is’’t like this is something that can’t be made easier in a follow up release.</p>
|
||||
|
||||
<p>I’m going to be keeping an eye on Drummer for things I can learn from it. I’ll also be looking for opportunities for interoperablility. There might be something there that can make both projects better.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>Shortcuts</title>
|
||||
<link>https://vincode.io/2021/10/13/shortcuts.html</link>
|
||||
<pubDate>Wed, 13 Oct 2021 12:47:12 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/10/13/shortcuts.html</guid>
|
||||
<description>
|
||||
|
||||
<p>I’ve been spending a lot of time lately working with <a href="https://support.apple.com/en-in/guide/shortcuts/welcome/ios">Shortcuts</a>. I have some thoughts about them.</p>
|
||||
|
||||
<h2 id="adding-shortcut-support-to-zavala-https-zavala-vincode-io">Adding Shortcut support to <a href="https://zavala.vincode.io">Zavala</a></h2>
|
||||
|
||||
<p>One of the early requirements for Zavala was that it should be able to be automated or scripted. Since Zavala works on both iOS and macOS, these scripts should be able to be shared.</p>
|
||||
|
||||
<p>Prior to WWDC21 there wasn’t a shared scripting environment common between iOS and macOS. AppleScript was macOS only and Shortcuts was iOS only. I eventually decided that I would embed a Python runtime and editor to get cross-platform scripting to work.</p>
|
||||
|
||||
<p>When Shortcuts for macOS was announced at WWDC21 this year, I now had a different option. My only concern was if Shortcuts would be powerful enough to script a full outliner.</p>
|
||||
|
||||
<h2 id="discovering-shortcuts">Discovering Shortcuts</h2>
|
||||
|
||||
<p>I had briefly worked on Shortcut support a couple years ago for <a href="https://netnewswire.com">NetNewsWire</a> but we ran into some roadblocks. These have since been removed. Still, because of them we didn’t get the full Shortcuts support we would have wanted and I didn’t fully learn how to use Shortcuts.</p>
|
||||
|
||||
<p>Cut to a couple years later and I wanted to do some really powerful automation with Zavala. For example, I wanted to post outlines as blog posts on both <a href="https://micro.blog">Micro.blog</a> and a <a href="https://jekyllrb.com">Jekyll</a> blog. I wanted to dynamically create outlines.</p>
|
||||
|
||||
<p>I’m happy to say that with some enhancements to Zavala to support Shortcuts, I&rsquo;ve been able to do everything (thus far) that I’ve set out to do. I’ve written over a dozen Shortcuts, some of them fairly complicated. I was surprised at what I could accomplish with a graphical programming language.</p>
|
||||
|
||||
<h2 id="shortcuts-are-very-capable">Shortcuts are Very Capable</h2>
|
||||
|
||||
<p>I’ve come to think of Shortcuts as the graphical programming equivalent of Unix shell scripts. You are expected to piece together a lot of small Shortcut actions to make a full Shortcut. Each of the Shortcut actions take an input and provide an output.</p>
|
||||
|
||||
<p>One of the big differences between Shortcuts and shell scripts is that you can define the type of data that you are using for input and output. Shortcuts has something called The <a href="https://support.apple.com/en-in/guide/shortcuts/apd4618db957/ios">Content Graph engine</a> which can convert data to different types on the fly, so using typed data isn’t as verbose as it could be. For example, you could pass an array of URL’s to an action that only accepts a single URL. Shortcuts will automatically pick the first (usually the only) item in the array without having to specify it. Or you could pass a URL to an action that takes a Text input and Shortcuts will change it to a text input.</p>
|
||||
|
||||
<h2 id="shortcomings">Shortcomings</h2>
|
||||
|
||||
<p>In iOS 15 and macOS 12, the Shortcut editor is buggy. Embarrassingly buggy. The delete button for actions almost never works. Yes, something as simple as a button not doing the simplest of tasks, doesn’t work. And it crashes a lot. Fortunately, the execution engine for Shortcuts seems to be stable and work well.</p>
|
||||
|
||||
<p>One thing I would like to see is the ability to add a Shortcut to the Share menu on macOS. You can already add it to the Share menu on iOS. You can add a Shortcut to the Services menu on macOS, so maybe that is the more appropriate location. I do think users are going to expect Shortcuts to be able to be available in the macOS Share menu since it shares an icon with the iOS equivalent.</p>
|
||||
|
||||
<p>The biggest thing I think is missing is an API to execute Shortcuts directly from within your application. Say, I write a Shortcut that moves all completed items in an outline to a row called “Done” at the bottom of the outline. I would like to be able to execute this Shortcut without leaving the application and without focus being given to the Shortcuts app. Right now that isn’t possible to the best of my knowledge.</p>
|
||||
|
||||
<h2 id="the-future">The Future</h2>
|
||||
|
||||
<p>It is pretty clear to me that Shortcuts are the future of Apple platform automation. It is powerful while still being accessible to non-programmers. I hope that this year’s quality problems are a short-term setback, because I’m all in for automating Zavala using Shortcuts.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2021/10/08/some-cool-stuff.html</link>
|
||||
<pubDate>Fri, 08 Oct 2021 15:01:05 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/10/08/some-cool-stuff.html</guid>
|
||||
<description><p>Some cool stuff is coming in Zavala 2.0. <a href="https://zavala.vincode.io/2021/10/08/2.0-Preview.html">zavala.vincode.io/2021/10/0&hellip;</a></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2021/09/28/my-outliner-zavala.html</link>
|
||||
<pubDate>Tue, 28 Sep 2021 15:22:15 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/09/28/my-outliner-zavala.html</guid>
|
||||
<description><p>My outliner, Zavala, has a new release in beta testing. There are lots of new features and I sure could use some help testing it. <a href="https://zavala.vincode.io/2021/09/28/1.2-Beta-1.html">zavala.vincode.io/2021/09/2&hellip;</a></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title></title>
|
||||
<link>https://vincode.io/2021/06/24/heres-a-great.html</link>
|
||||
<pubDate>Thu, 24 Jun 2021 19:55:28 -0500</pubDate>
|
||||
|
||||
<guid>http://vincode.micro.blog/2021/06/24/heres-a-great.html</guid>
|
||||
<description><p>Here&rsquo;s a great article about <a href="https://micro.blog/NetNewsWire">@NetNewsWire</a> with some excellent quotes from <a href="https://micro.blog/brentsimmons">@brentsimmons</a>. It really gets into how Brent and the team think about NetNewsWire&rsquo;s place in the world. <a href="https://www.lifewire.com/how-netnewswire-6-could-change-the-way-you-read-news-5190136">www.lifewire.com/how-netne&hellip;</a></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
</rss>
|
Loading…
x
Reference in New Issue
Block a user