react-krpano-toolkit
Version:
A React toolkit for KRPano integration with reusable functions and screen/hotspot configuration management
1,301 lines (1,270 loc) • 53 kB
JavaScript
'use strict';
var react = require('react');
var jsxRuntime = require('react/jsx-runtime');
/* eslint-disable @typescript-eslint/no-explicit-any */
var KrpanoAPI = /** @class */function () {
function KrpanoAPI() {
this.instance = null;
}
KrpanoAPI.prototype.setInstance = function (krpano) {
this.instance = krpano;
};
KrpanoAPI.prototype.isReady = function () {
return !!this.instance;
};
KrpanoAPI.prototype.call = function (action) {
var _a, _b;
if (!this.instance) {
console.error("instance Krpano chưa sẵn sàng");
return {};
}
return (_b = (_a = this.instance) === null || _a === void 0 ? void 0 : _a.call) === null || _b === void 0 ? void 0 : _b.call(_a, action);
};
KrpanoAPI.prototype.get = function (path) {
var _a, _b;
if (!this.instance) throw new Error("Krpano chưa sẵn sàng");
return (_b = (_a = this.instance) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(_a, path);
};
KrpanoAPI.prototype.set = function (path, value) {
var _a, _b;
if (!this.instance) throw new Error("Krpano chưa sẵn sàng");
(_b = (_a = this.instance) === null || _a === void 0 ? void 0 : _a.set) === null || _b === void 0 ? void 0 : _b.call(_a, path, value);
};
return KrpanoAPI;
}();
// singleton instance
var _krpanoAPI = null;
// export tên giống trước, nhưng là getter
var krpanoAPI = new Proxy({}, {
get: function get(_, prop) {
if (!_krpanoAPI) _krpanoAPI = new KrpanoAPI();
// @ts-ignore
return _krpanoAPI[prop];
},
set: function set(_, prop, value) {
if (!_krpanoAPI) _krpanoAPI = new KrpanoAPI();
// @ts-ignore
_krpanoAPI[prop] = value;
return true;
}
});
var EventBus = /** @class */function () {
function EventBus() {
this.events = {};
}
EventBus.prototype.on = function (event, listener) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(listener);
};
EventBus.prototype.off = function (event, listener) {
this.events[event] = (this.events[event] || []).filter(function (l) {
return l !== listener;
});
};
EventBus.prototype.emit = function (event) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
(this.events[event] || []).forEach(function (listener) {
return listener.apply(void 0, args);
});
};
EventBus.prototype.clear = function () {
this.events = {};
};
return EventBus;
}();
var KrpanoContext = /*#__PURE__*/react.createContext({
api: new KrpanoAPI(),
events: new EventBus()
});
var KrpanoProvider = function KrpanoProvider(_a) {
var children = _a.children;
var apiRef = react.useRef(new KrpanoAPI());
var eventBusRef = react.useRef(new EventBus());
react.useEffect(function () {
window.onKrpanoReady = function (instance) {
apiRef.current.setInstance(instance);
eventBusRef.current.emit("ready", instance);
};
}, []);
return jsxRuntime.jsx(KrpanoContext.Provider, {
value: {
api: apiRef.current,
events: eventBusRef.current
},
children: children
});
};
var useExecute = function useExecute() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
var execute = function execute(opts) {
var type = opts.type,
name = opts.name,
options = opts.options,
raw = opts.raw;
if (raw) {
api.call(raw);
return;
}
if (!type) {
console.warn("type là bắt buộc trừ khi dùng raw");
return;
}
if (!name && !["action", "scene", "view", "events", "display", "control"].includes(type)) {
console.warn("name là bắt buộc cho loại này");
return;
}
var basePath = name ? "".concat(type, "[").concat(name, "]") : type;
if (!options) {
console.warn("options chưa được cung cấp");
return;
}
Object.entries(options).forEach(function (_a) {
var option = _a[0],
value = _a[1];
api.set("".concat(basePath, ".").concat(option), value);
});
};
return execute;
};
var useScene = function useScene() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
// Scene operations
var scene = {
loadScene: function loadScene(sceneName, options) {
if (options === void 0) {
options = {};
}
var _a = options.blend,
blend = _a === void 0 ? "BLEND(0.5)" : _a,
_b = options.flags,
flags = _b === void 0 ? "" : _b;
api.call("loadscene(".concat(sceneName, ", null, ").concat(blend, ", ").concat(flags, ")"));
},
removeScene: function removeScene(sceneName) {
api.call("removescene(".concat(sceneName, ")"));
},
addScene: function addScene(sceneName, xmlPath) {
if (xmlPath) {
api.call("addscene(".concat(sceneName, "); loadxml(").concat(xmlPath, ")"));
} else {
api.call("addscene(".concat(sceneName, ")"));
}
},
copyScene: function copyScene(fromScene, toScene) {
api.call("copyscene(".concat(fromScene, ", ").concat(toScene, ")"));
}
};
return scene;
};
var useView = function useView() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
// View operations
var view = {
lookAt: function lookAt(hlookat, vlookat, fov, options) {
if (options === void 0) {
options = {};
}
var _a = options.smooth,
smooth = _a === void 0 ? true : _a,
_b = options.time,
time = _b === void 0 ? 1.0 : _b,
_c = options.tween,
tween = _c === void 0 ? "easeInOutSine" : _c,
blend = options.blend;
var blendParam = blend !== undefined ? ", ".concat(blend) : '';
if (smooth) {
api.call("tween(view.hlookat|view.vlookat".concat(fov ? '|view.fov' : '', ", ").concat(hlookat, "|").concat(vlookat).concat(fov ? "|".concat(fov) : '', ", ").concat(time, ", ").concat(tween).concat(blendParam, ")"));
} else {
api.set('view.hlookat', hlookat);
api.set('view.vlookat', vlookat);
if (fov !== undefined) api.set('view.fov', fov);
}
},
lookTo: function lookTo(ath, atv, options) {
if (options === void 0) {
options = {};
}
var _a = options.smooth,
smooth = _a === void 0 ? true : _a,
_b = options.time,
time = _b === void 0 ? 1.0 : _b;
if (smooth) {
api.call("lookto(".concat(ath, ", ").concat(atv, ", ").concat(time, ")"));
} else {
api.call("looktohotspot('', ".concat(ath, ", ").concat(atv, ")"));
}
},
zoomTo: function zoomTo(fov, options) {
if (options === void 0) {
options = {};
}
var _a = options.smooth,
smooth = _a === void 0 ? true : _a,
_b = options.time,
time = _b === void 0 ? 1.0 : _b;
if (smooth) {
api.call("tween(view.fov, ".concat(fov, ", ").concat(time, ")"));
} else {
api.set('view.fov', fov);
}
},
moveCamera: function moveCamera(direction, amount) {
if (amount === void 0) {
amount = 5;
}
var directions = {
up: "add(view.vlookat, ".concat(amount, ")"),
down: "add(view.vlookat, -".concat(amount, ")"),
left: "add(view.hlookat, -".concat(amount, ")"),
right: "add(view.hlookat, ".concat(amount, ")")
};
api.call(directions[direction]);
},
stopMovement: function stopMovement() {
api.call('stopdelayedcall(); stoptween();');
}
};
return view;
};
var useElement = function useElement() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) {
throw new Error("KrpanoContext chưa được cung cấp");
}
var api = ctx.api;
// Element operations
var element = {
show: function show(type, name, options) {
if (options === void 0) {
options = {};
}
var time = options.time,
_a = options.tween,
tween = _a === void 0 ? "easeInOutSine" : _a;
if (time) {
api.call("tween(".concat(type, "[").concat(name, "].alpha, 1.0, ").concat(time, ", ").concat(tween, "); set(").concat(type, "[").concat(name, "].visible, true);"));
} else {
api.set("".concat(type, "[").concat(name, "].visible"), true);
api.set("".concat(type, "[").concat(name, "].alpha"), 1.0);
}
},
hide: function hide(type, name, options) {
if (options === void 0) {
options = {};
}
var time = options.time,
_a = options.tween,
tween = _a === void 0 ? "easeInOutSine" : _a;
if (time) {
api.call("tween(".concat(type, "[").concat(name, "].alpha, 0.0, ").concat(time, ", ").concat(tween, ", set(").concat(type, "[").concat(name, "].visible, false));"));
} else {
api.set("".concat(type, "[").concat(name, "].visible"), false);
}
},
toggle: function toggle(type, name) {
var visible = api.get("".concat(type, "[").concat(name, "].visible"));
api.call("set(".concat(type, "[").concat(name, "].visible, ").concat(visible ? 'false' : 'true', ");"));
},
animate: function animate(type, name, properties, options) {
if (options === void 0) {
options = {};
}
var _a = options.time,
time = _a === void 0 ? 1.0 : _a,
_b = options.tween,
tween = _b === void 0 ? "easeInOutSine" : _b,
onComplete = options.onComplete;
var propStrings = Object.entries(properties).map(function (_a) {
var key = _a[0];
_a[1];
return "".concat(type, "[").concat(name, "].").concat(key);
}).join('|');
var valueStrings = Object.values(properties).join('|');
var onCompleteParam = onComplete ? ", ".concat(onComplete) : '';
api.call("tween(".concat(propStrings, ", ").concat(valueStrings, ", ").concat(time, ", ").concat(tween).concat(onCompleteParam, ")"));
},
addElement: function addElement(type, name, properties) {
if (properties === void 0) {
properties = {};
}
var elementType = properties.type || type;
var parent = properties.parent || "container";
api.call("add".concat(type, "(").concat(name, ")"));
api.call("set(".concat(type, "[").concat(name, "].parent, ").concat(parent, ")"));
api.call("set(".concat(type, "[").concat(name, "].type, ").concat(elementType, ")"));
Object.entries(properties).forEach(function (_a) {
var key = _a[0],
value = _a[1];
if (["type", "parent", "onloaded"].includes(key)) return;
var v = typeof value === "string" ? "\"".concat(value, "\"") : value;
api.call("set(".concat(type, "[").concat(name, "].").concat(key, ", ").concat(v, ")"));
});
},
updateElement: function updateElement(type, name, properties) {
if (properties === void 0) {
properties = {};
}
if (!api.get("".concat(type, "[").concat(name, "]"))) {
console.warn("".concat(type, "[").concat(name, "] kh\xF4ng t\u1ED3n t\u1EA1i, h\xE3y d\xF9ng addElement tr\u01B0\u1EDBc."));
return;
}
Object.entries(properties).forEach(function (_a) {
var key = _a[0],
value = _a[1];
if (["type", "parent", "onloaded"].includes(key)) return;
var v = typeof value === "string" ? "\"".concat(value, "\"") : value;
api.call("set(".concat(type, "[").concat(name, "].").concat(key, ", ").concat(v, ")"));
});
},
removeElement: function removeElement(type, name) {
api.call("remove".concat(type, "(").concat(name, ")"));
},
cloneElement: function cloneElement(type, fromName, toName) {
var _a;
if (!api.get("".concat(type, "[").concat(fromName, "]"))) {
console.warn("".concat(type, "[").concat(fromName, "] kh\xF4ng t\u1ED3n t\u1EA1i, kh\xF4ng th\u1EC3 clone."));
return false;
}
if (api.get("".concat(type, "[").concat(toName, "]"))) {
console.warn("".concat(type, "[").concat(toName, "] \u0111\xE3 t\u1ED3n t\u1EA1i, kh\xF4ng th\u1EC3 clone."));
return false;
}
api.call("add".concat(type, "(").concat(toName, ")"));
// Lấy tất cả thuộc tính của element gốc
var props = (_a = api.get("".concat(type, "[").concat(fromName, "]"))) === null || _a === void 0 ? void 0 : _a.DATA;
if (!props) {
console.warn("Kh\xF4ng th\u1EC3 l\u1EA5y thu\u1ED9c t\xEDnh c\u1EE7a ".concat(type, "[").concat(fromName, "]"));
return false;
}
// Gán tất cả thuộc tính cho element mới
Object.entries(props).forEach(function (_a) {
var key = _a[0],
value = _a[1];
if (key === "name") return; // bỏ qua thuộc tính name
var v = typeof value === "string" ? "\"".concat(value, "\"") : value;
api.call("set(".concat(type, "[").concat(toName, "].").concat(key, ", ").concat(v, ")"));
});
return true;
}
};
return element;
};
var useSound = function useSound() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
// Sound operations
var sound = {
playSound: function playSound(soundName, url, options) {
if (options === void 0) {
options = {};
}
var _a = options.volume,
volume = _a === void 0 ? 1.0 : _a,
_b = options.loops,
loops = _b === void 0 ? 1 : _b;
if (url) {
api.call("loadsound(".concat(soundName, ", ").concat(url, "); playsound(").concat(soundName, ", ").concat(volume, ", ").concat(loops, ")"));
} else {
api.call("playsound(".concat(soundName, ", ").concat(volume, ", ").concat(loops, ")"));
}
},
stopSound: function stopSound(soundName) {
api.call("stopsound(".concat(soundName, ")"));
},
pauseSound: function pauseSound(soundName) {
api.call("pausesound(".concat(soundName, ")"));
},
setSoundVolume: function setSoundVolume(soundName, volume) {
api.set("sound[".concat(soundName, "].volume"), volume);
},
stopAllSounds: function stopAllSounds() {
api.call('stopallsounds()');
}
};
return sound;
};
var useControl = function useControl() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) {
console.error("KrpanoContext chưa được cung cấp");
return null;
}
var api = ctx.api;
// Control operations
var control = {
enterFullscreen: function enterFullscreen() {
return api.set('fullscreen', true);
},
exitFullscreen: function exitFullscreen() {
return api.set('fullscreen', false);
},
toggleFullscreen: function toggleFullscreen() {
return api.set('fullscreen', !api.get('fullscreen'));
},
enableControl: function enableControl() {
return api.set('control.usercontrol', true);
},
disableControl: function disableControl() {
return api.set('control.usercontrol', false);
},
toggleAutoRotate: function toggleAutoRotate() {
if (api.get("autorotate.enabled") === true) {
api.set("autorotate.enabled", false);
} else {
api.set("autorotate.enabled", true);
}
},
getAutoRotateStatus: function getAutoRotateStatus() {
return api.get("autorotate.enabled") === true;
}
};
return control;
};
var useUtility = function useUtility() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
// Utility operations
var utility = {
loadXML: function loadXML(url, options) {
if (options === void 0) {
options = {};
}
var _a = options.keepdisplay,
keepdisplay = _a === void 0 ? false : _a;
api.call("loadxml(".concat(url, ", ").concat(keepdisplay, ")"));
},
reload: function reload() {
return api.call('reload()');
},
screenshot: function screenshot(options) {
if (options === void 0) {
options = {};
}
var _a = options.scale,
scale = _a === void 0 ? 1.0 : _a,
_b = options.alpha,
alpha = _b === void 0 ? false : _b;
api.call("screenshot(".concat(scale, ", ").concat(alpha, ")"));
},
trace: function trace(message) {
return api.call("trace(".concat(message, ")"));
},
error: function error(message) {
return api.call("error(".concat(message, ")"));
},
get: function get(variable) {
return api.get(variable);
},
set: function set(variable, value) {
return api.set(variable, value);
},
call: function call(action) {
return api.call(action);
},
callwith: function callwith(action) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
return api.call("callwith(".concat(action, ", ").concat(args.join(', '), ")"));
},
wait: function wait(time, callback) {
var callbackParam = callback ? ", ".concat(callback) : '';
api.call("delayedcall(".concat(time).concat(callbackParam, ")"));
}
};
return utility;
};
/**
* Hook to access the Krpano API context.
* @returns {call, get, set} - The Krpano API context.
* @throws {Error} - If useKrpano is called outside of KrpanoProvider.
*/
function useKrpano() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
// Import other hooks
var execute = useExecute();
var scene = useScene();
var view = useView();
var element = useElement();
var sound = useSound();
var control = useControl();
var utility = useUtility();
return {
execute: execute,
scene: scene,
view: view,
element: element,
sound: sound,
control: control,
utility: utility,
// Shortcuts for common operations
show: function show(name, type) {
if (type === void 0) {
type = 'hotspot';
}
return element.show(type, name);
},
hide: function hide(name, type) {
if (type === void 0) {
type = 'hotspot';
}
return element.hide(type, name);
},
toggle: function toggle(name, type) {
if (type === void 0) {
type = 'hotspot';
}
return element.toggle(type, name);
},
loadScene: scene.loadScene,
lookAt: view.lookAt,
zoomTo: view.zoomTo
};
}
function useKrpanoGlobalAction() {
var addAction = function addAction(name, callback) {
if (!krpanoAPI) return;
krpanoAPI.set("action[".concat(name, "]"), callback({}));
};
var callAction = function callAction(name, params) {
if (!krpanoAPI) return;
if (params) {
var args = Object.values(params).join(", ");
krpanoAPI.call("".concat(name, "(").concat(args, ")"));
} else {
krpanoAPI.call("".concat(name, "()"));
}
};
return {
addAction: addAction,
callAction: callAction
};
}
function useLoading() {
var _a = react.useState(0),
progress = _a[0],
setProgress = _a[1];
var _b = react.useContext(KrpanoContext),
api = _b.api,
events = _b.events;
react.useEffect(function () {
if (!api || !events) return;
var interval;
var startProgress = function startProgress() {
interval = setInterval(function () {
setProgress(function (prev) {
var next = prev + Math.random() * 5;
return Math.min(Math.floor(next), 95);
});
}, 100);
};
var finishProgress = function finishProgress() {
clearInterval(interval);
setProgress(100);
};
if (api.isReady()) {
startProgress();
finishProgress();
} else {
startProgress();
events.on("ready", finishProgress);
}
return function () {
clearInterval(interval);
events.off("ready", finishProgress);
};
}, [api, events]);
var isLoaded = progress >= 100;
return {
progress: progress,
isLoaded: isLoaded
};
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
function useKrpanoEventBridge() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
var handlersRef = react.useRef(new Map());
// Khởi tạo global event bridge
react.useEffect(function () {
if (typeof window !== 'undefined') {
// Tạo global bridge function để Krpano có thể gọi
window.krpanoEventBridge = function (eventType, eventData) {
var handlers = handlersRef.current.get(eventType);
if (handlers) {
var processedData_1 = __assign({
type: eventType,
timestamp: Date.now()
}, eventData);
handlers.forEach(function (handler) {
try {
handler(processedData_1);
} catch (error) {
console.error("Error in Krpano event handler for ".concat(eventType, ":"), error);
}
});
}
};
// Setup Krpano event listeners
setupKrpanoEventListeners();
}
return function () {
if (typeof window !== 'undefined') {
delete window.krpanoEventBridge;
}
};
}, [api]);
// Setup các event listeners trong Krpano
var setupKrpanoEventListeners = react.useCallback(function () {
if (!api) return;
// System Events
api.set('events.onloadcomplete', 'js(krpanoEventBridge("onloadcomplete", {}))');
api.set('events.onxmlcomplete', 'js(krpanoEventBridge("onxmlcomplete", {}))');
api.set('events.onready', 'js(krpanoEventBridge("onready", {}))');
api.set('events.onerror', 'js(krpanoEventBridge("onerror", {error: get(lasterror)}))');
api.set('events.onresize', 'js(krpanoEventBridge("onresize", {width: get(stagewidth), height: get(stageheight)}))');
api.set('events.onfullscreenchange', 'js(krpanoEventBridge("onfullscreenchange", {fullscreen: get(fullscreen)}))');
// Scene Events
api.set('events.onnewscene', 'js(krpanoEventBridge("onnewscene", {scenename: get(xml.scene), sceneindex: get(xml.sceneindex)}))');
api.set('events.onremovescene', 'js(krpanoEventBridge("onremovescene", {scenename: get(xml.scene)}))');
// View Events
api.set('events.onviewchange', 'js(krpanoEventBridge("onviewchange", {hlookat: get(view.hlookat), vlookat: get(view.vlookat), fov: get(view.fov)}))');
api.set('events.onviewchanged', 'js(krpanoEventBridge("onviewchanged", {hlookat: get(view.hlookat), vlookat: get(view.vlookat), fov: get(view.fov)}))');
api.set('events.onidle', 'js(krpanoEventBridge("onidle", {}))');
// Mouse Events
api.set('events.onclick', 'js(krpanoEventBridge("onclick", {stagex: get(mouse.stagex), stagey: get(mouse.stagey)}))');
api.set('events.ondblclick', 'js(krpanoEventBridge("ondblclick", {stagex: get(mouse.stagex), stagey: get(mouse.stagey)}))');
api.set('events.onmousedown', 'js(krpanoEventBridge("onmousedown", {stagex: get(mouse.stagex), stagey: get(mouse.stagey), button: get(mouse.button)}))');
api.set('events.onmouseup', 'js(krpanoEventBridge("onmouseup", {stagex: get(mouse.stagex), stagey: get(mouse.stagey), button: get(mouse.button)}))');
api.set('events.onmousemove', 'js(krpanoEventBridge("onmousemove", {stagex: get(mouse.stagex), stagey: get(mouse.stagey)}))');
// Keyboard Events
api.set('events.onkeydown', 'js(krpanoEventBridge("onkeydown", {keycode: get(key.keycode), key: get(key.key), ctrlkey: get(key.ctrlkey), shiftkey: get(key.shiftkey), altkey: get(key.altkey)}))');
api.set('events.onkeyup', 'js(krpanoEventBridge("onkeyup", {keycode: get(key.keycode), key: get(key.key), ctrlkey: get(key.ctrlkey), shiftkey: get(key.shiftkey), altkey: get(key.altkey)}))');
}, [api]);
// Thêm event listener
var addEventListener = react.useCallback(function (eventType, handler) {
var _a;
if (!handlersRef.current.has(eventType)) {
handlersRef.current.set(eventType, new Set());
}
(_a = handlersRef.current.get(eventType)) === null || _a === void 0 ? void 0 : _a.add(handler);
// Return cleanup function
return function () {
var _a;
(_a = handlersRef.current.get(eventType)) === null || _a === void 0 ? void 0 : _a["delete"](handler);
};
}, []);
// Xóa event listener
var removeEventListener = react.useCallback(function (eventType, handler) {
var _a;
(_a = handlersRef.current.get(eventType)) === null || _a === void 0 ? void 0 : _a["delete"](handler);
}, []);
// Xóa tất cả listeners của một event
var removeAllEventListeners = react.useCallback(function (eventType) {
if (eventType) {
handlersRef.current["delete"](eventType);
} else {
handlersRef.current.clear();
}
}, []);
// Trigger custom event
var triggerCustomEvent = react.useCallback(function (eventType, data) {
if (typeof window !== 'undefined' && window.krpanoEventBridge) {
window.krpanoEventBridge(eventType, data);
}
}, []);
return {
addEventListener: addEventListener,
removeAllEventListeners: removeAllEventListeners,
triggerCustomEvent: triggerCustomEvent,
removeEventListener: removeEventListener
};
}
/* eslint-disable @typescript-eslint/no-explicit-any */
function useKrpanoCommand() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
return ctx;
}
function useLayerEvents(layerName, handlers) {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
var addEventListener = useKrpanoEventBridge().addEventListener;
react.useEffect(function () {
if (!api || !layerName) return;
var cleanupFns = [];
// onclick
if (handlers.onclick) {
api.set("layer[".concat(layerName, "].onclick"), "js(krpanoEventBridge(\"onlayerclick\", {layername:\"".concat(layerName, "\", stagex:get(mouse.stagex), stagey:get(mouse.stagey)}))"));
var cleanup = addEventListener('onlayerclick', function (data) {
if ('layername' in data && data.layername === layerName) {
handlers.onclick(data);
}
});
cleanupFns.push(cleanup);
}
// onover
if (handlers.onover) {
api.set("layer[".concat(layerName, "].onover"), "js(krpanoEventBridge(\"onlayerover\", {layername:\"".concat(layerName, "\"}))"));
var cleanup = addEventListener('onlayerover', function (data) {
if ('layername' in data && data.layername === layerName) {
handlers.onover(data);
}
});
cleanupFns.push(cleanup);
}
// onout
if (handlers.onout) {
api.set("layer[".concat(layerName, "].onout"), "js(krpanoEventBridge(\"onlayerout\", {layername:\"".concat(layerName, "\"}))"));
var cleanup = addEventListener('onlayerout', function (data) {
if ('layername' in data && data.layername === layerName) {
handlers.onout(data);
}
});
cleanupFns.push(cleanup);
}
return function () {
cleanupFns.forEach(function (fn) {
return fn();
});
};
}, [api, layerName, handlers, addEventListener]);
}
var useHotspotEvent = function useHotspotEvent(hotspotName, handlers) {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
var addEventListener = useKrpanoEventBridge().addEventListener;
react.useEffect(function () {
if (!api) return function () {};
var cleanupFunctions = [];
if (handlers.onclick) {
api.set("hotspot[".concat(hotspotName, "].onclick"), "js(krpanoEventBridge(\"onhotspotclick\", {hotspotname: \"".concat(hotspotName, "\", stagex: get(mouse.stagex), stagey: get(mouse.stagey), ath: get(hotspot[").concat(hotspotName, "].ath), atv: get(hotspot[").concat(hotspotName, "].atv)}))"));
var cleanup = addEventListener('onhotspotclick', function (data) {
if ('hotspotname' in data && data.hotspotname === hotspotName) {
handlers.onclick(data);
}
});
cleanupFunctions.push(cleanup);
}
if (handlers.onover) {
api.set("hotspot[".concat(hotspotName, "].onover"), "js(krpanoEventBridge(\"onhotspotover\", {hotspotname: \"".concat(hotspotName, "\", ath: get(hotspot[").concat(hotspotName, "].ath), atv: get(hotspot[").concat(hotspotName, "].atv)}))"));
var cleanup = addEventListener('onhotspotover', function (data) {
if ('hotspotname' in data && data.hotspotname === hotspotName) {
handlers.onover(data);
}
});
cleanupFunctions.push(cleanup);
}
if (handlers.onout) {
api.set("hotspot[".concat(hotspotName, "].onout"), "js(krpanoEventBridge(\"onhotspotout\", {hotspotname: \"".concat(hotspotName, "\"}))"));
var cleanup = addEventListener('onhotspotout', function (data) {
if ('hotspotname' in data && data.hotspotname === hotspotName) {
handlers.onout(data);
}
});
cleanupFunctions.push(cleanup);
}
return function () {
cleanupFunctions.forEach(function (cleanup) {
return cleanup();
});
};
}, [api, addEventListener]);
};
function useViewEvents(handlers) {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var addEventListener = useKrpanoEventBridge().addEventListener;
var api = ctx.api;
react.useEffect(function () {
if (!api) return;
var cleanupFns = [];
// onviewchange
if (handlers.onviewchange) {
api.set("events.onviewchange", "js(krpanoEventBridge(\"onviewchange\", {hlookat:get(view.hlookat), vlookat:get(view.vlookat), fov:get(view.fov)}))");
var cleanup = addEventListener("onviewchange", function (data) {
handlers.onviewchange(data);
});
cleanupFns.push(cleanup);
}
// onviewchanged
if (handlers.onviewchanged) {
api.set("events.onviewchanged", "js(krpanoEventBridge(\"onviewchanged\", {hlookat:get(view.hlookat), vlookat:get(view.vlookat), fov:get(view.fov)}))");
var cleanup = addEventListener("onviewchanged", function (data) {
handlers.onviewchanged(data);
});
cleanupFns.push(cleanup);
}
// onidle
if (handlers.onidle) {
api.set("events.onidle", "js(krpanoEventBridge(\"onidle\", {}))");
var cleanup = addEventListener("onidle", function (data) {
handlers.onidle(data);
});
cleanupFns.push(cleanup);
}
return function () {
cleanupFns.forEach(function (fn) {
return fn();
});
};
}, [api, handlers, addEventListener]);
}
function useSceneEvents(handlers) {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var addEventListener = useKrpanoEventBridge().addEventListener;
var api = ctx.api;
react.useEffect(function () {
if (!api) return;
var cleanupFns = [];
// onstart
if (handlers.onstart) {
api.set("events.onstart", "js(krpanoEventBridge(\"onstart\", {scene:get(xml.scene)}))");
var cleanup = addEventListener("onstart", function (data) {
handlers.onstart(data);
});
cleanupFns.push(cleanup);
}
// onloaded
if (handlers.onloaded) {
api.set("events.onloaded", "js(krpanoEventBridge(\"onloaded\", {scene:get(xml.scene)}))");
var cleanup = addEventListener("onloaded", function (data) {
handlers.onloaded(data);
});
cleanupFns.push(cleanup);
}
// onready
if (handlers.onready) {
api.set("events.onready", "js(krpanoEventBridge(\"onready\", {scene:get(xml.scene)}))");
var cleanup = addEventListener("onready", function (data) {
handlers.onready(data);
});
cleanupFns.push(cleanup);
}
// onxmlcomplete
if (handlers.onxmlcomplete) {
api.set("events.onxmlcomplete", "js(krpanoEventBridge(\"onxmlcomplete\", {scene:get(xml.scene)}))");
var cleanup = addEventListener("onxmlcomplete", function (data) {
handlers.onxmlcomplete(data);
});
cleanupFns.push(cleanup);
}
// onnewpano
if (handlers.onnewpano) {
api.set("events.onnewpano", "js(krpanoEventBridge(\"onnewpano\", {scene:get(xml.scene)}))");
var cleanup = addEventListener("onnewpano", function (data) {
handlers.onnewpano(data);
});
cleanupFns.push(cleanup);
}
// onremovepano
if (handlers.onremovepano) {
api.set("events.onremovepano", "js(krpanoEventBridge(\"onremovepano\", {scene:get(xml.scene)}))");
var cleanup = addEventListener("onremovepano", function (data) {
handlers.onremovepano(data);
});
cleanupFns.push(cleanup);
}
return function () {
cleanupFns.forEach(function (fn) {
return fn();
});
};
}, [api, handlers, addEventListener]);
}
function useMouseEvents(handlers) {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var addEventListener = useKrpanoEventBridge().addEventListener;
var api = ctx.api;
react.useEffect(function () {
if (!api) return;
var cleanupFns = [];
// onclick
if (handlers.onclick) {
api.set("events.onclick", "js(krpanoEventBridge(\"onclick\", {stagex:get(mouse.stagex), stagey:get(mouse.stagey)}))");
var cleanup = addEventListener("onclick", function (data) {
handlers.onclick(data);
});
cleanupFns.push(cleanup);
}
// ondblclick
if (handlers.ondblclick) {
api.set("events.ondblclick", "js(krpanoEventBridge(\"ondblclick\", {stagex:get(mouse.stagex), stagey:get(mouse.stagey)}))");
var cleanup = addEventListener("ondblclick", function (data) {
handlers.ondblclick(data);
});
cleanupFns.push(cleanup);
}
// onmousedown
if (handlers.onmousedown) {
api.set("events.onmousedown", "js(krpanoEventBridge(\"onmousedown\", {stagex:get(mouse.stagex), stagey:get(mouse.stagey), button:get(mouse.button)}))");
var cleanup = addEventListener("onmousedown", function (data) {
handlers.onmousedown(data);
});
cleanupFns.push(cleanup);
}
// onmouseup
if (handlers.onmouseup) {
api.set("events.onmouseup", "js(krpanoEventBridge(\"onmouseup\", {stagex:get(mouse.stagex), stagey:get(mouse.stagey), button:get(mouse.button)}))");
var cleanup = addEventListener("onmouseup", function (data) {
handlers.onmouseup(data);
});
cleanupFns.push(cleanup);
}
// onmousemove
if (handlers.onmousemove) {
api.set("events.onmousemove", "js(krpanoEventBridge(\"onmousemove\", {stagex:get(mouse.stagex), stagey:get(mouse.stagey)}))");
var cleanup = addEventListener("onmousemove", function (data) {
handlers.onmousemove(data);
});
cleanupFns.push(cleanup);
}
return function () {
cleanupFns.forEach(function (fn) {
return fn();
});
};
}, [api, handlers, addEventListener]);
}
function useSystemEvents(handlers) {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
var addEventListener = useKrpanoEventBridge().addEventListener;
react.useEffect(function () {
if (!api) return;
var cleanupFns = [];
// helper để đăng ký
var register = function register(event, expr) {
if (handlers[event]) {
api.set("events.".concat(event), expr);
var cleanup = addEventListener(event, function (data) {
var _a, _b;
switch (event) {
case "onkeydown":
case "onkeyup":
(_a = handlers[event]) === null || _a === void 0 ? void 0 : _a.call(handlers, data);
break;
case "onready":
case "onxmlcomplete":
case "onloadcomplete":
case "onnewpano":
case "onremovepano":
case "onresize":
case "onenterfullscreen":
case "onexitfullscreen":
(_b = handlers[event]) === null || _b === void 0 ? void 0 : _b.call(handlers, data);
break;
}
});
cleanupFns.push(cleanup);
}
};
// đăng ký các event
register("onready", "js(krpanoEventBridge(\"onready\", {}))");
register("onxmlcomplete", "js(krpanoEventBridge(\"onxmlcomplete\", {}))");
register("onnewpano", "js(krpanoEventBridge(\"onnewpano\", {scene:get(xml.scene)}))");
register("onremovepano", "js(krpanoEventBridge(\"onremovepano\", {}))");
register("onkeydown", "js(krpanoEventBridge(\"onkeydown\", {keycode:get(keycode)}))");
register("onkeyup", "js(krpanoEventBridge(\"onkeyup\", {keycode:get(keycode)}))");
register("onresize", "js(krpanoEventBridge(\"onresize\", {}))");
register("onenterfullscreen", "js(krpanoEventBridge(\"onenterfullscreen\", {}))");
register("onexitfullscreen", "js(krpanoEventBridge(\"onexitfullscreen\", {}))");
return function () {
return cleanupFns.forEach(function (fn) {
return fn();
});
};
}, [api, handlers, addEventListener]);
}
function useKeyboardEvents(handlers) {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var addEventListener = useKrpanoEventBridge().addEventListener;
var api = ctx.api;
react.useEffect(function () {
if (!api) return;
var cleanupFns = [];
// onkeydown
if (handlers.onkeydown) {
api.set("events.onkeydown", "js(krpanoEventBridge(\"onkeydown\", {\n keycode:get(key.keycode),\n key:get(key.key),\n ctrlkey:get(key.ctrlkey),\n shiftkey:get(key.shiftkey),\n altkey:get(key.altkey)\n }))");
var cleanup = addEventListener("onkeydown", function (data) {
handlers.onkeydown(data);
});
cleanupFns.push(cleanup);
}
// onkeyup
if (handlers.onkeyup) {
api.set("events.onkeyup", "js(krpanoEventBridge(\"onkeyup\", {\n keycode:get(key.keycode),\n key:get(key.key),\n ctrlkey:get(key.ctrlkey),\n shiftkey:get(key.shiftkey),\n altkey:get(key.altkey)\n }))");
var cleanup = addEventListener("onkeyup", function (data) {
handlers.onkeyup(data);
});
cleanupFns.push(cleanup);
}
return function () {
cleanupFns.forEach(function (fn) {
return fn();
});
};
}, [api, handlers, addEventListener]);
}
/**
* Hook để lắng nghe events từ Krpano
*/
function useKrpanoEventListener() {
var ctx = react.useContext(KrpanoContext);
if (!ctx) throw new Error("KrpanoContext chưa được cung cấp");
var api = ctx.api;
var handlersRef = react.useRef(new Map());
// Khởi tạo global event bridge
react.useEffect(function () {
if (typeof window !== 'undefined') {
// Tạo global bridge function để Krpano có thể gọi
window.krpanoEventBridge = function (eventType, eventData) {
var handlers = handlersRef.current.get(eventType);
if (handlers) {
var processedData_1 = __assign({
type: eventType,
timestamp: Date.now()
}, eventData);
handlers.forEach(function (handler) {
try {
handler(processedData_1);
} catch (error) {
console.error("Error in Krpano event handler for ".concat(eventType, ":"), error);
}
});
}
};
// Setup Krpano event listeners
setupKrpanoEventListeners();
}
return function () {
if (typeof window !== 'undefined') {
delete window.krpanoEventBridge;
}
};
}, [api]);
// Setup các event listeners trong Krpano
var setupKrpanoEventListeners = react.useCallback(function () {
if (!api) return;
// System Events
api.set('events.onloadcomplete', 'js(krpanoEventBridge("onloadcomplete", {}))');
api.set('events.onxmlcomplete', 'js(krpanoEventBridge("onxmlcomplete", {}))');
api.set('events.onready', 'js(krpanoEventBridge("onready", {}))');
api.set('events.onerror', 'js(krpanoEventBridge("onerror", {error: get(lasterror)}))');
api.set('events.onresize', 'js(krpanoEventBridge("onresize", {width: get(stagewidth), height: get(stageheight)}))');
api.set('events.onfullscreenchange', 'js(krpanoEventBridge("onfullscreenchange", {fullscreen: get(fullscreen)}))');
// Scene Events
api.set('events.onnewscene', 'js(krpanoEventBridge("onnewscene", {scenename: get(xml.scene), sceneindex: get(xml.sceneindex)}))');
api.set('events.onremovescene', 'js(krpanoEventBridge("onremovescene", {scenename: get(xml.scene)}))');
// View Events
api.set('events.onviewchange', 'js(krpanoEventBridge("onviewchange", {hlookat: get(view.hlookat), vlookat: get(view.vlookat), fov: get(view.fov)}))');
api.set('events.onviewchanged', 'js(krpanoEventBridge("onviewchanged", {hlookat: get(view.hlookat), vlookat: get(view.vlookat), fov: get(view.fov)}))');
api.set('events.onidle', 'js(krpanoEventBridge("onidle", {}))');
// Mouse Events
api.set('events.onclick', 'js(krpanoEventBridge("onclick", {stagex: get(mouse.stagex), stagey: get(mouse.stagey)}))');
api.set('events.ondblclick', 'js(krpanoEventBridge("ondblclick", {stagex: get(mouse.stagex), stagey: get(mouse.stagey)}))');
api.set('events.onmousedown', 'js(krpanoEventBridge("onmousedown", {stagex: get(mouse.stagex), stagey: get(mouse.stagey), button: get(mouse.button)}))');
api.set('events.onmouseup', 'js(krpanoEventBridge("onmouseup", {stagex: get(mouse.stagex), stagey: get(mouse.stagey), button: get(mouse.button)}))');
api.set('events.onmousemove', 'js(krpanoEventBridge("onmousemove", {stagex: get(mouse.stagex), stagey: get(mouse.stagey)}))');
// Keyboard Events
api.set('events.onkeydown', 'js(krpanoEventBridge("onkeydown", {keycode: get(key.keycode), key: get(key.key), ctrlkey: get(key.ctrlkey), shiftkey: get(key.shiftkey), altkey: get(key.altkey)}))');
api.set('events.onkeyup', 'js(krpanoEventBridge("onkeyup", {keycode: get(key.keycode), key: get(key.key), ctrlkey: get(key.ctrlkey), shiftkey: get(key.shiftkey), altkey: get(key.altkey)}))');
}, [api]);
// Thêm event listener
var addEventListener = react.useCallback(function (eventType, handler) {
var _a;
if (!handlersRef.current.has(eventType)) {
handlersRef.current.set(eventType, new Set());
}
(_a = handlersRef.current.get(eventType)) === null || _a === void 0 ? void 0 : _a.add(handler);
// Return cleanup function
return function () {
var _a;
(_a = handlersRef.current.get(eventType)) === null || _a === void 0 ? void 0 : _a["delete"](handler);
};
}, []);
// Xóa event listener
var removeEventListener = react.useCallback(function (eventType, handler) {
var _a;
(_a = handlersRef.current.get(eventType)) === null || _a === void 0 ? void 0 : _a["delete"](handler);
}, []);
// Xóa tất cả listeners của một event
var removeAllEventListeners = react.useCallback(function (eventType) {
if (eventType) {
handlersRef.current["delete"](eventType);
} else {
handlersRef.current.clear();
}
}, []);
// Trigger custom event
var triggerCustomEvent = react.useCallback(function (eventType, data) {
if (typeof window !== 'undefined' && window.krpanoEventBridge) {
window.krpanoEventBridge(eventType, data);
}
}, []);
// Register multiple event handlers at once
var registerEventHandlers = react.useCallback(function (handlers) {
var cleanupFunctions = [];
Object.entries(handlers).forEach(function (_a) {
var eventType = _a[0],
handler = _a[1];
if (handler) {
var cleanup = addEventListener(eventType, handler);
cleanupFunctions.push(cleanup);
}
});
// Return cleanup function for all handlers
return function () {
cleanupFunctions.forEach(function (cleanup) {
return cleanup();
});
};
}, [addEventListener]);
// Setup hotspot event handler dynamically
var setupHotspotEvents = react.useCallback(function (hotspotName, handlers) {
if (!api) return function () {};
var cleanupFunctions = [];
if (handlers.onclick) {
api.set("hotspot[".concat(hotspotName, "].onclick"), "js(krpanoEventBridge(\"onhotspotclick\", {hotspotname: \"".concat(hotspotName, "\", stagex: get(mouse.stagex), stagey: get(mouse.stagey), ath: get(hotspot[").concat(hotspotName, "].ath), atv: get(hotspot[").concat(hotspotName, "].atv)}))"));
var cleanup = addEventListener('onhotspotclick', function (data) {
if ('hotspotname' in data && data.hotspotname === hotspotName) {
handlers.onclick(data);
}
});
cleanupFunctions.push(cleanup);
}
if (handlers.onover) {
api.set("hotspot[".concat(hotspotName, "].onover"), "js(krpanoEventBridge(\"onhotspotover\", {hotspotname: \"".concat(hotspotName, "\", ath: get(hotspot[").concat(hotspotName, "].ath), atv: get(hotspot[").concat(hotspotName, "].atv)}))"));
var cleanup = addEventListener('onhotspotover', function (data) {
if ('hotspotname' in data && data.hotspotname === hotspotName) {
handlers.onover(data);
}
});
cleanupFunctions.push(cleanup);
}
if (handlers.onout) {
api.set("hotspot[".concat(hotspotName, "].onout"), "js(krpanoEventBridge(\"onhotspotout\", {hotspotname: \"".concat(hotspotName, "\"}))"));
var cleanup = addEventListener('onhotspotout', function (data) {
if ('hotspotname' in data && data.hotspotname === hotspotName) {
handlers.onout(data);
}
});
cleanupFunctions.push(cleanup);
}
return function () {
cleanupFunctions.forEach(function (cleanup) {
return cleanup();
});
};
}, [api, addEventListener]);
// Setup layer event handler dynamically
var setupLayerEvents = react.useCallback(function (layerName, handlers) {
if (!api) return function () {};
var cleanupFunctions = [];
if (handlers.onclick) {
api.set("layer[".concat(layerName, "].onclick"), "js(krpanoEventBridge(\"onlayerclick\", {layername: \"".concat(layerName, "\", stagex: get(mouse.stagex), stagey: get(mouse.stagey)}))"));
var cleanup = addEventListener('onlayerclick', function (data) {
if ('layername' in data && data.layername === layerName) {
handlers.onclick(data);
}
});
cleanupFunctions.push(cleanup);
}
if (handlers.onover) {
api.set("layer[".concat(layerName, "].onover"), "js(krpanoEventBridge(\"onlayerover\", {layername: \"".concat(layerName, "\"}))"));
var cleanup = addEventListener('onlayerover', function (data) {
if ('layername' in data && data.layername === layerName) {
handlers.onover(data);
}
});
cleanupFunctions.push(cleanup);
}
if (handlers.onout) {
api.set("layer[".concat(layerName, "].onout"), "js(krpanoEventBridge(\"onlayerout\", {layername: \"".concat(layerName, "\"}))"));
var cleanup = addEventListener('onlayerout', function (data) {
if ('layername' in data && data.layername === layerName) {
handlers.onout(data);
}
});
cleanupFunctions.push(cleanup);
}
return function () {
cleanupFunctions.forEach(function (cleanup) {
return cleanup();
});
};
}, [api, addEventListener]);
return {
// Core functions
addEventListener: addEventListener,
removeEventListener: removeEventListener,
removeAllEventListeners: removeAllEventListeners,
registerEventHandlers: registerEventHandlers,
triggerCustomEvent: triggerCustomEvent,
// Specialized functions
setupHotspotEvents: setupHotspotEvents,
setupLayerEvents: setupLayerEvents,
// Utility functions
isListening: function isListening(eventType) {
return handlersRef.current.has(eventType);
},
getListenerCount: function getListenerCount(eventType) {
var _a;
return ((_a = handlersRef.current.get(eventType)) === null || _a === void 0 ? void 0 : _a.size) || 0;
},
getAllListeners: function getAllListeners() {
return Array.from(handlersRef.current.keys());
}
};
}
var KrpanoViewer = function KrpanoViewer(_a) {
var path = _a.path,
_b = _a.xmlName,
xmlName = _b === void 0 ? "tour.xml" : _b,
_c = _a.jsN