UNPKG

db-lgtv-focus-engine

Version:

the Best TV focus engine

249 lines (243 loc) 7.4 kB
import Leaf from './Leaf' import Trunk from './Trunk' import LongScroll from './Trunk/LongScroll' import Input from './Leaf/Input' import Loading from "../tool/loading"; import Dialog from "../tool/dialog"; import { bindResizeListener, unbindResizeListener } from '../event/resize' import { bindKeydownListener, unbindKeydownListener } from '../event/keydown' import { bindKeyupListener, unbindKeyupListener } from '../event/keyup' import { bindWheelListener, unbindWheelListener } from '../event/wheel' import { bindHashListener, unbindHashListener } from '../event/hashchange' import { bindMutationObserver, unbindMutationObserver } from '../event/mutation' import config from '../config.json' class FEngine { constructor(opt = {}) { let option = { ...config, ...opt } this.initData(option) this.use(Loading) this.use(Dialog) this.activated() window.$engine = this } use(plugin) { plugin.install(this) } initData(opt) { this.options = opt this.history = [] this.leaf_pool = [] this.trunk_pool = [] this.cached_map = {} this.focused_map = {} this.freezed_map = {} this.stash_map = {} this.last_key_down = null this.last_key_up = null this.history_focused_id = null this.$tick_callback_stack = [] this.$scroll_callback_stack = [] } findLeafById(id) { let leaf = this.leaf_pool.find(_i => _i.id === id) return leaf } findLeafByDom(el) { let id = el.getAttribute('db-id') || window.getIdByDom(el) return this.findLeafById(id) } // 根据id获取trunk findTrunkById(id) { let trunk = this.trunk_pool.find(_i => _i.id === id) return trunk } findTrunkByDom(el) { let id = window.getIdByDom(el) return this.findTrunkById(id) } findFocusedLeaf() { return this.focused_map[window.location.href] ? this.findLeafById(this.focused_map[window.location.href]) : null } activated() { bindResizeListener(this) bindKeydownListener(this) bindKeyupListener(this) bindWheelListener(this) bindMutationObserver(this) bindHashListener(this) this.render() } deactivated() { unbindResizeListener(this) unbindKeydownListener(this) unbindKeyupListener(this) unbindWheelListener(this) unbindMutationObserver(this) unbindHashListener(this) } destroyed() { this.deactivated() this.forget() } forget({ cache, focus, freeze } = { cache: true, focus: true, freeze: true }) { cache && delete(this.cached_map[window.location.href]) focus && delete(this.focused_map[window.location.href]) freeze && delete(this.freezed_map[window.location.href]) this.render() } clear() { let focus_pool = document.querySelectorAll('[db-focus]') Array.from(focus_pool).forEach(_i => { _i.removeAttribute('db-focus') }) let child_focus_pool = document.querySelectorAll('[db-child-focus]') Array.from(child_focus_pool).forEach(_i => { _i.removeAttribute('db-child-focus') }) let freeze_pool = document.querySelectorAll('[db-freeze]') Array.from(freeze_pool).forEach(_i => { _i.removeAttribute('db-freeze') }) let cache_pool = document.querySelectorAll('[db-cache]') Array.from(cache_pool).forEach(_i => { _i.removeAttribute('db-cache') }) } render() { this.clear() // 初始化叶子元素池 let leaf_pool = document.querySelectorAll('[db-leaf]') this.leaf_pool = Array.from(leaf_pool).map(_i => { if (_i.tagName === 'INPUT') return new Input(this, _i) return new Leaf(this, _i) }).filter(_i => _i); // 初始化枝干元素池 let trunk_pool = [ ...Array.from(document.querySelectorAll('[db-trunk]')), ...Array.from(document.querySelectorAll('[db-scroll]')), ...Array.from(document.querySelectorAll('[db-long-scroll]')) ] this.trunk_pool = trunk_pool.map(_i => { if (_i.hasAttribute('db-scroll')) return new Trunk(this, _i) if (_i.hasAttribute('db-long-scroll')) return new LongScroll(this, _i) return new Trunk(this, _i) }); // 恢复页面状态 this.remember() this.$tickCallback() } remember() { // 恢复缓存状态 let cached_map = this.cached_map[window.location.href] for (let id in cached_map) { let leaf = this.findLeafById(id) leaf && leaf.cache() } // 恢复冻结状态 let freezed_map = this.freezed_map[window.location.href] for (let id in freezed_map) { let leaf = this.findLeafById(id) leaf && leaf.freeze() } // 恢复焦点状态 let focused_id = this.focused_map[window.location.href] let leaf = this.findLeafById(focused_id) if (!leaf || leaf.freezed) leaf = this.leaf_pool.filter(_i => !_i.freezed && _i.isLeafVisibleFor() && _i.el.hasAttribute('db-default'))[0] || this.leaf_pool.filter(_i => !_i.freezed && _i.isLeafVisibleFor())[0] leaf && leaf.focus() } stash(types, key) { let stash = {} types.forEach((_i) => { stash[_i] = JSON.parse(JSON.stringify(this[`${_i}_map`][window.location.href] || {})) }) this.stash_map[key] = stash } pop(key) { if (!this.stash_map[key]) return for (let _i in this.stash_map[key]) { this[`${_i}_map`][window.location.href] = this.stash_map[key][_i] } this.render() this.stash_map[key] = null } freeze() { this.leaf_pool.forEach(leaf => leaf.freeze({ render: false })) this.render() } unfreeze() { this.leaf_pool.forEach(leaf => leaf.unfreeze({ render: false })) this.render() } $nextTick(callback) { this.$tick_callback_stack.push(callback) } $tickCallback() { let stack = this.$tick_callback_stack this.$tick_callback_stack = [] stack.forEach(_i => { _i() }) } $nextScroll(callback) { this.$scroll_callback_stack.push(callback) } $scrollCallback() { let stack = this.$scroll_callback_stack this.$scroll_callback_stack = [] stack.forEach(_i => { _i() }) } reverse() { let target_step = null let _distance = 0 while(_distance < 2) { let step = this.history.pop() if (step.cmd === 'focus') { target_step = step _distance ++ } } if (target_step.leaf_id) { let leaf = this.findLeafById(target_step.leaf_id) leaf && leaf.focus() } } } FEngine.prototype.Leaf = Leaf FEngine.prototype.Trunk = Trunk FEngine.prototype.LongScroll = LongScroll export default FEngine