remoteview
Version:
Remote isolated browser platform by Dosyago
320 lines (292 loc) • 11.1 kB
JavaScript
import {iden,deviceIsMobile, throttle,DEBUG,elogit} from './common.js';
import {cloneKeyEvent} from './constructor.js';
import {d as R} from '../node_modules/dumbass/r.js';
import * as Subviews from './subviews/index.js';
import {dss, stylists} from './styles.js';
import {getBitmapCoordinates} from './transformEvent.js';
export const subviews = Subviews;
//const DEFAULT_URL = 'https://google.com';
//const isIOS = navigator.platform && navigator.platform.match("iPhone|iPod|iPad");
const USE_INPUT_MODE = false;
export function component(state) {
const {H,/*sizeBrowserToBounds,*/ asyncSizeBrowserToBounds, emulateNavigator, bondTasks, /*installFrameListener,*/ canvasBondTasks} = state;
const audio_port = Number(location.port ? location.port : ( location.protocol == 'https' ? 443 : 80 ) ) - 2;
const audio_url = `${location.protocol}//${location.hostname}:${audio_port}/`;
//const FocusBorrowerSel = '[name="address"], #selectinput, .control';
const viewState = Object.assign(state.viewState, {
touchX: 0, touchY: 0,
textarea: null,
keyinput: null,
canvasEl: null,
viewFrameEl: null,
shouldHaveFocus: null,
focusTextarea, blurTextarea,
focusKeyinput, blurKeyinput
});
state.viewState = viewState;
const toggleVirtualKeyboard = e => {
e.preventDefault();
let el = viewState.shouldHaveFocus;
if ( el ) {
if ( el == viewState.keyinput ) {
blurKeyinput();
} else if ( el == viewState.textarea ) {
blurTextarea();
}
} else {
focusKeyinput();
}
};
const retargetTab = e => retargetTabToRemote(e,H);
state.retargetTab = retargetTab;
state.toggleVirtualKeyboard = toggleVirtualKeyboard;
// this will likely have to be updated for iOS since "keyboard summons by focus" MUST
// be triggered by a user action, I believe, and I think it will not work after a setTimeout
/*const refocusMeIfNotAllowedBorrower = (e, view_state) => {
const me = e.target;
setTimeout(() => {
const active = document.activeElement;
if ( !! active && active.matches ) {
if ( ! active.matches(FocusBorrowerSel) && view_state.shouldHaveFocus == me ) {
me.focus();
}
}
}, 50);
};*/
const retargetTouchScroll = e => retargetTouchScrollToRemote(e,H,viewState);
bondTasks.unshift(el => state.viewState.voodooEl = el);
bondTasks.push(() => dss.initializeDSS(state, stylists));
bondTasks.push(() => {
document.addEventListener('keydown', event => {
if ( !event.target.matches('body') || state.viewState.shouldHaveFocus ) return;
if ( event.code == "Space" ) {
state.H({
type: 'wheel',
target: state.viewState.canvasEl,
pageX: 0,
pageY: 0,
clientX: 0,
clientY: 0,
deltaMode: 2,
deltaX: 0,
contextId: state.viewState.latestScrollContext,
deltaY: event.shiftKey ? -0.618 : 0.618
});
//event.preventDefault();
} else if ( event.key == "Tab" ) {
retargetTab(event);
} else if ( event.key == "Enter" ) {
H(cloneKeyEvent(event, true));
}
});
document.addEventListener('keyup', event => {
if ( !event.target.matches('body') || state.viewState.shouldHaveFocus ) return;
if ( event.key == "Enter" ) {
H(cloneKeyEvent(event, true));
}
});
});
subviews.startBandwidthLoop(state);
state.viewState.draw = () => {
return R`
<main class="voodoo" bond=${bondTasks} stylist="styleVoodooMain">
${subviews.BandwidthIndicator(state)}
${subviews.TabList(state)}
${subviews.Controls(state)}
<article class=tab-viewport stylist="styleTabViewport styleContextMenu">
${subviews.LoadingIndicator(state)}
${state.useViewFrame ? (
state.demoMode ?
R`
<iframe name=viewFrame
scrolling=yes
src=/plugins/demo/index.html
load=${[
loaded => loaded.target.hasLoaded = true,
state.installFrameListener,
...canvasBondTasks
]}
bond=${[
el => state.viewState.viewFrameEl = el,
asyncSizeBrowserToBounds,
emulateNavigator,
...canvasBondTasks
]}
></iframe>
` :
state.factoryMode ?
R`
<iframe name=viewFrame
scrolling=yes
load=${[loaded => loaded.target.hasLoaded = true, ...canvasBondTasks]}
bond=${[
el => state.viewState.viewFrameEl = el,
asyncSizeBrowserToBounds,
emulateNavigator,
state.installFrameListener,
...canvasBondTasks,
el => el.src = `/plugins/projector/${isBundle()? 'bundle' : 'index'}.html`
]}
></iframe>
`
:
R`
<iframe name=viewFrame
scrolling=yes
load=${[loaded => loaded.target.hasLoaded = true, ...canvasBondTasks]}
bond=${[
el => state.viewState.viewFrameEl = el,
asyncSizeBrowserToBounds,
emulateNavigator,
state.installFrameListener,
...canvasBondTasks,
el => el.src = `/plugins/appminifier/${isBundle()? 'bundle' : 'index'}.html`
]}
></iframe>
`
) :
R`
<canvas
click=${[
elogit,
() => {
if ( viewState.shouldHaveFocus && document.activeElement != viewState.shouldHaveFocus ) {
viewState.shouldHaveFocus.focus();
}
}
]}
bond=${[saveCanvas, asyncSizeBrowserToBounds, emulateNavigator, ...canvasBondTasks]}
touchstart:passive=${[elogit,retargetTouchScroll]}
touchmove=${[
e => e.preventDefault(),
elogit,
throttle(retargetTouchScroll, state.EVENT_THROTTLE_MS)
]}
wheel:passive=${throttle(H, state.EVENT_THROTTLE_MS)}
mousemove:passive=${[elogit, throttle(H, state.EVENT_THROTTLE_MS)]}
mousedown=${[elogit,H]}
mouseup=${[elogit,H]}
pointermove:passive=${[elogit, throttle(H, state.EVENT_THROTTLE_MS)]}
pointerdown=${[
deviceIsMobile() ? e => startTimer(e, state.viewState) : iden,
elogit,
H
]}
pointerup=${[
deviceIsMobile() ? e => endTimer(e, state.viewState) : iden,
elogit,
H
]}
contextmenu=${[elogit,subviews.makeContextMenuHandler(state)]}
></canvas>
`
}
<select id=selectinput stylist="styleSelectInput"
input=${e => H({
synthetic: true,
type: "select",
state,
event: e
})}
>
<option value="" disabled>Select an option</option>
</select>
</article>
${subviews.Modals(state)}
</main>
<audio bond=${el => self.addEventListener('click', () => el.play(), {once:true})} autoplay loop id=audio>
<source src="${audio_url}" type=audio/mp3>
</audio>
${DEBUG.pluginsMenu ? subviews.PluginsMenu(state) : ''}
`;
};
state.viewState.dss = dss;
return state.viewState.draw();
function isBundle() {
return location.pathname == "/bundle.html";
}
function focusKeyinput(type = 'text', inputmode = 'text', value = '') {
const {viewState} = state;
viewState.keyinput.type = type;
if ( USE_INPUT_MODE ) {
viewState.keyinput.inputmode = inputmode;
}
viewState.keyinput.value = value;
if ( document.activeElement != viewState.keyinput ) {
viewState.keyinput.focus({preventScroll:true});
}
viewState.shouldHaveFocus = viewState.keyinput;
}
function blurKeyinput() {
const {viewState} = state;
if ( document.activeElement == viewState.keyinput )
viewState.keyinput.blur();
viewState.shouldHaveFocus = null;
}
function focusTextarea(inputmode = 'text', value = '') {
const {viewState} = state;
if ( USE_INPUT_MODE ) {
viewState.textarea.inputmode = inputmode;
}
//viewState.textarea.value = value;
if ( document.activeElement != viewState.textarea ) {
viewState.textarea.focus({preventScroll:true});
}
viewState.shouldHaveFocus = viewState.textarea;
}
function blurTextarea() {
const {viewState} = state;
if ( document.activeElement == viewState.textarea )
viewState.textarea.blur();
viewState.shouldHaveFocus = null;
}
function saveCanvas(canvasEl) {
state.viewState.canvasEl = canvasEl;
state.viewState.ctx = canvasEl.getContext('2d');
}
}
// helper functions
function startTimer(e, viewState) {
const {pointerId:pointerId = 'default'} = e;
viewState[pointerId] = performance.now();
}
function endTimer(e, viewState) {
const {pointerId:pointerId = 'default'} = e;
viewState[pointerId] = performance.now() - viewState[pointerId];
if ( viewState[pointerId] > subviews.CTX_MENU_THRESHOLD ) {
e.preventDefault();
e.stopPropagation();
}
}
function retargetTouchScrollToRemote(event, H, viewState) {
const {type} = event;
const {target} = event;
const {changedTouches:changes} = event;
if ( changes.length > 1 ) return;
const touch = changes[0];
const {clientX,clientY} = touch;
const {bitmapX,bitmapY} = getBitmapCoordinates({target,clientX,clientY});
if ( type == 'touchmove' ) {
event.preventDefault();
const deltaX = Math.ceil(viewState.touchX - bitmapX);
const deltaY = Math.ceil(viewState.touchY - bitmapY);
viewState.killNextMouseReleased = true;
H({
synthetic: true,
type: "touchscroll",
bitmapX, bitmapY,
deltaX, deltaY,
event: event,
contextId: viewState.latestScrollContext
});
}
viewState.touchX = bitmapX;
viewState.touchY = bitmapY;
}
function retargetTabToRemote(event, H) {
if ( event.key !== "Tab" ) return;
event.preventDefault();
event.stopPropagation();
const ev = cloneKeyEvent(event, true);
H(ev);
}