import DomHandler , { DomHandlerOptions , Element } from "domhandler" ; import * as DomUtils from "domutils" ; import { Parser , ParserOptions } from "./Parser" ; interface FeedItem { id ? : string ; title ? : string ; link ? : string ; description ? : string ; pubDate ? : Date ; } interface Feed { type ? : string ; id ? : string ; title ? : string ; link ? : string ; description ? : string ; updated ? : Date ; author ? : string ; items ? : FeedItem [ ] ; } export class FeedHandler extends DomHandler { feed ? : Feed ; param callback param options constructor ( callback ? , options ? ) { if ( typeof callback === "object" && callback !== null ) { callback = undefined ; options = callback ; } super ( callback , options ) ; } onend ( ) { const feed = { } ; const feedRoot = getOneElement ( isValidFeed , this . dom ) ; if ( feedRoot ) { if ( feedRoot . name === "feed" ) { const childs = feedRoot . children ; feed . type = "atom" ; addConditionally ( feed , "id" , "id" , childs ) ; addConditionally ( feed , "title" , "title" , childs ) ; const href = getAttribute ( "href" , getOneElement ( "link" , childs ) ) ; if ( href ) { feed . link = href ; } addConditionally ( feed , "description" , "subtitle" , childs ) ; const updated = fetch ( "updated" , childs ) ; if ( updated ) { feed . updated = new Date ( updated ) ; } addConditionally ( feed , "author" , "email" , childs , true ) ; feed . items = getElements ( "entry" , childs ) . map ( item => { const entry = { } ; const { children } = item ; addConditionally ( entry , "id" , "id" , children ) ; addConditionally ( entry , "title" , "title" , children ) ; const href = getAttribute ( "href" , getOneElement ( "link" , children ) ) ; if ( href ) { entry . link = href ; } const description = fetch ( "summary" , children ) || fetch ( "content" , children ) ; if ( description ) { entry . description = description ; } const pubDate = fetch ( "updated" , children ) ; if ( pubDate ) { entry . pubDate = new Date ( pubDate ) ; } return entry ; } ) ; } else { const childs = getOneElement ( "channel" , feedRoot . children ) . children ; feed . type = feedRoot . name . substr ( 0 , 3 ) ; feed . id = "" ; addConditionally ( feed , "title" , "title" , childs ) ; addConditionally ( feed , "link" , "link" , childs ) ; addConditionally ( feed , "description" , "description" , childs ) ; const updated = fetch ( "lastBuildDate" , childs ) ; if ( updated ) { feed . updated = new Date ( updated ) ; } addConditionally ( feed , "author" , "managingEditor" , childs , true ) ; feed . items = getElements ( "item" , feedRoot . children ) . map ( ( item ) => { const entry = { } ; const { children } = item ; addConditionally ( entry , "id" , "guid" , children ) ; addConditionally ( entry , "title" , "title" , children ) ; addConditionally ( entry , "link" , "link" , children ) ; addConditionally ( entry , "description" , "description" , children ) ; const pubDate = fetch ( "pubDate" , children ) ; if ( pubDate ) entry . pubDate = new Date ( pubDate ) ; return entry ; } ) ; } } this . feed = feed ; this . handleCallback ( feedRoot ? null : Error ( "couldn't find root of feed" ) ) ; } } function getElements ( what , where ) { return DomUtils . getElementsByTagName ( what , where , true ) ; } function getOneElement ( what , where ) { return DomUtils . getElementsByTagName ( what , where , true , 1 ) [ 0 ] ; } function fetch ( what , where , recurse = false ) { return DomUtils . getText ( DomUtils . getElementsByTagName ( what , where , recurse , 1 ) ) . trim ( ) ; } function getAttribute ( name , elem ) { if ( ! elem ) { return null ; } const { attribs } = elem ; return attribs [ name ] ; } function addConditionally < T > ( obj , prop , what , where , recurse = false ) { const tmp = fetch ( what , where , recurse ) ; if ( tmp ) obj [ prop ] = tmp ; } function isValidFeed ( value ) { return value === "rss" || value === "feed" || value === "rdf:RDF" ; } const defaultOptions = { xmlMode : true } ; param feed param options export function parseFeed ( feed , options = defaultOptions ) { const handler = new FeedHandler ( options ) ; new Parser ( handler , options ) . end ( feed ) ; return handler . feed ; }