Holy-Unblocker/public/vibeOS/scripts/main.js
2020-10-15 20:25:21 -07:00

569 lines
14 KiB
JavaScript

var mCanvas = document.getElementById('mCanvas'),
overlay = document.getElementById('overlay'),
mctx = mCanvas.getContext('2d'),
msize = {w: mCanvas.width, h: mCanvas.height},
mfocus = false,
renq = [],
highRenq = [],
above_high_renq = [],
images = {},
cursor = {img: 'pointer', grab: {} },
background = {value : 'wallpapers/a.png'},
wallpaperBusiness = ()=>{},
interactables = {},
emptyFunction = ()=>{},
hiddenContainer=document.createElement('div'),
globalProxy = 'https://ldm.sys32.dev/';
// hidden container for hidden elements!
document.body.appendChild(hiddenContainer);
hiddenContainer.style.display = 'none';
hiddenContainer.style.position = 'absolute';
mctx.imageSmoothingEnabled = true;
function wordWrap(str, maxWidth) {
if(typeof str != 'string')return '';
var newLineStr = "\n"; done = false; res = '';
while (str.length > maxWidth) {
found = false;
// Inserts new line at first whitespace of the line
for (i = maxWidth - 1; i >= 0; i--) {
if ( new RegExp(/^\s$/).test(str.charAt(i).charAt(0)) ) {
res = res + [str.slice(0, i), newLineStr].join('');
str = str.slice(i + 1);
found = true;
break;
}
}
// Inserts new line at maxWidth position, the word is too long to wrap
if (!found) {
res += [str.slice(0, maxWidth), newLineStr].join('');
str = str.slice(maxWidth);
}
}
return res + str;
}
CanvasRenderingContext2D.prototype.drawImageURL = function(src, sx , sy, swidth, sheight, width, height){
if(images[src] == null){
// add a new image to the array with the url as a key
images[src] = new Image();
images[src].src = src;
images[src].addEventListener('load', ()=>{
try{
this.drawImage(images[src], sx, sy, swidth, sheight);
}catch(err){
console.error(err, images[src]);
// this.fillStyle = '#000'
// this.fillRect(sx, sy, swidth, sheight);
}
});
} else {
try{
this.drawImage(images[src], sx, sy, swidth, sheight);
}catch(err){
console.error(err, images[src]);
// this.fillStyle = '#000'
// this.fillRect(sx, sy, swidth, sheight);
}
}
// image should exist here
}
CanvasRenderingContext2D.prototype.roundRect = function (x,y,width,height,radius) {
radius = Math.min(Math.max(width-1,1),Math.max(height-1,1),radius);
var rectX = x;
var rectY = y;
var rectWidth = width;
var rectHeight = height;
var cornerRadius = radius;
this.lineJoin = "round";
this.lineWidth = cornerRadius;
this.strokeRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
this.fillRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
this.stroke();
this.fill();
}
CanvasRenderingContext2D.prototype.fillWrapText = function (text, x, y, maxWidth, lineHeight) {
if(typeof text != 'string')return null;
var words = text.split(' ');
var line = '';
for(var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = this.measureText(testLine);
var testWidth = metrics.width;
if(testWidth > maxWidth && n > 0) {
this.fillText(line, x, y);
line = words[n] + ' ';
y += lineHeight;
} else {
line = testLine;
}
}
this.fillText(line, x, y);
}
class interactable {
constructor(id, width, height, hoverstart, hoverend, clickstart, clickend, rightclickstart, rightclickend){
// hm
this.width = Number(width);
this.height = Number(height);
// let the caller define the positions here
this.x = 0;
this.y = 0;
this.index = 0;
this.hover = false;
this.pressed = false;
this.delete = ()=>{
delete interactables[id];
}
this.setValue = (key, value) => {
this[key] = value;
}
this.id = id; // for reading it, dont change
// functions and stuff
if(typeof hoverstart != 'undefined')this.hoverstart = hoverstart
else this.hoverstart = emptyFunction;
if(typeof hoverend != 'undefined')this.hoverend = hoverend
else this.hoverend = emptyFunction;
if(typeof clickstart != 'undefined')this.clickstart = clickstart
else this.clickstart = emptyFunction;
if(typeof clickend != 'undefined')this.clickend = clickend
else this.clickend = emptyFunction;
if(typeof rightclickstart != 'undefined')this.rightclickstart = rightclickstart
else this.rightclickstart = emptyFunction;
if(typeof rightclickend != 'undefined')this.rightclickend = rightclickend
else this.rightclickend = rightclickend;
// near last
interactables[id] = this;
}
}
class cwindow {
constructor(id, x, y, afterRender, whichRenq){
// whichRenq should be renq by default unless set
if(whichRenq == null)this.whichRenq = renq
else this.whichRenq = whichRenq
// let the caller define the title, width, and height?
if(Object.entries(interactables).some((e,i)=> e[0].startsWith(id)))id = id + Date.now()
this.renderFunc = emptyFunction // TEMPORARY VALUE
this.title = 'New Window';
this.width = 500;
this.height = 500;
this.x = x;
this.y = y;
this.closing = emptyFunction; // LET THE CALLER DEFINE THIS, IT IS CALLED WHEN WINDOW GETS CLOSED
this.icon = 'status/24/image-missing.png'
this.bgColor = '#ccc';
this.moveToFront = ()=>{
renq[this.renqID] = emptyFunction;
this.renqID = renq.length;
renq.push(this.renderFunc);
this.contentBox.index = Object.entries(interactables).length;
this.titleBar.index = Object.entries(interactables).length + 1;
this.closeButton.index = Object.entries(interactables).length + 2;
}
this.titleBar = new interactable(id + '_titlebar', this.width, 30, ()=>{
// hoverstart
}, ()=>{
// hoverend
}, ()=>{
// click start
this.moveToFront();
}, ()=>{
// clickend
});
this.renqID = this.whichRenq.length;
this.titleBar.index = this.whichRenq.length;
this.contentBox = new interactable(id + '_contentbox', this.width, this.height,
emptyFunction,
emptyFunction,
()=>{
// click start
// a bit buggy on the contentbox
// this.moveToFront();
/*
if(this.renqID == renq.length-1)return;
console.log(this.renqID,renq.length);
var oldrenqID = this.renqID
this.renqID = renq.length; // last value of array
renq[this.renqID] = renq[oldrenqID];
renq.splice(renq[oldrenqID], 1);
console.log(this.renqID, renq.length);
*/
});
this.closeButton = new interactable(id + '_close', 18, 18,
()=>{ // hover start
}, ()=>{ // hover end
}, ()=>{ // click start
// renq.splice(oldrenqID, 1);
}, ()=>{ // click end
if(this.closeButton.pressed && this.closeButton.hover){
// todo: closing stuff
this.close();
}
});
this.close = ()=>{ // get rid of window nicely
this.whichRenq.splice(this.renqID, 1);
this.closing();
this.contentBox.delete();
this.closeButton.delete();
this.titleBar.delete();
}
this.contentBox.index = this.whichRenq.length + 1;
this.closeButton.index = this.whichRenq.length + 2;
this.renderFunc = ()=>{
// console.log('id:',this.titleBar.id, 'hover:', this.titleBar.hover, 'pressed:', this.titleBar.pressed)
if(this.titleBar.pressed){
var grabX = this.postX + cursor.x - cursor.grab.x,
grabY = this.postY + cursor.y - cursor.grab.y;
this.x = grabX;
this.y = grabY;
}else{
this.postX = this.x;
this.postY = this.y;
}
this.titleBar.x = this.x
this.titleBar.y = this.y
this.titleBar.width = this.width
this.contentBox.x = this.x
this.contentBox.y = this.y + this.titleBar.height
this.contentBox.height = this.height
this.contentBox.width = this.width
// content box
mctx.fillStyle = '#000';
mctx.shadowColor = '#000';
mctx.strokeStyle='#000';
mctx.shadowBlur = 7;
mctx.roundRect(this.x, this.y, this.width, this.height + this.titleBar.height / 1.5, 15);
mctx.shadowBlur = 0;
mctx.fillStyle=this.bgColor;
mctx.strokeStyle=this.bgColor;
mctx.roundRect(this.x, this.y + this.titleBar.height - 10, this.width, this.height, 15);
// window bar
mctx.fillStyle='#404040';
mctx.strokeStyle='#404040';
mctx.roundRect(this.x, this.y, this.width, this.titleBar.height, 15);
mctx.fillRect(this.x, this.y + this.titleBar.height/2, this.titleBar.width, 15);
// title
mctx.fillStyle='#fff';
mctx.font = "14px Open Sans";
mctx.fillText(this.title, this.titleBar.x + 40 , this.titleBar.y + 20 );
// icon
mctx.drawImageURL('tango/' + this.icon, this.titleBar.x + 12, this.titleBar.y + 6, 20, 20);
// close stuff :(
this.closeButton.x = this.titleBar.x + this.titleBar.width - this.titleBar.height;
this.closeButton.y = this.titleBar.y + 5;
if(!this.closeButton.hover && !this.closeButton.pressed){
mctx.drawImageURL('tango/window/close_idle.png', this.closeButton.x, this.closeButton.y, this.closeButton.width, this.closeButton.height)
}else if(this.closeButton.hover && !this.closeButton.pressed){
mctx.drawImageURL('tango/window/close_hover.png', this.closeButton.x, this.closeButton.y, this.closeButton.width, this.closeButton.height)
}else if(this.closeButton.pressed){
mctx.drawImageURL('tango/window/close_press.png', this.closeButton.x, this.closeButton.y, this.closeButton.width, this.closeButton.height)
}
/*
if(this.contentBox.hover == true && this.cursor != null && this.cursor.hover != null){
cursor.img = this.cursor.hover;
}
*/
// do this after all stuff before
afterRender(this);
}
this.whichRenq[this.renqID] = this.renderFunc;
}
}
var mouseMoveHandler = (e)=>{
e.preventDefault();
// set cursor positions
cursor.x = e.layerX;
cursor.y = e.layerY;
var found = [];
Object.entries(interactables).forEach((ee,ii)=>{
var index = ee[0],
entry = ee[1],
xRangeMin=entry.x, xRangeMax=entry.x + entry.width, yRangeMin=entry.y, yRangeMax=entry.y + entry.height,
inX=(cursor.x >= xRangeMin && cursor.x <= xRangeMax), inY=(cursor.y >= yRangeMin && cursor.y <= yRangeMax);
if(inX && inY){ // cursor is inside an element
found.push(index);
}
});
var highestIndexEntry = null;
found.forEach((ee,ii)=>{ // sort through all found, pick the highest index
var entry = interactables[ee],
highestEntry = interactables[highestIndexEntry];
if(entry.disabled == true)return; // ignore disabled elements
if(typeof highestEntry == 'undefined')highestIndexEntry = ee
else if(entry.index >= highestEntry.index)highestIndexEntry = ee;
});
Object.entries(interactables).forEach((ee,ii)=>{
var index = ee[0],
entry = ee[1];
if(entry != highestIndexEntry)interactables[index].setValue('hover', false);
});
if(highestIndexEntry != null){
// set the thing to hover : true and callbacks blah
var entry = interactables[highestIndexEntry];
entry.setValue('hover', true);
entry.hoverstart();
}
}
overlay.addEventListener('mousemove', mouseMoveHandler);
overlay.addEventListener('mousedown', e=>{
e.preventDefault();
mouseMoveHandler(e); // incase some business happened
cursor.down = true;
cursor.grab.x = e.layerX
cursor.grab.y = e.layerY
Object.entries(interactables).forEach((ee,ii)=>{ // check if element is hovered, if so its the top index and mouse is on it
var index = ee[0],
entry = ee[1];
if(entry.hover){
interactables[index].setValue('pressed', true);
if(e.which == 3){ // right click
// do right click business here
// return
}
entry.clickstart(e);
cursor.focus = index;
}
});
});
overlay.addEventListener('mouseup', e=>{
e.preventDefault();
// cursor.hovering = key of element hovering not the actual element to be simple
cursor.down = false;
Object.entries(interactables).forEach((ee,ii)=>{ // check if element is hovered, if so its the top index and mouse is on it
var index = ee[0],
entry = ee[1];
if(entry.pressed)entry.clickend();
// if(typeof interactables[index] == 'undefined')return; // after clickend, if this was a close button it could have just been deleted
entry.setValue('pressed', false);
});
});
overlay.addEventListener('contextmenu', e=>{
e.preventDefault();
});
overlay.addEventListener('wheel', e=>{
e.preventDefault();
});
window.addEventListener('focus', e=>{
mfocus = true;
});
window.addEventListener('blur', e=>{
mfocus = false;
});
if(localStorage.getItem('background') != null){
background = JSON.parse(localStorage.getItem('background'));
}else{
localStorage.setItem('background', JSON.stringify(background, null, '\t') )
}
setInterval(()=>{
if(JSON.parse(localStorage.getItem('background'))!= background){
localStorage.setItem('background', JSON.stringify(background));
}
if(mCanvas.getAttribute('width') != msize.w){
mCanvas.setAttribute('width', msize.w);
mCanvas.style.width = msize.w + 'px';
mCanvas.style.height = msize.h + 'px';
overlay.setAttribute('width', msize.w);
overlay.style.width = msize.w + 'px';
overlay.style.height = msize.h + 'px';
}
if(mCanvas.getAttribute('height') != msize.h){
mCanvas.setAttribute('height', msize.h);
}
try{
wallpaperBusiness();
}catch(err){
mctx.drawImageURL('tango/wallpapers/a.png', 0, 0, msize.w, msize.h);
}
if(window.innerWidth < msize.w || window.innerHeight < msize.h){ // window is smaller than the current resolution
mctx.fillStyle = '#fff';
mctx.fillRect(0, 0, msize.w, msize.h);
// center text
mctx.textAlign = 'center';
mctx.fillStyle = '#000';
mctx.font = '14px Open Sans';
mctx.fillText(`Your browser window is smaller than the minimum size of ${msize.w}x${msize.h}! (the window is ${window.innerWidth}x${window.innerHeight})`, window.innerWidth / 2, window.innerHeight / 2 - 14);
mctx.textAlign = 'start';
}else{
renq.forEach((e,i)=>{
e(); // run all render operations in order
});
highRenq.forEach((e,i)=>{ // desktop and stuff
e();
});
}
above_high_renq.forEach((e,i)=>{ // super high stuff like above display level
e();
});
// RENDER CURSOR OR SET IT AFTER GOING THROUGH THE RENQ
if(!mfocus){
mctx.drawImageURL('tango/cursor/'+cursor.img+'.cur', cursor.x - 3, cursor.y - 4, 32, 32);
overlay.style.cursor = 'none';
}else{
overlay.style.cursor = 'url("tango/cursor/'+cursor.img+'.cur"), none';
}
},1000/60);