Focus-friendly VMC shell for Tizen with live TV and playback against the master API. Co-authored-by: Cursor <cursoragent@cursor.com>
100 lines
2.8 KiB
JavaScript
100 lines
2.8 KiB
JavaScript
(function (global) {
|
|
var keys = {
|
|
LEFT: 37,
|
|
UP: 38,
|
|
RIGHT: 39,
|
|
DOWN: 40,
|
|
ENTER: 13,
|
|
BACK: 10009,
|
|
ESC: 27,
|
|
PLAY: 415,
|
|
PAUSE: 19,
|
|
PLAYPAUSE: 10252,
|
|
FF: 417,
|
|
REW: 412,
|
|
STOP: 413,
|
|
};
|
|
|
|
function visibleFocusables(root) {
|
|
root = root || document;
|
|
var nodes = root.querySelectorAll(".focusable, .tile, .profile-tile, .ep, .num-key, .btn");
|
|
var out = [];
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
var el = nodes[i];
|
|
if (el.disabled) continue;
|
|
if (el.classList.contains("hidden")) continue;
|
|
if (el.closest && el.closest(".hidden")) continue;
|
|
var r = el.getBoundingClientRect();
|
|
if (r.width < 2 || r.height < 2) continue;
|
|
out.push(el);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function center(el) {
|
|
var r = el.getBoundingClientRect();
|
|
return { x: r.left + r.width / 2, y: r.top + r.height / 2, r: r };
|
|
}
|
|
|
|
function move(direction) {
|
|
var active = document.activeElement;
|
|
var list = visibleFocusables();
|
|
if (!list.length) return;
|
|
if (!active || list.indexOf(active) < 0) {
|
|
list[0].focus();
|
|
return;
|
|
}
|
|
var a = center(active);
|
|
var best = null;
|
|
var bestScore = Infinity;
|
|
|
|
for (var i = 0; i < list.length; i++) {
|
|
var el = list[i];
|
|
if (el === active) continue;
|
|
var b = center(el);
|
|
var dx = b.x - a.x;
|
|
var dy = b.y - a.y;
|
|
var ok = false;
|
|
if (direction === "left") ok = dx < -10 && Math.abs(dy) < Math.abs(dx) * 1.6;
|
|
if (direction === "right") ok = dx > 10 && Math.abs(dy) < Math.abs(dx) * 1.6;
|
|
if (direction === "up") ok = dy < -10 && Math.abs(dx) < Math.abs(dy) * 1.8;
|
|
if (direction === "down") ok = dy > 10 && Math.abs(dx) < Math.abs(dy) * 1.8;
|
|
if (!ok) continue;
|
|
var score = Math.abs(dx) + Math.abs(dy) * 1.2;
|
|
if (score < bestScore) {
|
|
bestScore = score;
|
|
best = el;
|
|
}
|
|
}
|
|
if (best) {
|
|
best.focus();
|
|
if (best.scrollIntoView) best.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
}
|
|
}
|
|
|
|
function registerKeys() {
|
|
try {
|
|
if (global.tizen && tizen.tvinputdevice) {
|
|
var supported = tizen.tvinputdevice.getSupportedKeys() || [];
|
|
var names = ["MediaPlay", "MediaPause", "MediaPlayPause", "MediaFastForward", "MediaRewind", "MediaStop"];
|
|
for (var i = 0; i < supported.length; i++) {
|
|
if (names.indexOf(supported[i].name) >= 0) {
|
|
try {
|
|
tizen.tvinputdevice.registerKey(supported[i].name);
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
global.McFocus = {
|
|
keys: keys,
|
|
move: move,
|
|
focusFirst: function (root) {
|
|
var list = visibleFocusables(root);
|
|
if (list[0]) list[0].focus();
|
|
},
|
|
registerKeys: registerKeys,
|
|
};
|
|
})(window);
|