import { meetsMinContent } from './adapterContract.mjs'
import extractHtmlTitle from './extractHtmlTitle.mjs'
//由Nuxt state字串中切出指定欄位之物件片段, 以大括號配對決定其邊界
//
//先前以單一regex「field:\{[\s\S]*?(?:,|\{)content:"..."」直接找content, 其惰性比對
//會跨越物件邊界: 當content是該物件的首個鍵時, 開頭的左大括號已被 :\{ 消耗,
//比對遂延伸到後面另一個欄位而抓到別人的內文, 且靜默成功不報錯。
//
//不能改以「中間不得含大括號」收緊: 真實state內含巢狀物件(實測articleDetail含
//tag:{name:"原创",color:"#999999"}), 那樣會直接找不到content。
//故改為先切出物件範圍再於範圍內尋找——邊界由配對決定, 與鍵的順序無關
function _sliceObject(html, field) {
let key = field + ':{'
let i = html.indexOf(key)
if (i < 0) {
return null
}
//自左大括號起掃描, 字串內之大括號與跳脫字元不計入配對
let start = i + key.length - 1
let depth = 0
let inStr = false
let esc = false
for (let k = start; k < html.length; k++) {
let c = html[k]
if (esc) {
esc = false
continue
}
if (c === '\\') {
esc = true
continue
}
if (inStr) {
if (c === '"') {
inStr = false
}
continue
}
if (c === '"') {
inStr = true
continue
}
if (c === '{') {
depth += 1
}
else if (c === '}') {
depth -= 1
if (depth === 0) {
return html.slice(start, k + 1)
}
}
}
//大括號未配對完成, 視為找不到
return null
}
/**
* 解析格隆匯之文章與快訊內文
*
* 由Nuxt SSR state中之dtbDetail(快訊)或articleDetail(文章)欄位取出content,
* 反轉義後剝除HTML標籤與實體並壓縮空白;作為內建站台adapter之parse掛點使用
*
* @param {String} html 輸入網頁HTML字串
* @param {String} url 輸入網址字串,本解析器未使用
* @returns {Object} 回傳解析結果物件,成功時為{success:true,title,content,contentLength},失敗時為{success:false,reason,message}
* @example
*
* import { defaultAdapters } from './src/fetchWeb.mjs'
*
* let r = defaultAdapters[0].parse(html, 'https://www.gelonghui.com/p/123456')
* console.log(r.success, r.title)
* // => true '流動性溢價的真相'
*
*/
//格隆匯(快訊與文章), 由Nuxt SSR state提取結構化資料
function parseGelonghui(html, url) {
let fields = ['dtbDetail', 'articleDetail']
for (let field of fields) {
//先以大括號配對切出該欄位之物件, 再於其內找content, 使比對不會跨到別的欄位
let seg = _sliceObject(html, field)
if (seg === null) {
continue
}
let match = seg.match(/(?:^\{|,)content:"((?:[^"\\]|\\.)*)"/)
if (!match) {
continue
}
let raw = match[1]
.replace(/\\u([0-9a-fA-F]{4})/g, (s, hex) => String.fromCharCode(parseInt(hex, 16)))
.replace(/\\n/g, '\n')
.replace(/\\"/g, '"')
.replace(/\\\\/g, '\\')
let content = raw.replace(/<[^>]+>/g, '').replace(/&[a-z]+;/gi, ' ').replace(/\s+/g, ' ').trim()
let title = extractHtmlTitle(html)
if (!meetsMinContent(content)) {
return { success: false, reason: 'empty-content', message: 'content too short (' + content.length + ' chars)' }
}
return { success: true, title, content, contentLength: content.length }
}
return { success: false, reason: 'adapter-parse-miss', message: 'gelonghui: no content found in __NUXT__ state' }
}
export default parseGelonghui