Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,448 @@
'use strict';
// test result indicator
var Status = {
NOT_FINISHED: 1,
FINISHED: 2,
ABORT: 3
};
// UTILS
var util = {
// Use this function to get correct click position on canvas
relMouseCoords: function(canvas, evt) {
var rect = canvas.getBoundingClientRect();
var eventX = evt.clientX - rect.left;
var eventY = evt.clientY - rect.top;
return {x: eventX, y: eventY};
},
clearScreen: function(ctx) {
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
},
drawRectFromCoord: function(ctx, x1, y1, x2, y2) {
ctx.translate(0.5, 0.5);
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
ctx.translate(-0.5, -0.5);
},
// Fill rect without overlapping border,
fillRectFromCoord: function(ctx, x1, y1, x2, y2) {
ctx.fillRect(x1 + 1, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 1);
},
drawCircle: function(ctx, x, y, radius, width, color) {
ctx.lineWidth = width;
ctx.strokeStyle = color;
ctx.beginPath();
ctx.translate(0.5, 0.5);
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.translate(-0.5, -0.5);
ctx.closePath();
ctx.stroke();
},
distance_sqr: function(x1, y1, x2, y2) {
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
},
// Return the index less or equal to num
binary_search: function(arr, num) {
var begin = 0;
var end = arr.length - 1;
while (begin < end) {
var pivot = Math.round((begin + end) / 2);
if (num < arr[pivot]) {
end = pivot - 1;
}
else if (num > arr[pivot]) {
begin = pivot + 1;
}
else {
return pivot;
}
}
if (num < arr[begin]) {
return begin - 1;
}
return begin;
}
};
// TEST 1
// Draw 4 circles on the corners and 1 circle in the center
var test1 = function(canvas) {
var ctx = canvas.getContext('2d');
var circle_radius = Math.min(canvas.width, canvas.height) * 0.1;
var circles_coord =
[[circle_radius, circle_radius],
[circle_radius, canvas.height - circle_radius],
[canvas.width - circle_radius, circle_radius],
[canvas.width - circle_radius, canvas.height - circle_radius],
[canvas.width / 2, canvas.height / 2]];
var lineWidth = 2;
var lineColor = 'red';
var lineColorClicked = 'lime';
var is_clicked = [];
var exitIconPositionX;
var exitIconPositionY;
var exitIconWidth;
var exitIconHeight;
(function init() {
util.clearScreen(ctx);
// Draw not-clicked circle
for (var i = 0; i < circles_coord.length; i++) {
util.drawCircle(ctx, circles_coord[i][0], circles_coord[i][1],
circle_radius, lineWidth, lineColor);
is_clicked[i] = false;
}
// Draw exit icon
var image = document.getElementById('icon-exit');
exitIconWidth = image.width;
exitIconHeight = image.height;
// Draw icon below center
exitIconPositionX = (canvas.width - exitIconWidth) / 2;
exitIconPositionY = canvas.height * 0.65;
ctx.drawImage(image, exitIconPositionX, exitIconPositionY);
})();
// Touch test, change circle color if touch point is in the circle.
// Test ends if all circles are touched
this.test = function(x, y) {
// Clicked on exit button
if (x > exitIconPositionX && x <= exitIconPositionX + exitIconWidth &&
y > exitIconPositionY && y <= exitIconPositionY + exitIconHeight) {
return Status.ABORT;
}
for (var i = 0; i < circles_coord.length; i++) {
if (!is_clicked[i] &&
util.distance_sqr(x, y, circles_coord[i][0], circles_coord[i][1]) <
circle_radius * circle_radius) {
// Clear red circle by covering a bigger white circle on it
util.drawCircle(ctx, circles_coord[i][0], circles_coord[i][1],
circle_radius, lineWidth + 2, 'white');
// Draw new circle
util.drawCircle(ctx, circles_coord[i][0], circles_coord[i][1],
circle_radius, lineWidth, lineColorClicked);
is_clicked[i] = true;
break;
}
}
for (var i = 0; i < circles_coord.length; i++) {
if (!is_clicked[i]) {
return Status.NOT_FINISHED;
}
}
// Test over
return Status.FINISHED;
};
};
// TEST 2
// Draw several cubes on the edge of screen
var test2 = function(canvas) {
var ctx = canvas.getContext('2d');
var aspect = canvas.width / canvas.height;
var cubes_num_vertical = 20;
var cubes_num_horizontal = Math.round(cubes_num_vertical * aspect);
var cube_width = canvas.width / cubes_num_horizontal;
var cube_height = canvas.height / cubes_num_vertical;
var cube_coord_x = [];
var cube_coord_y = [];
var is_clicked_top = [];
var is_clicked_bottom = [];
var is_clicked_left = [];
var is_clicked_right = [];
var total_clicked = 0;
var exitIconPositionX;
var exitIconPositionY;
var exitIconWidth;
var exitIconHeight;
(function init() {
util.clearScreen(ctx);
ctx.strokeStyle = 'black';
ctx.fillStyle = 'lime';
ctx.lineWidth = 1;
// Because width/height of screen can not be divided evenly
// So we precaculate the grid (coordination) of cubes for future use
for (var i = 0; i < cubes_num_horizontal; i++) {
cube_coord_x[i] = Math.round(cube_width * i);
}
cube_coord_x[cubes_num_horizontal] = canvas.width - 1;
for (var i = 0; i < cubes_num_vertical; i++) {
cube_coord_y[i] = Math.round(cube_height * i);
}
cube_coord_y[cubes_num_vertical] = canvas.height - 1;
// Draw Top/Bottom/Left/Right cubes
for (var i = 0; i < cubes_num_horizontal; i++) {
util.drawRectFromCoord(ctx, cube_coord_x[i], cube_coord_y[0],
cube_coord_x[i + 1], cube_coord_y[1]);
is_clicked_top[i] = false;
}
for (var i = 0; i < cubes_num_horizontal; i++) {
util.drawRectFromCoord(ctx,
cube_coord_x[i],
cube_coord_y[cubes_num_vertical - 1],
cube_coord_x[i + 1],
cube_coord_y[cubes_num_vertical]);
is_clicked_bottom[i] = false;
}
for (var i = 0; i < cubes_num_vertical; i++) {
util.drawRectFromCoord(ctx,
cube_coord_x[0],
cube_coord_y[i],
cube_coord_x[1],
cube_coord_y[i + 1]);
is_clicked_left[i] = false;
}
for (var i = 0; i < cubes_num_vertical; i++) {
util.drawRectFromCoord(ctx,
cube_coord_x[cubes_num_horizontal - 1],
cube_coord_y[i],
cube_coord_x[cubes_num_horizontal],
cube_coord_y[i + 1]);
is_clicked_right[i] = false;
}
// Draw exit icon
var image = document.getElementById('icon-exit');
exitIconWidth = image.width;
exitIconHeight = image.height;
exitIconPositionX = (canvas.width - exitIconWidth) / 2;
exitIconPositionY = (canvas.height - exitIconHeight) / 2;
ctx.drawImage(image, exitIconPositionX, exitIconPositionY);
}
)();
// Touch test, fill cubes if touch point is in the cube.
// Test ends if all cubes are touched
this.test = function(x, y) {
// Clicked on exit button
if (x > exitIconPositionX && x <= exitIconPositionX + exitIconWidth &&
y > exitIconPositionY && y <= exitIconPositionY + exitIconHeight) {
return Status.ABORT;
}
if (cube_coord_x[1] <= x && x <= cube_coord_x[cubes_num_horizontal - 1] &&
cube_coord_y[1] <= y && y <= cube_coord_y[cubes_num_vertical - 1]) {
return Status.NOT_FINISHED;
}
var startXi = util.binary_search(cube_coord_x, x);
var startYi = util.binary_search(cube_coord_y, y);
util.fillRectFromCoord(ctx,
cube_coord_x[startXi],
cube_coord_y[startYi],
cube_coord_x[startXi + 1],
cube_coord_y[startYi + 1]);
if (startYi == 0 && !is_clicked_top[startXi]) {
is_clicked_top[startXi] = true;
}
if (startYi == cubes_num_vertical - 1 && !is_clicked_bottom[startXi]) {
is_clicked_bottom[startXi] = true;
}
if (startXi == 0 && !is_clicked_left[startYi]) {
is_clicked_left[startYi] = true;
}
if (startXi == cubes_num_horizontal - 1 && !is_clicked_right[startYi]) {
is_clicked_right[startYi] = true;
}
for (var i = 0; i < cubes_num_vertical; i++) {
if (!is_clicked_left[i] || !is_clicked_right[i]) {
return Status.NOT_FINISHED;
}
}
for (var i = 0; i < cubes_num_horizontal; i++) {
if (!is_clicked_top[i] || !is_clicked_bottom[i]) {
return Status.NOT_FINISHED;
}
}
// Test over
return Status.FINISHED;
};
};
var test_driver = function() {
var current_test = null;
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.width = canvas.width + 'px';
canvas.style.height = canvas.height + 'px';
// returns true if no other tests
function proceed() {
if (current_test == null) {
current_test = new test1(canvas);
return false;
}
else if (current_test instanceof test1) {
current_test = new test2(canvas);
return false;
}
else if (current_test instanceof test2) {
return true;
}
}
proceed();
// returns whether all tests are finished
this.touch = function(x, y) {
var touchResult = current_test.test(x, y);
if (touchResult == Status.ABORT) {
return Status.ABORT;
}
// one test finished, go on next test
if (touchResult == Status.FINISHED) {
var isAllFinished = proceed();
if (isAllFinished) {
return Status.FINISHED;
}
}
return Status.NOT_FINISHED;
};
};
// Handle touch input, Codes modified from
// https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Events/Touch_events
var touchTest = function() {
var ongoingTouches = [];
var driver = null;
var canvas = null;
function copyTouch(touch) {
return { identifier: touch.identifier,
pageX: touch.pageX,
pageY: touch.pageY };
}
function ongoingTouchIndexById(idToFind) {
for (var i = 0; i < ongoingTouches.length; i++) {
var id = ongoingTouches[i].identifier;
if (id == idToFind) {
return i;
}
}
return -1; // not found
}
function handleEvent(evt) {
var touches = evt.changedTouches;
var result;
evt.preventDefault();
switch (evt.type) {
case 'touchstart':
for (var i = 0; i < touches.length; i++) {
ongoingTouches.push(copyTouch(touches[i]));
var coord = util.relMouseCoords(canvas, touches[i]);
result = driver.touch(coord.x, coord.y);
}
break;
case 'touchmove':
for (var i = 0; i < touches.length; i++) {
var coord = util.relMouseCoords(canvas, touches[i]);
var idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
var coord_last = util.relMouseCoords(canvas, ongoingTouches[idx]);
result = driver.touch(coord.x, coord.y);
// swap in the new touch record
ongoingTouches.splice(idx, 1, copyTouch(touches[i]));
} else {
console.log('can\'t figure out which touch to continue');
}
}
break;
case 'touchend':
for (var i = 0; i < touches.length; i++) {
var coord = util.relMouseCoords(canvas, touches[i]);
var idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
ongoingTouches.splice(i, 1); // remove it; we're done
} else {
console.log('can\'t figure out which touch to end');
}
}
break;
case 'touchcancel':
for (var i = 0; i < touches.length; i++) {
ongoingTouches.splice(i, 1); // remove it; we're done
}
break;
}
// All tests over
if (result == Status.FINISHED || result == Status.ABORT) {
// Clear remaining touches
touches = null;
canvas.removeEventListener('touchstart', handleEvent, false);
canvas.removeEventListener('touchend', handleEvent, false);
canvas.removeEventListener('touchcancel', handleEvent, false);
canvas.removeEventListener('touchleave', handleEvent, false);
canvas.removeEventListener('touchmove', handleEvent, false);
console.log('uninitialized.');
if (result == Status.ABORT) {
alert('Test terminated by user');
} else {
alert('Test Success!');
}
document.mozCancelFullScreen();
// This is a hack to close touch test, uitest.js will close this iframe
window.parent.window.location.hash = '';
}
}
this.init = function() {
driver = new test_driver();
if (canvas == null) {
canvas = document.getElementsByTagName('canvas')[0];
canvas.addEventListener('touchstart', handleEvent, false);
canvas.addEventListener('touchend', handleEvent, false);
canvas.addEventListener('touchcancel', handleEvent, false);
canvas.addEventListener('touchleave', handleEvent, false);
canvas.addEventListener('touchmove', handleEvent, false);
console.log('initialized.');
}
};
};
// XXX: There is a problem of getting into fullscreen
// When we called mozRequestFullscreen, it actually resized two times.
// We get almost fullscreen (except status bar) after first event, and truely
// fullscreen after second event. So we need to wait for the second event.
var resizedTimes = 0;
window.addEventListener('resize', function startTest() {
if (resizedTimes < 1) {
resizedTimes++;
}
else {
window.removeEventListener('resize', startTest);
var test = new touchTest;
test.init();
}
});
window.addEventListener('load', function() {
document.getElementById('canvas').classList.remove('invisible');
var exitButton = document.getElementById('fullscreen-exit');
document.body.mozRequestFullScreen();
});