mirror of
https://github.com/QuiteAFancyEmerald/Holy-Unblocker.git
synced 2025-05-14 12:20:02 -04:00
569 lines
14 KiB
JavaScript
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);
|