parseDetectValue.mjs

import cloneDeep from 'lodash-es/cloneDeep.js'
import isobj from 'wsemi/src/isobj.mjs'
import isfun from 'wsemi/src/isfun.mjs'
import isestr from 'wsemi/src/isestr.mjs'


/**
 * 解析偵測類指令(v-domresize、v-dommutation、v-domstable、v-domvisible)之指令值
 *
 * 未給或true為啟用並使用預設設定, false或null為停用, 純物件為啟用並以其鍵作為設定, 其他型別(字串、數字、陣列、函數)視同未給並給警告
 * 物件只採用該指令之可用鍵, 其他鍵不採用並給警告: 模板物件字面值每次重繪皆為新物件, 未知鍵之值若為物件或函數而列入比較, 每次重繪皆判為改變而重建
 * snap為判斷是否須重建偵測器之快照: 非函數設定之值(深拷貝, 故原地修改之陣列等亦可比出差異)與函數設定之鍵名(函數本身於呼叫時取最新指令值, 其更換不需重建)
 * 重新比較鍵(spec.refreshKey)之值不列入快照、不作為設定, 另以rkey回傳, 其改變只請偵測器重新比較
 *
 * @param {*} value 輸入指令值
 * @param {Object} [spec={}] 輸入指令規格物件
 * @param {Array} [spec.keys=[]] 輸入可用設定鍵之字串陣列
 * @param {String} [spec.refreshKey=''] 輸入重新比較鍵之名稱字串, 須同時列於keys, 預設''表示無
 * @param {Function} [spec.normalize=null] 輸入設定正規化函數, 傳入只含可用鍵之設定物件與{given}(指令值是否為物件), 回傳{cfg,warns}, 例如v-domresize補event預設值並檢查其值, 預設null
 * @returns {Object} 回傳物件, enabled為是否啟用, cfg為設定物件, funs為函數設定之鍵名陣列, snap為快照(停用時為null), rkey為重新比較鍵之值(深拷貝, 未給為undefined), warns為警告訊息字串陣列
 */
function parseDetectValue(value, spec = {}) {
    let keys = spec.keys || []
    let refreshKey = isestr(spec.refreshKey) ? spec.refreshKey : ''
    let normalize = spec.normalize || null

    //enabled, cfg, rkey, warns
    let enabled = true
    let cfg = {}
    let rkey
    let warns = []
    if (value === false || value === null) {
        enabled = false
    }
    else if (value === undefined || value === true) {
        cfg = {}
    }
    else if (isobj(value)) {
        let unknown = []
        for (let k of Object.keys(value)) {
            if (keys.indexOf(k) < 0) {
                unknown.push(k)
            }
            else if (k === refreshKey) {
                rkey = cloneDeep(value[k])
            }
            else {
                cfg[k] = value[k]
            }
        }
        if (unknown.length > 0) {
            warns.push(`設定鍵${unknown.join('、')}不適用而不採用, 可用之鍵為${keys.length > 0 ? keys.join('、') : '無'}`)
        }
    }
    else {
        let t = Array.isArray(value) ? 'array' : typeof value
        warns.push(`指令值須為true、false、null或物件, 目前為${t}, 視同未給(啟用並使用預設設定)`)
    }

    //停用
    if (!enabled) {
        return {
            enabled,
            cfg,
            funs: [],
            snap: null,
            rkey,
            warns,
        }
    }

    //normalize, given為指令值是否為物件(未給或true時cfg亦為空物件, 由此區分)
    if (isfun(normalize)) {
        let r = normalize(cfg, { given: isobj(value) })
        cfg = r.cfg
        warns = [...warns, ...(r.warns || [])]
    }

    //funs, vals
    let funs = []
    let vals = {}
    for (let k of Object.keys(cfg).sort()) {
        if (isfun(cfg[k])) {
            funs.push(k)
        }
        else {
            vals[k] = cfg[k]
        }
    }

    //snap
    let snap = {
        vals: cloneDeep(vals),
        funs,
    }

    return {
        enabled,
        cfg,
        funs,
        snap,
        rkey,
        warns,
    }
}


export default parseDetectValue