diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0baf83e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:system", + "python-envs.pythonProjects": [] +} \ No newline at end of file diff --git a/2/scratch.html b/2/scratch.html new file mode 100644 index 0000000..e69de29 diff --git a/BarnyWarp Icon (Sturdy).svg b/BarnyWarp Icon (Sturdy).svg new file mode 100644 index 0000000..63e129f --- /dev/null +++ b/BarnyWarp Icon (Sturdy).svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/BarnyWarp Icon.svg b/BarnyWarp Icon.svg new file mode 100644 index 0000000..aa31fb2 --- /dev/null +++ b/BarnyWarp Icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Core/barny.js b/Core/barny.js new file mode 100644 index 0000000..acd89c8 --- /dev/null +++ b/Core/barny.js @@ -0,0 +1,1385 @@ +import "./Core/settings.py"; + +Object.extend(Squeak, { + vmPath: "/", + platformSubtype: "Browser", + osVersion: navigator.userAgent, // might want to parse + windowSystem: "HTML", +}); + +// UI namespace +window.SqueakJS = {}; + +////////////////////////////////////////////////////////////////////////////// +// display & event setup +////////////////////////////////////////////////////////////////////////////// + +function setupFullscreen(display, canvas, options) { + // Fullscreen can only be enabled in an event handler. So we check the + // fullscreen flag on every mouse down/up and keyboard event. + var box = canvas.parentElement, + fullscreenEvent = "fullscreenchange", + fullscreenElement = "fullscreenElement", + fullscreenEnabled = "fullscreenEnabled"; + + if (!box.requestFullscreen) { + [ // Fullscreen support is still very browser-dependent + {req: box.webkitRequestFullscreen, exit: document.webkitExitFullscreen, + evt: "webkitfullscreenchange", elem: "webkitFullscreenElement", enable: "webkitFullscreenEnabled"}, + {req: box.mozRequestFullScreen, exit: document.mozCancelFullScreen, + evt: "mozfullscreenchange", elem: "mozFullScreenElement", enable: "mozFullScreenEnabled"}, + {req: box.msRequestFullscreen, exit: document.msExitFullscreen, + evt: "MSFullscreenChange", elem: "msFullscreenElement", enable: "msFullscreenEnabled"}, + ].forEach(function(browser) { + if (browser.req) { + box.requestFullscreen = browser.req; + document.exitFullscreen = browser.exit; + fullscreenEvent = browser.evt; + fullscreenElement = browser.elem; + fullscreenEnabled = browser.enable; + } + }); + } + + // If the user canceled fullscreen, turn off the fullscreen flag so + // we don't try to enable it again in the next event + function fullscreenChange(fullscreen) { + display.fullscreen = fullscreen; + var fullwindow = fullscreen || options.fullscreen; + box.style.background = fullwindow ? 'black' : ''; + box.style.border = fullwindow ? 'none' : ''; + box.style.borderRadius = fullwindow ? '0px' : ''; + setTimeout(onresize, 0); + } + + var checkFullscreen; + + if (box.requestFullscreen) { + document.addEventListener(fullscreenEvent, function(){fullscreenChange(box == document[fullscreenElement]);}); + checkFullscreen = function() { + if (document[fullscreenEnabled] && (box == document[fullscreenElement]) != display.fullscreen) { + if (display.fullscreen) box.requestFullscreen(); + else document.exitFullscreen(); + } + }; + } else { + var isFullscreen = false; + checkFullscreen = function() { + if (isFullscreen != display.fullscreen) { + isFullscreen = display.fullscreen; + fullscreenChange(isFullscreen); + } + }; + } + + return checkFullscreen; +} + +function recordModifiers(evt, display) { + var shiftPressed = evt.shiftKey, + ctrlPressed = evt.ctrlKey && !evt.altKey, + cmdPressed = (display.isMac ? evt.metaKey : evt.altKey && !evt.ctrlKey) + || display.cmdButtonTouched, + modifiers = + (shiftPressed ? Squeak.Keyboard_Shift : 0) + + (ctrlPressed ? Squeak.Keyboard_Ctrl : 0) + + (cmdPressed ? Squeak.Keyboard_Cmd : 0); + display.buttons = (display.buttons & ~Squeak.Keyboard_All) | modifiers; + return modifiers; +} + +var canUseMouseOffset = null; + +function updateMousePos(evt, canvas, display) { + if (canUseMouseOffset === null) { + // Per https://caniuse.com/mdn-api_mouseevent_offsetx, essentially all *current* + // browsers support `offsetX`/`offsetY`, but it does little harm to fall back to the + // older `layerX`/`layerY` for now. + canUseMouseOffset = 'offsetX' in evt; + } + var evtX = canUseMouseOffset ? evt.offsetX : evt.layerX, + evtY = canUseMouseOffset ? evt.offsetY : evt.layerY; + if (display.cursorCanvas) { + display.cursorCanvas.style.left = (evtX + canvas.offsetLeft + display.cursorOffsetX) + "px"; + display.cursorCanvas.style.top = (evtY + canvas.offsetTop + display.cursorOffsetY) + "px"; + } + var x = (evtX * canvas.width / canvas.offsetWidth) | 0, + y = (evtY * canvas.height / canvas.offsetHeight) | 0, + w = display.width || canvas.width, + h = display.height || canvas.height; + // clamp to display size + display.mouseX = Math.max(0, Math.min(w, x)); + display.mouseY = Math.max(0, Math.min(h, y)); +} + +function recordMouseEvent(what, evt, canvas, display, options) { + updateMousePos(evt, canvas, display); + if (!display.vm) return; + var buttons = display.buttons & Squeak.Mouse_All; + switch (what) { + case 'mousedown': + switch (evt.button || 0) { + case 0: buttons = Squeak.Mouse_Red; break; // left + case 1: buttons = Squeak.Mouse_Yellow; break; // middle + case 2: buttons = Squeak.Mouse_Blue; break; // right + } + if (buttons === Squeak.Mouse_Red && (evt.altKey || evt.metaKey) || display.cmdButtonTouched) + buttons = Squeak.Mouse_Yellow; // emulate middle-click + if (options.swapButtons) + if (buttons == Squeak.Mouse_Yellow) buttons = Squeak.Mouse_Blue; + else if (buttons == Squeak.Mouse_Blue) buttons = Squeak.Mouse_Yellow; + break; + case 'mousemove': + break; // nothing more to do + case 'mouseup': + buttons = 0; + break; + } + display.buttons = buttons | recordModifiers(evt, display); + if (display.eventQueue) { + display.eventQueue.push([ + Squeak.EventTypeMouse, + evt.timeStamp, // converted to Squeak time in makeSqueakEvent() + display.mouseX, + display.mouseY, + display.buttons & Squeak.Mouse_All, + display.buttons >> 3, + ]); + if (display.signalInputEvent) + display.signalInputEvent(); + } + display.idle = 0; + if (what === 'mouseup') display.runFor(100, what); // process copy/paste or fullscreen flag change + else display.runNow(what); // don't wait for timeout to run +} + +function recordWheelEvent(evt, display) { + if (!display.vm) return; + if (!display.eventQueue || !display.vm.image.isSpur) { + // for old images, queue wheel events as ctrl+up/down + fakeCmdOrCtrlKey(evt.deltaY > 0 ? 31 : 30, evt.timeStamp, display); + return; + // TODO: use or set VM parameter 48 (see vmParameterAt) + } + var squeakEvt = [ + Squeak.EventTypeMouseWheel, + evt.timeStamp, // converted to Squeak time in makeSqueakEvent() + evt.deltaX, + -evt.deltaY, + display.buttons & Squeak.Mouse_All, + display.buttons >> 3, + ]; + display.eventQueue.push(squeakEvt); + if (display.signalInputEvent) + display.signalInputEvent(); + display.idle = 0; + if (display.runNow) display.runNow('wheel'); // don't wait for timeout to run +} + +// Squeak traditional keycodes are MacRoman +var MacRomanToUnicode = [ + 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, + 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, + 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, + 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, + 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, + 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, + 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, + 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8, + 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, + 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, + 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, + 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02, + 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, + 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, + 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, + 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7, +]; +var UnicodeToMacRoman = {}; +for (var i = 0; i < MacRomanToUnicode.length; i++) + UnicodeToMacRoman[MacRomanToUnicode[i]] = i + 128; + +function recordKeyboardEvent(unicode, timestamp, display) { + if (!display.vm) return; + var macCode = UnicodeToMacRoman[unicode] || (unicode < 128 ? unicode : 0); + var modifiersAndKey = (display.buttons >> 3) << 8 | macCode; + if (display.eventQueue) { + display.eventQueue.push([ + Squeak.EventTypeKeyboard, + timestamp, // converted to Squeak time in makeSqueakEvent() + macCode, // MacRoman + Squeak.EventKeyChar, + display.buttons >> 3, + unicode, // Unicode + ]); + if (display.signalInputEvent) + display.signalInputEvent(); + // There are some old images that use both event-based + // and polling primitives. To make those work, keep the + // last key event + display.keys[0] = modifiersAndKey; + } else if (modifiersAndKey === display.vm.interruptKeycode) { + display.vm.interruptPending = true; + } else { + // no event queue, queue keys the old-fashioned way + display.keys.push(modifiersAndKey); + } + display.idle = 0; + if (display.runNow) display.runNow('keyboard'); // don't wait for timeout to run +} + +function recordDragDropEvent(type, evt, canvas, display) { + if (!display.vm || !display.eventQueue) return; + updateMousePos(evt, canvas, display); + display.eventQueue.push([ + Squeak.EventTypeDragDropFiles, + evt.timeStamp, // converted to Squeak time in makeSqueakEvent() + type, + display.mouseX, + display.mouseY, + display.buttons >> 3, + display.droppedFiles.length, + ]); + if (display.signalInputEvent) + display.signalInputEvent(); + display.idle = 0; + if (display.runNow) display.runNow('drag-drop'); // don't wait for timeout to run +} + +function fakeCmdOrCtrlKey(key, timestamp, display) { + // set both Cmd and Ctrl bit, because we don't know what the image wants + display.buttons &= ~Squeak.Keyboard_All; // remove all modifiers + display.buttons |= Squeak.Keyboard_Cmd | Squeak.Keyboard_Ctrl; + display.keys = []; // flush other keys + recordKeyboardEvent(key, timestamp, display); +} + +function makeSqueakEvent(evt, sqEvtBuf, sqTimeOffset) { + sqEvtBuf[0] = evt[0]; + sqEvtBuf[1] = (evt[1] - sqTimeOffset) & Squeak.MillisecondClockMask; + for (var i = 2; i < evt.length; i++) + sqEvtBuf[i] = evt[i]; +} + +function createSqueakDisplay(canvas, options) { + options = options || {}; + if (options.fullscreen) { + document.body.style.margin = 0; + document.body.style.backgroundColor = 'black'; + canvas.style.border = 'none'; + canvas.style.borderRadius = '0px'; + document.ontouchmove = function(evt) { evt.preventDefault(); }; + } + var display = { + context: canvas.getContext("2d"), + fullscreen: false, + width: 0, // if 0, VM uses canvas.width + height: 0, // if 0, VM uses canvas.height + scale: 1, // VM will use window.devicePixelRatio if highdpi is enabled, also changes when touch-zooming + highdpi: options.highdpi, // TODO: use or set VM parameter 48 (see vmParameterAt) + mouseX: 0, + mouseY: 0, + buttons: 0, + keys: [], + cmdButtonTouched: null, // touchscreen button pressed (touch ID) + eventQueue: null, // only used if image uses event primitives + clipboardString: '', + clipboardStringChanged: false, + handlingEvent: '', // set to 'mouse' or 'keyboard' while handling an event + cursorCanvas: options.cursor !== false && document.getElementById("sqCursor") || document.createElement("canvas"), + cursorOffsetX: 0, + cursorOffsetY: 0, + droppedFiles: [], + signalInputEvent: null, // function set by VM + changedCallback: null, // invoked when display size/scale changes + // additional functions added below + }; + if (options.pixelated) { + canvas.classList.add("pixelated"); + display.cursorCanvas && display.cursorCanvas.classList.add("pixelated"); + } + + display.reset = function() { + display.eventQueue = null; + display.signalInputEvent = null; + display.lastTick = 0; + display.getNextEvent = function(firstEvtBuf, firstOffset) { + // might be called from VM to get queued event + display.eventQueue = []; // create queue on first call + display.eventQueue.push = function(evt) { + display.eventQueue.offset = Date.now() - evt[1]; // get epoch from first event + delete display.eventQueue.push; // use original push from now on + display.eventQueue.push(evt); + }; + display.getNextEvent = function(evtBuf, timeOffset) { + var evt = display.eventQueue.shift(); + if (evt) makeSqueakEvent(evt, evtBuf, timeOffset - display.eventQueue.offset); + else evtBuf[0] = Squeak.EventTypeNone; + }; + display.getNextEvent(firstEvtBuf, firstOffset); + }; + }; + display.reset(); + + var checkFullscreen = setupFullscreen(display, canvas, options); + display.fullscreenRequest = function(fullscreen, thenDo) { + // called from primitive to change fullscreen mode + if (display.fullscreen != fullscreen) { + display.fullscreen = fullscreen; + display.resizeTodo = thenDo; // called after resizing + display.resizeTodoTimeout = setTimeout(display.resizeDone, 1000); + checkFullscreen(); + } else thenDo(); + }; + display.resizeDone = function() { + clearTimeout(display.resizeTodoTimeout); + var todo = display.resizeTodo; + if (todo) { + display.resizeTodo = null; + todo(); + } + }; + display.clear = function() { + canvas.width = canvas.width; + }; + display.setTitle = function(title) { + document.title = title; + }; + display.showBanner = function(msg, style) { + style = style || display.context.canvas.style || {}; + var ctx = display.context; + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = style.color || "#F90"; + ctx.font = style.font || "bold 48px sans-serif"; + if (!style.font && ctx.measureText(msg).width > canvas.width) + ctx.font = "bold 24px sans-serif"; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText(msg, canvas.width / 2, canvas.height / 2); + }; + display.showProgress = function(value, style) { + style = style || display.context.canvas.style || {}; + var ctx = display.context, + w = (canvas.width / 3) | 0, + h = 24, + x = canvas.width * 0.5 - w / 2, + y = canvas.height * 0.5 + 2 * h; + ctx.fillStyle = style.background || "#000"; + ctx.fillRect(x, y, w, h); + ctx.lineWidth = 2; + ctx.strokeStyle = style.stroke || "#F90"; + ctx.strokeRect(x, y, w, h); + ctx.fillStyle = style.fill || "#F90"; + ctx.fillRect(x, y, w * value, h); + }; + display.executeClipboardPasteKey = function(text, timestamp) { + if (!display.vm) return true; + try { + display.clipboardString = text; + // simulate paste event for Squeak + fakeCmdOrCtrlKey('v'.charCodeAt(0), timestamp, display); + } catch(err) { + console.error("paste error " + err); + } + }; + display.executeClipboardCopyKey = function(key, timestamp) { + if (!display.vm) return true; + // simulate copy event for Squeak so it places its text in clipboard + display.clipboardStringChanged = false; + fakeCmdOrCtrlKey((key || 'c').charCodeAt(0), timestamp, display); + var start = Date.now(); + // now interpret until Squeak has copied to the clipboard + while (!display.clipboardStringChanged && Date.now() - start < 500) + display.vm.interpret(20); + if (!display.clipboardStringChanged) return; + // got it, now copy to the system clipboard + try { + return display.clipboardString; + } catch(err) { + console.error("copy error " + err); + } + }; + canvas.onmousedown = function(evt) { + checkFullscreen(); + recordMouseEvent('mousedown', evt, canvas, display, options); + evt.preventDefault(); + return false; + }; + canvas.onmouseup = function(evt) { + recordMouseEvent('mouseup', evt, canvas, display, options); + checkFullscreen(); + evt.preventDefault(); + }; + canvas.onmousemove = function(evt) { + recordMouseEvent('mousemove', evt, canvas, display, options); + evt.preventDefault(); + }; + canvas.onwheel = function(evt) { + recordWheelEvent(evt, display); + evt.preventDefault(); + }; + canvas.oncontextmenu = function() { + return false; + }; + // touch event handling + var touch = { + state: 'idle', + button: 0, + x: 0, + y: 0, + dist: 0, + down: {}, + }; + function touchToMouse(evt) { + if (evt.touches.length) { + // average all touch positions + // but ignore the cmd button touch + var x = 0, y = 0, n = 0; + for (var i = 0; i < evt.touches.length; i++) { + if (evt.touches[i].identifier === display.cmdButtonTouched) continue; + x += evt.touches[i].pageX; + y += evt.touches[i].pageY; + n++; + } + if (n > 0) { + touch.x = x / n; + touch.y = y / n; + } + } + return { + timeStamp: evt.timeStamp, + button: touch.button, + offsetX: touch.x - canvas.offsetLeft, + offsetY: touch.y - canvas.offsetTop, + }; + } + function dd(ax, ay, bx, by) {var x = ax - bx, y = ay - by; return Math.sqrt(x*x + y*y);} + function dist(a, b) {return dd(a.pageX, a.pageY, b.pageX, b.pageY);} + function dent(n, l, t, u) { return n < l ? n + t - l : n > u ? n + t - u : t; } + function adjustCanvas(l, t, w, h) { + var cursorCanvas = display.cursorCanvas, + cssScale = w / canvas.width, + ratio = display.highdpi ? window.devicePixelRatio : 1, + pixelScale = cssScale * ratio; + canvas.style.left = (l|0) + "px"; + canvas.style.top = (t|0) + "px"; + canvas.style.width = (w|0) + "px"; + canvas.style.height = (h|0) + "px"; + if (cursorCanvas) { + cursorCanvas.style.left = (l + display.cursorOffsetX + display.mouseX * cssScale|0) + "px"; + cursorCanvas.style.top = (t + display.cursorOffsetY + display.mouseY * cssScale|0) + "px"; + cursorCanvas.style.width = (cursorCanvas.width * pixelScale|0) + "px"; + cursorCanvas.style.height = (cursorCanvas.height * pixelScale|0) + "px"; + } + // if pixelation is not forced, turn it on for integer scales + if (!options.pixelated) { + if (pixelScale % 1 === 0 || pixelScale > 5) { + canvas.classList.add("pixelated"); + cursorCanvas && cursorCanvas.classList.add("pixelated"); + } else { + canvas.classList.remove("pixelated"); + cursorCanvas && display.cursorCanvas.classList.remove("pixelated"); + } + } + display.css = { + left: l, + top: t, + width: w, + height: h, + scale: cssScale, + pixelScale: pixelScale, + ratio: ratio, + }; + if (display.changedCallback) display.changedCallback(); + return cssScale; + } + // zooming/panning with two fingers + var maxZoom = 5; + function zoomStart(evt) { + touch.dist = dist(evt.touches[0], evt.touches[1]); + touch.down.x = touch.x; + touch.down.y = touch.y; + touch.down.dist = touch.dist; + touch.down.left = canvas.offsetLeft; + touch.down.top = canvas.offsetTop; + touch.down.width = canvas.offsetWidth; + touch.down.height = canvas.offsetHeight; + // store original canvas bounds + if (!touch.orig) touch.orig = { + left: touch.down.left, + top: touch.down.top, + right: touch.down.left + touch.down.width, + bottom: touch.down.top + touch.down.height, + width: touch.down.width, + height: touch.down.height, + }; + } + function zoomMove(evt) { + if (evt.touches.length < 2) return; + touch.dist = dist(evt.touches[0], evt.touches[1]); + var minScale = touch.orig.width / touch.down.width, + //nowScale = dent(touch.dist / touch.down.dist, 0.8, 1, 1.5), + nowScale = touch.dist / touch.down.dist, + scale = Math.min(Math.max(nowScale, minScale * 0.95), minScale * maxZoom), + w = touch.down.width * scale, + h = touch.orig.height * w / touch.orig.width, + l = touch.down.left - (touch.down.x - touch.down.left) * (scale - 1) + (touch.x - touch.down.x), + t = touch.down.top - (touch.down.y - touch.down.top) * (scale - 1) + (touch.y - touch.down.y); + // allow to rubber-band by 20px for feedback + l = Math.max(Math.min(l, touch.orig.left + 20), touch.orig.right - w - 20); + t = Math.max(Math.min(t, touch.orig.top + 20), touch.orig.bottom - h - 20); + adjustCanvas(l, t, w, h); + } + function zoomEnd(evt) { + var l = canvas.offsetLeft, + t = canvas.offsetTop, + w = canvas.offsetWidth, + h = canvas.offsetHeight; + w = Math.min(Math.max(w, touch.orig.width), touch.orig.width * maxZoom); + h = touch.orig.height * w / touch.orig.width; + l = Math.max(Math.min(l, touch.orig.left), touch.orig.right - w); + t = Math.max(Math.min(t, touch.orig.top), touch.orig.bottom - h); + var scale = adjustCanvas(l, t, w, h); + if ((scale - display.scale) < 0.0001) { + touch.orig = null; + onresize(); + } + } + // State machine to distinguish between 1st/2nd mouse button and zoom/pan: + // * if moved, or no 2nd finger within 100ms of 1st down, start mousing + // * if fingers moved significantly within 200ms of 2nd down, start zooming + // * if touch ended within this time, generate click (down+up) + // * otherwise, start mousing with 2nd button + // * also, ignore finger on cmd button + // When mousing, always generate a move event before down event so that + // mouseover eventhandlers in image work better + canvas.ontouchstart = function(evt) { + evt.preventDefault(); + var e = touchToMouse(evt); + for (var i = 0; i < evt.changedTouches.length; i++) { + if (evt.changedTouches[i].identifier === display.cmdButtonTouched) continue; + switch (touch.state) { + case 'idle': + touch.state = 'got1stFinger'; + touch.first = e; + setTimeout(function(){ + if (touch.state !== 'got1stFinger') return; + touch.state = 'mousing'; + touch.button = e.button = 0; + recordMouseEvent('mousemove', e, canvas, display, options); + recordMouseEvent('mousedown', e, canvas, display, options); + }, 100); + break; + case 'got1stFinger': + touch.state = 'got2ndFinger'; + zoomStart(evt); + setTimeout(function(){ + if (touch.state !== 'got2ndFinger') return; + var didMove = Math.abs(touch.down.dist - touch.dist) > 10 || + dd(touch.down.x, touch.down.y, touch.x, touch.y) > 10; + if (didMove) { + touch.state = 'zooming'; + } else { + touch.state = 'mousing'; + touch.button = e.button = 2; + recordMouseEvent('mousemove', e, canvas, display, options); + recordMouseEvent('mousedown', e, canvas, display, options); + } + }, 200); + break; + } + } + }; + canvas.ontouchmove = function(evt) { + evt.preventDefault(); + var e = touchToMouse(evt); + switch (touch.state) { + case 'got1stFinger': + touch.state = 'mousing'; + touch.button = e.button = 0; + recordMouseEvent('mousemove', e, canvas, display, options); + recordMouseEvent('mousedown', e, canvas, display, options); + break; + case 'mousing': + recordMouseEvent('mousemove', e, canvas, display, options); + break; + case 'got2ndFinger': + if (evt.touches.length > 1) + touch.dist = dist(evt.touches[0], evt.touches[1]); + break; + case 'zooming': + zoomMove(evt); + break; + } + }; + canvas.ontouchend = function(evt) { + evt.preventDefault(); + checkFullscreen(); + var e = touchToMouse(evt); + var n = evt.touches.length; + if (Array.from(evt.touches).findIndex(t => t.identifier === display.cmdButtonTouched) >= 0) n--; + for (var i = 0; i < evt.changedTouches.length; i++) { + if (evt.changedTouches[i].identifier === display.cmdButtonTouched) { + continue; + } + switch (touch.state) { + case 'mousing': + if (n > 0) break; + touch.state = 'idle'; + recordMouseEvent('mouseup', e, canvas, display, options); + break; + case 'got1stFinger': + touch.state = 'idle'; + touch.button = e.button = 0; + recordMouseEvent('mousemove', e, canvas, display, options); + recordMouseEvent('mousedown', e, canvas, display, options); + recordMouseEvent('mouseup', e, canvas, display, options); + break; + case 'got2ndFinger': + touch.state = 'mousing'; + touch.button = e.button = 2; + recordMouseEvent('mousemove', e, canvas, display, options); + recordMouseEvent('mousedown', e, canvas, display, options); + break; + case 'zooming': + if (n > 0) break; + touch.state = 'idle'; + zoomEnd(evt); + break; + } + } + }; + canvas.ontouchcancel = function(evt) { + canvas.ontouchend(evt); + }; + // cursorCanvas shows Squeak cursor + if (display.cursorCanvas) { + var absolute = window.getComputedStyle(canvas).position === "absolute"; + display.cursorCanvas.style.display = "block"; + display.cursorCanvas.style.position = absolute ? "absolute": "fixed"; + display.cursorCanvas.style.cursor = "none"; + display.cursorCanvas.style.background = "transparent"; + display.cursorCanvas.style.pointerEvents = "none"; + canvas.parentElement.appendChild(display.cursorCanvas); + canvas.style.cursor = "none"; + } + // keyboard stuff + // create hidden input field to capture not only keyboard events + // but also copy/paste and input events (for dead keys) + var input = document.createElement("input"); + input.setAttribute("autocomplete", "off"); + input.setAttribute("autocorrect", "off"); + input.setAttribute("autocapitalize", "off"); + input.setAttribute("spellcheck", "false"); + input.style.position = "absolute"; + input.style.left = "-1000px"; + canvas.parentElement.appendChild(input); + // touch-keyboard buttons + if ('ontouchstart' in document) { + // button to show on-screen keyboard + var keyboardButton = document.createElement('div'); + keyboardButton.innerHTML = ''; + keyboardButton.setAttribute('style', 'position:fixed;right:0;bottom:0;background-color:rgba(128,128,128,0.5);border-radius:5px'); + canvas.parentElement.appendChild(keyboardButton); + keyboardButton.onmousedown = function(evt) { + // show on-screen keyboard + input.focus({ preventScroll: true }); + evt.preventDefault(); + } + keyboardButton.ontouchstart = keyboardButton.onmousedown; + // modifier button for CMD key + var cmdButton = document.createElement('div'); + cmdButton.innerHTML = '⌘'; + cmdButton.setAttribute('style', 'position:fixed;left:0;background-color:rgba(128,128,128,0.5);width:50px;height:50px;font-size:30px;text-align:center;vertical-align:middle;line-height:50px;border-radius:5px'); + if (window.visualViewport) { + // fix position of button when virtual keyboard is shown + const vv = window.visualViewport; + const fixPosition = () => cmdButton.style.top = `${vv.height}px`; + vv.addEventListener('resize', fixPosition); + cmdButton.style.transform = `translateY(-100%)`; + fixPosition(); + } else { + cmdButton.style.bottom = '0'; + } + canvas.parentElement.appendChild(cmdButton); + cmdButton.ontouchstart = function(evt) { + display.cmdButtonTouched = evt.changedTouches[0].identifier; + cmdButton.style.backgroundColor = 'rgba(255,255,255,0.5)'; + evt.preventDefault(); + evt.stopPropagation(); + } + cmdButton.ontouchend = function(evt) { + display.cmdButtonTouched = null; + cmdButton.style.backgroundColor = 'rgba(128,128,128,0.5)'; + evt.preventDefault(); + evt.stopPropagation(); + } + cmdButton.ontouchcancel = cmdButton.ontouchend; + } else { + // keep focus on input field + input.onblur = function() { input.focus({ preventScroll: true }); }; + input.focus({ preventScroll: true }); + } + display.isMac = navigator.userAgent.includes("Mac"); + // emulate keypress events + var deadKey = false, // true if last keydown was a dead key + deadChars = []; + input.oninput = function(evt) { + if (!display.vm) return true; + if (evt.inputType === "insertText" // regular key, or Chrome + || evt.inputType === "insertCompositionText" // Firefox, Chrome + || evt.inputType === "insertFromComposition") // Safari + { + // generate backspace to delete inserted dead chars + var hadDeadChars = deadChars.length > 0; + if (hadDeadChars) { + var oldButtons = display.buttons; + display.buttons &= ~Squeak.Keyboard_All; // remove all modifiers + for (var i = 0; i < deadChars.length; i++) { + recordKeyboardEvent(8, evt.timeStamp, display); + } + display.buttons = oldButtons; + deadChars = []; + } + // generate keyboard events for each character + // single input could be many characters, e.g. for emoji + var chars = Array.from(evt.data); // split by surrogate pairs + for (var i = 0; i < chars.length; i++) { + var unicode = chars[i].codePointAt(0); // codePointAt combines pair into unicode + recordKeyboardEvent(unicode, evt.timeStamp, display); + } + if (!hadDeadChars && evt.isComposing && evt.inputType === "insertCompositionText") { + deadChars = deadChars.concat(chars); + } + } + if (!deadChars.length) resetInput(); + }; + input.onkeydown = function(evt) { + checkFullscreen(); + if (!display.vm) return true; + deadKey = evt.key === "Dead"; + if (deadKey) return; // let browser handle dead keys + recordModifiers(evt, display); + var squeakCode = ({ + 8: 8, // Backspace + 9: 9, // Tab + 13: 13, // Return + 27: 27, // Escape + 32: 32, // Space + 33: 11, // PageUp + 34: 12, // PageDown + 35: 4, // End + 36: 1, // Home + 37: 28, // Left + 38: 30, // Up + 39: 29, // Right + 40: 31, // Down + 45: 5, // Insert + 46: 127, // Delete + })[evt.keyCode]; + if (squeakCode) { // special key pressed + recordKeyboardEvent(squeakCode, evt.timeStamp, display); + return evt.preventDefault(); + } + // copy/paste new-style + if (display.isMac ? evt.metaKey : evt.ctrlKey) { + switch (evt.key) { + case "c": + case "x": + if (!navigator.clipboard) return; // fire document.oncopy/oncut + var text = display.executeClipboardCopyKey(evt.key, evt.timeStamp); + if (typeof text === 'string') { + navigator.clipboard.writeText(text) + .catch(function(err) { console.error("display: copy error " + err.message); }); + } + return evt.preventDefault(); + case "v": + if (!navigator.clipboard) return; // fire document.onpaste + navigator.clipboard.readText() + .then(function(text) { + display.executeClipboardPasteKey(text, evt.timeStamp); + }) + .catch(function(err) { console.error("display: paste error " + err.message); }); + return evt.preventDefault(); + } + } + if (evt.key.length !== 1) return; // let browser handle other keys + if (display.buttons & (Squeak.Keyboard_Cmd | Squeak.Keyboard_Ctrl)) { + var code = evt.key.toLowerCase().charCodeAt(0); + if ((display.buttons & Squeak.Keyboard_Ctrl) && code >= 96 && code < 127) code &= 0x1F; // ctrl- + recordKeyboardEvent(code, evt.timeStamp, display); + return evt.preventDefault(); + } + }; + input.onkeyup = function(evt) { + if (!display.vm) return true; + recordModifiers(evt, display); + }; + function resetInput() { + input.value = "**"; + input.selectionStart = 1; + input.selectionEnd = 1; + } + resetInput(); + // hack to generate arrow keys when moving the cursor (e.g. via spacebar on iPhone) + // we're not getting any events for that but the cursor (selection) changes + if ('ontouchstart' in document) { + let count = 0; // count how often the interval has run after the first move + setInterval(() => { + const direction = input.selectionStart - 1; + if (direction === 0) { + count = 0; + return; + } + // move cursor once, then not for 500ms, then every 250ms + if (count === 0 || count > 2) { + const key = direction < 1 ? 28 : 29; // arrow left or right + recordKeyboardEvent(key, Date.now(), display); + } + input.selectionStart = 1; + input.selectionEnd = 1; + count++; + }, 250); + } + // more copy/paste + if (navigator.clipboard) { + // new-style copy/paste (all modern browsers) + display.readFromSystemClipboard = () => display.handlingEvent && + navigator.clipboard.readText() + .then(text => display.clipboardString = text) + .catch(err => console.error("readFromSystemClipboard " + err.message)); + display.writeToSystemClipboard = () => display.handlingEvent && + navigator.clipboard.writeText(display.clipboardString) + .then(() => display.clipboardStringChanged = false) + .catch(err => console.error("writeToSystemClipboard " + err.message)); + } else { + // old-style copy/paste + document.oncopy = function(evt, key) { + var text = display.executeClipboardCopyKey(key, evt.timeStamp); + if (typeof text === 'string') { + evt.clipboardData.setData("Text", text); + } + evt.preventDefault(); + }; + document.oncut = function(evt) { + if (!display.vm) return true; + document.oncopy(evt, 'x'); + }; + document.onpaste = function(evt) { + var text = evt.clipboardData.getData('Text'); + display.executeClipboardPasteKey(text, evt.timeStamp); + evt.preventDefault(); + }; + } + // do not use addEventListener, we want to replace any previous drop handler + function dragEventHasFiles(evt) { + for (var i = 0; i < evt.dataTransfer.types.length; i++) + if (evt.dataTransfer.types[i] == 'Files') return true; + return false; + } + document.ondragover = function(evt) { + evt.preventDefault(); + if (!dragEventHasFiles(evt)) { + evt.dataTransfer.dropEffect = 'none'; + } else { + evt.dataTransfer.dropEffect = 'copy'; + recordDragDropEvent(Squeak.EventDragMove, evt, canvas, display); + } + }; + document.ondragenter = function(evt) { + if (!dragEventHasFiles(evt)) return; + recordDragDropEvent(Squeak.EventDragEnter, evt, canvas, display); + }; + document.ondragleave = function(evt) { + if (!dragEventHasFiles(evt)) return; + recordDragDropEvent(Squeak.EventDragLeave, evt, canvas, display); + }; + document.ondrop = function(evt) { + evt.preventDefault(); + if (!dragEventHasFiles(evt)) return false; + var files = [].slice.call(evt.dataTransfer.files), + loaded = [], + image, imageName = null; + display.droppedFiles = []; + files.forEach(function(f) { + var path = options.root + f.name; + display.droppedFiles.push(path); + var reader = new FileReader(); + reader.onload = function () { + var buffer = this.result; + Squeak.filePut(path, buffer); + loaded.push(path); + if (!image && /.*image$/.test(path) && (!display.vm || confirm("Run " + f.name + " now?\n(cancel to use as file)"))) { + image = buffer; + imageName = path; + } + if (loaded.length == files.length) { + if (image) { + if (display.vm) { + display.quitFlag = true; + options.onQuit = function(vm, display, options) { + options.onQuit = null; + SqueakJS.appName = imageName.replace(/.*\//,'').replace(/\.image$/,''); + SqueakJS.runImage(image, imageName, display, options); + } + } else { + SqueakJS.appName = imageName.replace(/.*\//,'').replace(/\.image$/,''); + SqueakJS.runImage(image, imageName, display, options); + } + } else { + recordDragDropEvent(Squeak.EventDragDrop, evt, canvas, display); + } + } + }; + reader.readAsArrayBuffer(f); + }); + return false; + }; + + var debounceTimeout; + function onresize() { + if (touch.orig) return; // manually resized + // call resizeDone only if window size didn't change for 300ms + var debounceWidth = window.innerWidth, + debounceHeight = window.innerHeight; + clearTimeout(debounceTimeout); + debounceTimeout = setTimeout(function() { + if (debounceWidth == window.innerWidth && debounceHeight == window.innerHeight) + display.resizeDone(); + else + onresize(); + }, 300); + // CSS won't let us do what we want so we will layout the canvas ourselves. + var x = 0, + y = 0, + w = window.innerWidth, + h = window.innerHeight, + paddingX = 0, // padding outside canvas + paddingY = 0; + // above are the default values for laying out the canvas + if (!options.fixedWidth) { // set canvas resolution + if (!options.minWidth) options.minWidth = 700; + if (!options.minHeight) options.minHeight = 700; + var defaultScale = display.highdpi ? window.devicePixelRatio : 1, + scaleW = w < options.minWidth ? options.minWidth / w : defaultScale, + scaleH = h < options.minHeight ? options.minHeight / h : defaultScale, + scale = Math.max(scaleW, scaleH); + display.width = Math.floor(w * scale); + display.height = Math.floor(h * scale); + display.scale = w / display.width; + } else { // fixed resolution and aspect ratio + display.width = options.fixedWidth; + display.height = options.fixedHeight; + var wantRatio = display.width / display.height, + haveRatio = w / h; + if (haveRatio > wantRatio) { + paddingX = w - Math.floor(h * wantRatio); + } else { + paddingY = h - Math.floor(w / wantRatio); + } + display.scale = (w - paddingX) / display.width; + } + // set resolution + if (canvas.width != display.width || canvas.height != display.height) { + var preserveScreen = options.fixedWidth || !display.resizeTodo, // preserve unless changing fullscreen + imgData = preserveScreen && display.context.getImageData(0, 0, canvas.width, canvas.height); + canvas.width = display.width; + canvas.height = display.height; + if (imgData) display.context.putImageData(imgData, 0, 0); + } + // set canvas and cursor canvas size, position, pixelation + adjustCanvas( + x + Math.floor(paddingX / 2), + y + Math.floor(paddingY / 2), + w - paddingX, + h - paddingY + ); + }; + + if (!options.embedded) { + onresize(); + window.onresize = onresize; + } + + return display; +} + +function setupSpinner(vm, options) { + var spinner = options.spinner; + if (!spinner) return null; + spinner.onmousedown = function(evt) { + if (confirm(SqueakJS.appName + " is busy. Interrupt?")) + vm.interruptPending = true; + }; + return spinner.style; +} + +var spinnerAngle = 0, + becameBusy = 0; +function updateSpinner(spinner, idleMS, vm, display) { + var busy = idleMS === 0, + animating = vm.lastTick - display.lastTick < 500; + if (!busy || animating) { + spinner.display = "none"; + becameBusy = 0; + } else { + if (becameBusy === 0) { + becameBusy = vm.lastTick; + } else if (vm.lastTick - becameBusy > 1000) { + spinner.display = "block"; + spinnerAngle = (spinnerAngle + 30) % 360; + spinner.webkitTransform = spinner.transform = "rotate(" + spinnerAngle + "deg)"; + } + } +} + +////////////////////////////////////////////////////////////////////////////// +// main loop +////////////////////////////////////////////////////////////////////////////// + +var loop; // holds timeout for main loop + +SqueakJS.runImage = function(buffer, name, display, options) { + window.onbeforeunload = function(evt) { + var msg = SqueakJS.appName + " is still running"; + evt.returnValue = msg; + return msg; + }; + window.clearTimeout(loop); + display.reset(); + display.clear(); + display.showBanner("Loading " + SqueakJS.appName); + display.showProgress(0); + window.setTimeout(function readImageAsync() { + var image = new Squeak.Image(name); + image.readFromBuffer(buffer, function startRunning() { + display.quitFlag = false; + var vm = new Squeak.Interpreter(image, display, options); + SqueakJS.vm = vm; + Squeak.Settings["squeakImageName"] = name; + display.clear(); + display.showBanner("Starting " + SqueakJS.appName); + var spinner = setupSpinner(vm, options); + function run() { + try { + if (display.quitFlag) SqueakJS.onQuit(vm, display, options); + else vm.interpret(50, function runAgain(ms) { + if (ms == "sleep") ms = 200; + if (spinner) updateSpinner(spinner, ms, vm, display); + loop = window.setTimeout(run, ms); + }); + } catch(error) { + console.error(error); + alert(error); + } + } + display.runNow = function(event) { + window.clearTimeout(loop); + display.handlingEvent = event; + run(); + display.handlingEvent = ''; + }; + display.runFor = function(milliseconds, event) { + var stoptime = Date.now() + milliseconds; + do { + if (display.quitFlag) return; + display.runNow(event); + } while (Date.now() < stoptime); + }; + if (options.onStart) options.onStart(vm, display, options); + run(); + }, + function readProgress(value) {display.showProgress(value);}); + }, 0); +}; + +function processOptions(options) { + var search = (location.hash || location.search).slice(1), + args = search && search.split("&"); + if (args) for (var i = 0; i < args.length; i++) { + var keyAndVal = args[i].split("="), + key = keyAndVal[0], + val = true; + if (keyAndVal.length > 1) { + val = decodeURIComponent(keyAndVal.slice(1).join("=")); + if (val.match(/^(true|false|null|[0-9"[{].*)$/)) + try { val = JSON.parse(val); } catch(e) { + if (val[0] === "[") val = val.slice(1,-1).split(","); // handle string arrays + // if not JSON use string itself + } + } + options[key] = val; + } + var root = Squeak.splitFilePath(options.root || "/").fullname; + Squeak.dirCreate(root, true); + if (!/\/$/.test(root)) root += "/"; + options.root = root; + if (options.w) options.fixedWidth = options.w; + if (options.h) options.fixedHeight = options.h; + if (options.fixedWidth && !options.fixedHeight) options.fixedHeight = options.fixedWidth * 3 / 4 | 0; + if (options.fixedHeight && !options.fixedWidth) options.fixedWidth = options.fixedHeight * 4 / 3 | 0; + if (options.fixedWidth && options.fixedHeight) options.fullscreen = true; + SqueakJS.options = options; +} + +function fetchTemplates(options) { + if (options.templates) { + if (options.templates.constructor === Array) { + var templates = {}; + options.templates.forEach(function(path){ templates[path] = path; }); + options.templates = templates; + } + for (var path in options.templates) { + var dir = path[0] == "/" ? path : options.root + path, + baseUrl = new URL(options.url, document.baseURI).href.split(/[?#]/)[0], + url = Squeak.splitUrl(options.templates[path], baseUrl).full; + if (url.endsWith("/")) url = url.slice(0,-1); + if (url.endsWith("/.")) url = url.slice(0,-2); + Squeak.fetchTemplateDir(dir, url); + } + } +} + +function processFile(file, display, options, thenDo) { + Squeak.filePut(options.root + file.name, file.data, function() { + console.log("Stored " + options.root + file.name); + if (file.zip) { + processZip(file, display, options, thenDo); + } else { + thenDo(); + } + }); +} + +function processZip(file, display, options, thenDo) { + display.showBanner("Analyzing " + file.name); + JSZip.loadAsync(file.data, { createFolders: true }).then(function(zip) { + var todo = []; + zip.forEach(function(filename, meta) { + if (filename.startsWith("__MACOSX/") || filename.endsWith(".DS_Store")) return; // skip macOS metadata + if (meta.dir) { + filename = filename.replace(/\/$/, ""); + Squeak.dirCreate(options.root + filename, true); + return; + } + if (!options.image.name && filename.match(/\.image$/)) + options.image.name = filename; + if (options.forceDownload || !Squeak.fileExists(options.root + filename)) { + todo.push(filename); + } else if (options.image.name === filename) { + // image exists, need to fetch it from storage + var _thenDo = thenDo; + thenDo = function() { + Squeak.fileGet(options.root + filename, function(data) { + options.image.data = data; + return _thenDo(); + }, function onError() { + Squeak.fileDelete(options.root + file.name); + return processZip(file, display, options, _thenDo); + }); + } + } + }); + if (todo.length === 0) return thenDo(); + var done = 0; + display.showBanner("Unzipping " + file.name); + display.showProgress(0); + todo.forEach(function(filename){ + console.log("Inflating " + file.name + ": " + filename); + function progress(x) { display.showProgress((x.percent / 100 + done) / todo.length); } + zip.file(filename).async("arraybuffer", progress).then(function(buffer){ + console.log("Expanded size of " + filename + ": " + buffer.byteLength + " bytes"); + var unzipped = {}; + if (options.image.name === filename) + unzipped = options.image; + unzipped.name = filename; + unzipped.data = buffer; + processFile(unzipped, display, options, function() { + if (++done === todo.length) thenDo(); + }); + }); + }); + }); +} + +function checkExisting(file, display, options, ifExists, ifNotExists) { + if (!Squeak.fileExists(options.root + file.name)) + return ifNotExists(); + if (file.image || file.zip) { + // if it's the image or a zip, load from file storage + Squeak.fileGet(options.root + file.name, function(data) { + file.data = data; + if (file.zip) processZip(file, display, options, ifExists); + else ifExists(); + }, function onError() { + // if error, download it + Squeak.fileDelete(options.root + file.name); + return ifNotExists(); + }); + } else { + // for all other files assume they're okay + ifExists(); + } +} + +function downloadFile(file, display, options, thenDo) { + display.showBanner("Downloading " + file.name); + var rq = new XMLHttpRequest(), + proxy = options.proxy || ""; + rq.open('GET', proxy + file.url); + if (options.ajax) rq.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + rq.responseType = 'arraybuffer'; + rq.onprogress = function(e) { + if (e.lengthComputable) display.showProgress(e.loaded / e.total); + }; + rq.onload = function(e) { + if (this.status == 200) { + file.data = this.response; + processFile(file, display, options, thenDo); + } + else this.onerror(this.statusText); + }; + rq.onerror = function(e) { + if (options.proxy) { + console.error(Squeak.bytesAsString(new Uint8Array(this.response))); + return alert("Failed to download:\n" + file.url); + } + var proxy = Squeak.defaultCORSProxy, + retry = new XMLHttpRequest(); + console.warn('Retrying with CORS proxy: ' + proxy + file.url); + retry.open('GET', proxy + file.url); + if (options.ajax) retry.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + retry.responseType = rq.responseType; + retry.onprogress = rq.onprogress; + retry.onload = rq.onload; + retry.onerror = function() { + console.error(Squeak.bytesAsString(new Uint8Array(this.response))); + alert("Failed to download:\n" + file.url)}; + retry.send(); + }; + rq.send(); +} + +function fetchFiles(files, display, options, thenDo) { + // check if files exist locally and download if nessecary + function getNextFile() { + if (files.length === 0) return thenDo(); + var file = files.shift(), + forceDownload = options.forceDownload || file.forceDownload; + if (forceDownload) downloadFile(file, display, options, getNextFile); + else checkExisting(file, display, options, + function ifExists() { + getNextFile(); + }, + function ifNotExists() { + downloadFile(file, display, options, getNextFile); + }); + } + getNextFile(); +} + +SqueakJS.runSqueak = function(imageUrl, canvas, options={}) { + if (!canvas) { + canvas = document.createElement("canvas"); + canvas.style.position = "absolute"; + canvas.style.left = "0"; + canvas.style.top = "0"; + canvas.style.width = "100%"; + canvas.style.height = "100%"; + document.body.appendChild(canvas); + } + // we need to fetch all files first, then run the image + processOptions(options); + if (imageUrl && imageUrl.endsWith(".zip")) { + options.zip = imageUrl.match(/[^\/]*$/)[0]; + options.url = imageUrl.replace(/[^\/]*$/, ""); + imageUrl = null; + } + if (!imageUrl && options.image) imageUrl = options.image; + var baseUrl = options.url || ""; + if (!baseUrl && imageUrl && imageUrl.replace(/[^\/]*$/, "")) { + baseUrl = imageUrl.replace(/[^\/]*$/, ""); + imageUrl = imageUrl.replace(/^.*\//, ""); + } + options.url = baseUrl; + if (baseUrl[0] === "/" && baseUrl[1] !== "/" && baseUrl.length > 1 && options.root === "/") { + options.root = baseUrl; + } + fetchTemplates(options); + var display = createSqueakDisplay(canvas, options), + image = {url: null, name: null, image: true, data: null}, + files = []; + display.argv = options.argv; + if (imageUrl) { + var url = Squeak.splitUrl(imageUrl, baseUrl); + image.url = url.full; + image.name = url.filename; + } + if (options.files) { + options.files.forEach(function(f) { + var url = Squeak.splitUrl(f, baseUrl); + if (image.name === url.filename) {/* pushed after other files */} + else if (!image.url && f.match(/\.image$/)) { + image.name = url.filename; + image.url = url.full; + } else { + files.push({url: url.full, name: url.filename}); + } + }); + } + if (options.zip) { + var zips = typeof options.zip === "string" ? [options.zip] : options.zip; + zips.forEach(function(zip) { + var url = Squeak.splitUrl(zip, baseUrl); + var prefix = ""; + // if filename has no version info, but full url has it, use full url as prefix + if (!url.filename.match(/[0-9]/) && url.uptoslash.match(/[0-9]/)) { + prefix = url.uptoslash.replace(/^[^:]+:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_"); + } + files.push({url: url.full, name: prefix + url.filename, zip: true}); + }); + } + if (image.url) files.push(image); + if (options.document) { + var url = Squeak.splitUrl(options.document, baseUrl); + files.push({url: url.full, name: url.filename, forceDownload: options.forceDownload !== false}); + display.documentName = options.root + url.filename; + } + options.image = image; + fetchFiles(files, display, options, function thenDo() { + Squeak.fsck(); // will run async + var image = options.image; + if (!image.name) return alert("could not find an image"); + if (!image.data) return alert("could not find image " + image.name); + SqueakJS.appName = options.appName || image.name.replace(/(.*\/|\.image$)/g, ""); + SqueakJS.runImage(image.data, options.root + image.name, display, options); + }); + return display; +}; + +SqueakJS.quitSqueak = function() { + SqueakJS.vm.quitFlag = true; +}; + +SqueakJS.onQuit = function(vm, display, options) { + window.onbeforeunload = null; + display.vm = null; + if (options.spinner) options.spinner.style.display = "none"; + if (options.onQuit) options.onQuit(vm, display, options); + else display.showBanner(SqueakJS.appName + " stopped."); +}; + +////////////////////////////////////////////////////////////////////////////// +// browser stuff +////////////////////////////////////////////////////////////////////////////// + +if (window.applicationCache) { + applicationCache.addEventListener('updateready', function() { + // use original appName from options + var appName = window.SqueakJS && SqueakJS.options && SqueakJS.options.appName || "SqueakJS"; + if (confirm(appName + ' has been updated. Restart now?')) { + window.onbeforeunload = null; + window.location.reload(); + } + }); +} diff --git a/Core/setting.py b/Core/setting.py new file mode 100644 index 0000000..0ce965f --- /dev/null +++ b/Core/setting.py @@ -0,0 +1,10 @@ +# My first python file in this static folder. + +# I hope this is good. With Coding Giants I can learn more about this. + +# No.1: I'm making sure this executes correctly. I'm doing some print statements for test purposes... | No.2: I will add this to the index.html file. That file is out of the static folder. | No.3: Outside, there are just .html files, but why...? + +Setting_Variable = "this is some useless setting variable." +print("Setting Variable loaded here.") +for x in Setting_Variable: + print(x) \ No newline at end of file diff --git a/Core/yarn.mas b/Core/yarn.mas new file mode 100644 index 0000000..5ab8c27 --- /dev/null +++ b/Core/yarn.mas @@ -0,0 +1,12 @@ +import === "./Core/yarn.roy" + + +(--[[ + cpu(GetCore,yarn) -- { + const(yarn) == "GetCoreFileRegisterSystem", 0 + } + ;ranks -- { + Owner === ("arancia313",(0)yarn) + } +]end] +).(Mas) diff --git a/Core/yarn.roy b/Core/yarn.roy new file mode 100644 index 0000000..7eeb0c8 --- /dev/null +++ b/Core/yarn.roy @@ -0,0 +1 @@ +;print("yo") \ No newline at end of file diff --git a/README.md b/README.md index baa2ad5..12238e0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ # BarnyWarp BarnyWarp is a Arancia 3 Mod that compiles the projects in HTML. -# Warning -There's no a website, so you can download it. +# What... +What i was thinking??!! OF COURSE THERE IS A WEBSITE!!! you don't need to download anymore! diff --git a/Recovery.txt b/Recovery.txt new file mode 100644 index 0000000..72251e2 --- /dev/null +++ b/Recovery.txt @@ -0,0 +1,370 @@ + + + + + + BarnyWarp - Organizza. Programma. Inventa. + + + + + + + + + + + + + + + +
+
+

Recent Commits

+
    +
    +

    Welcome to the page of BarnyWarp.

    +
    + + + + + + + + + \ No newline at end of file diff --git a/Space Ambience.wav b/Space Ambience.wav new file mode 100644 index 0000000..e3a5ad0 Binary files /dev/null and b/Space Ambience.wav differ diff --git a/ToothLess/R.A.T.txt b/ToothLess/R.A.T.txt deleted file mode 100644 index 629af8c..0000000 --- a/ToothLess/R.A.T.txt +++ /dev/null @@ -1 +0,0 @@ -In the past, Spike has created a bug in the secure folder. That's because of his errors. He overheats the secure folder for doing a single error. \ No newline at end of file diff --git a/ToothLess/README.md b/ToothLess/README.md deleted file mode 100644 index 0c24270..0000000 --- a/ToothLess/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# ToothLess - diff --git a/Windex Antivirus.html b/Windex Antivirus.html deleted file mode 100644 index bd6da07..0000000 --- a/Windex Antivirus.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Windex Antivirus - - - -

    Windex Antivirus

    -

    Windex Antivirus is a free program that protects you from malware, viruses, and other threats. It's easy to use and it contains secured Clogs. Windex Antivirus is a BarnyWarp mod.

    - -

    Your connection is secured

    - - - \ No newline at end of file diff --git a/Windex.html b/Windex.html deleted file mode 100644 index 654702c..0000000 --- a/Windex.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - -BarnyWarp - -

    BarnyWarp

    -

    You can use BarnyWarp 1.4 -or BarnyWarp 2.0.

    - -

    Welcome to the home page of BarnyWarp.

    - - -

    ----------------------------------------------------------------------------------------------------------------------------------------------------------

    - - - - -
    - - - - -
    - - diff --git a/editor.html b/editor.html new file mode 100644 index 0000000..e34f6a9 --- /dev/null +++ b/editor.html @@ -0,0 +1,594 @@ + + + + + + + + + BarnyWarp 2.5 + + + + + +
    + + + +
    + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gallery.html b/gallery.html new file mode 100644 index 0000000..557ee5b --- /dev/null +++ b/gallery.html @@ -0,0 +1,163 @@ + + + + + + + + BarnyWarp Charathers Gallery + + +
    +

    BarnyWarp Charathers Gallery

    +

    This is the BarnyWarp Charathers Gallery.

    + Home + +

    Charathers

    + + +

    Arancia Carnivora

    +

    Arancia Carnivora è un clone di pianta carnivora che sa programmare, intasare e atre cose. + Arancia Carnivora è stato prodotto e disegnato dal ministro di pianta carnivora. +

    + + +

    g

    +

    g è uno dell'Orsay che sa disegnare, intasare, ecc. Di solito va a intasare con qualsiasi notifica di Youtube. + + g è stato prodotto e disegnato dal ministro di garrymations e di pianta carnivora. +

    + + +

    Cardinal Red Scratch Cat

    +

    Cardinal Red Scratch Cat è un gatto cardinale, considerato come il "Re" del governo cardinale, ma a volte combina dei pasticci. Alcuni dannosi, come per esempio: + 1: Mangia Troppo. + 2: Invita notifiche che NON conosce. + 3: Aiuta Dango a ripristinare le notifiche oramai non funzionanti. + 4: Come i gatti di sempre, NON fa mai il bagno, ma viene obbligato da Red Scratch Cat. + Cardinal Red Scratch Cat è stato prodotto e disegnato dal ministro di Arancia Carnivora. +

    + + +

    k

    +

    k è la mascotte di BarnyWarp, ma anche una dell'Orsay che va a intasare di solito con una notifica. Di solito vuole andare nell'Area Personale, anche quando ci sono delle feste fatte da SPIKE. + Gli piace anche dormire, intasare, ecc. + k è stata prodotta e disegnata dal ministro di garrymations e di pianta carnivora. +

    + +

    Dango cat

    +

    Mascotte di TurboWarp.

    + + +

    Quattro

    +

    Semplicemente uno dell'Area Personale che è sempre impegnato nei suoi appostamenti.

    + + +

    Kuh (cappa)

    +

    Uno dell'Area Personale che ha l'IQ al 2000%, Basta dirli una domanda complicata e Kuh risponde correttamente, Come GitHub Copilot, MAVALAA!!!

    + + +

    Spike

    +

    Come dicevo sempre dall'anno scorso, Spike è un grifone troppo assonato, il che NON sta mai attento alle lezioni e prende sempre + Brutti voti nell'area personale. + Spike è stato prodotto e disegnato dal ministro di pianta carnivora. +

    + + +

    i

    +

    Semplicemente una dell'Orsay che non parla mai, ma scrive quello che pensa su un foglio.

    + + +

    Classic Kuh (cappa classica)

    +

    Classic Kuh è un clone di Kuh che, come k, ama dormire, ma PER MOOOOOOOOOOOLTO tempo. Poi, classic Kuh, ha abilità e forza molto più elevati di quelle di Kuh. + Classic Kuh è stato prodotto e disegnato dall'Area Personale. +

    +
    + +
    + +
  • Icon | GitHub | Arancia 3 +
  • + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..1bea53c --- /dev/null +++ b/index.html @@ -0,0 +1,18 @@ + + + + + + + + + Redirect + + + +

    If you weren't redirected automatically, click here, clicca qui.

    + + diff --git a/index.js b/index.js deleted file mode 100644 index 8186588..0000000 --- a/index.js +++ /dev/null @@ -1,4 +0,0 @@ -alert('Hello!'); -if (confirm('Still continue?') === true) { - // codice da eseguire se l'utente preme "OK" - } \ No newline at end of file diff --git a/lib/addtohomescreen.css b/lib/addtohomescreen.css new file mode 100644 index 0000000..0ec58c2 --- /dev/null +++ b/lib/addtohomescreen.css @@ -0,0 +1,257 @@ +.ath-viewport * { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.ath-viewport { + position: relative; + z-index: 2147483641; + pointer-events: none; + + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-text-size-adjust: none; + -moz-text-size-adjust: none; + -ms-text-size-adjust: none; + -o-text-size-adjust: none; + text-size-adjust: none; +} + +.ath-modal { + pointer-events: auto !important; + background: rgba(0,0,0,0.6); +} + +.ath-mandatory { + background: #000; +} + +.ath-container { + pointer-events: auto !important; + position: absolute; + z-index: 2147483641; + padding: 0.7em 0.6em; + width: 18em; + + background: #eee; + background-size: 100% auto; + + box-shadow: 0 0.2em 0 #000000; + + font-family: sans-serif; + font-size: 15px; + line-height: 1.5em; + text-align: center; +} + +.ath-container small { + font-size: 0.8em; + line-height: 1.3em; + display: block; + margin-top: 0.5em; +} + +.ath-ios.ath-phone { + bottom: 1.8em; + left: 50%; + margin-left: -9em; +} + +.ath-ios6.ath-tablet { + left: 5em; + top: 1.8em; +} + +.ath-ios7.ath-tablet { + left: 0.7em; + top: 1.8em; +} + +.ath-ios8.ath-tablet, +.ath-ios9.ath-tablet{ + right: 0.4em; + top: 1.8em; +} + +.ath-android { + bottom: 1.8em; + left: 50%; + margin-left: -9em; +} + +/* close icon */ +.ath-container:before { + content: ''; + position: relative; + display: block; + float: right; + margin: -0.7em -0.6em 0 0.5em; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAACECAMAAABmmnOVAAAAdVBMVEUAAAA5OTkzMzM7Ozs3NzdBQUFAQEA/Pz8+Pj5BQUFAQEA/Pz8+Pj5BQUFAQEA/Pz9BQUE+Pj4/Pz8/Pz8+Pj4/Pz8/Pz8/Pz8+Pj4/Pz8+Pj4/Pz8/Pz8/Pz8/Pz8/Pz8+Pj4/Pz8/Pz8/Pz8/Pz9AQEA/Pz+fdCaPAAAAJnRSTlMACQoNDjM4OTo7PEFCQ0RFS6ytsbS1tru8vcTFxu7x8vX19vf4+C5yomAAAAJESURBVHgBvdzLTsJAGEfxr4C2KBcVkQsIDsK8/yPaqIsPzVlyzrKrX/5p0kkXEz81L23otc9NpIbbWia2YVLqdnhlqFlhGWpSDHe1aopsSIpRb8gK0dC3G30b9rVmhWZIimTICsvQtx/FsuYOrWHoDjX3Gu31gzJxdki934WrAIOsAIOsAIOiAMPhPsJTgKGN0BVsYIVsYIVpYIVpYIVpYIVpYIVpYIVpYIVpYIVlAIVgEBRs8BRs8BRs8BRs8BRs8BRs8BRs8BRTNmgKNngKNngKNngKNngKNhiKGxgiOlZoBlaYBlaYBlaYBlaYBlaYBlaYBlaYBlZIBlBMfQMrVAMr2KAqBENSHFHhGEABhi5CV6gGUKgGUKgGUKgGUFwuqgEUvoEVsoEVpoEUpgEUggF+gKTKY+h1fxSlC7/Z+RrxOQ3fcEoAPPHZBlaYBlaYBlaYBlZYBlYIhvLBCstw7PgM7hkiWOEZWGEaWGEaWGEaIsakEAysmHkGVpxmvoEVqoEVpoEVpoEVpoEVpoEVpoEVkoEVgkFQsEFSsEFQsGEcoSvY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnmbNAUT2c2WAo2eAo2eAo2eAo2eAo2eArNEPFACjZ4CjZ4CjZ4CjaIird/rBvFH6llNCvewdli1URWCIakSIZesUaDoFg36dKFWk9zCZDei3TtwmCj7pC22AwikiIZPEU29IpFNliKxa/hC9DFITjQPYhcAAAAAElFTkSuQmCC); + background-color: rgba(0, 0, 0, 0.8); + background-size: 50%; + background-repeat: no-repeat; + background-position: 50%; + width: 2.7em; + height: 2.7em; + text-align: center; + overflow: hidden; + color: #a33; + z-index: 2147483642; +} + +.ath-container.ath-icon:before { + position: absolute; + top: 0; + right: 0; + margin: 0; + float: none; +} + +.ath-mandatory .ath-container:before { + display: none; +} + +.ath-container.ath-android:before { + float: left; + margin: -0.7em 0.5em 0 -0.6em; +} + +.ath-container.ath-android.ath-icon:before { + position: absolute; + right: auto; + left: 0; + margin: 0; + float: none; +} + + +/* applied only if the application icon is shown */ +.ath-container.ath-icon { + +} + +.ath-action-icon { + display: inline-block; + vertical-align: middle; + background-position: 50%; + background-repeat: no-repeat; + text-indent: -9999em; + overflow: hidden; +} + +.ath-ios7 .ath-action-icon, +.ath-ios8 .ath-action-icon, +.ath-ios9 .ath-action-icon{ + width: 1.6em; + height: 1.6em; + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAACtCAYAAAB7l7tOAAAF6UlEQVR4AezZWWxUZRiH8VcQEdxZEFFiUZBFUCIa1ABBDARDcCciYGKMqTEGww3SOcNSAwQTjOBiiIpEhRjAhRgXRC8MFxojEhAFZUGttVhaoSxlaW3n8W3yXZxm6vTrOMM5Q98n+V9MMu1pvl++uZhKuypghu49KaaTWGdZSYoVN6VD95nMpLNYZ9XNbdQR2od2k88O3Gm6Bh0t7H0p5Vwp2Ax3ajpu2tYbciFWwkTFO63DY6+JcI4USFaSyYpWp8N7SVZJKR3EinkBk9JxvZFXxhnZSjBaoWp1ZL0ES8WKYXMZp0AndORgy8WKFe5Yf1zvvSBWDEpys2LU6MjD5kmEWQlGKsJRHXlcqUSQVcItEnDEA6gAb7LhjvD9WO6yIEfICQI5A1nzGCYB1T4og5bBiFcyv2f6ujYhl4iVxwKG6qp8MK55HsqPwK0rMr9v/yEo3uCPrJstVh5KMER30Aeh31Ioq0FrHfjXw9CYghnrvYFTuqfEymFzGSwBlT4ARYr7u+K6GLmCVGvAGg2NMG0d/sgJnpScZLjXSkC5z8H3eQ72/k24Q8NfzvwFyK4qtuJSZKaubRPyE/K/Mtx+EvCHL+7uasId1t10w0scz/RzSzYzAfgKV30D3LPaG7lRkR8RK4tKKJKAMp+D7r0EfmmOe0x3m2itAc/ZxBjgAt1mXHWKPPkdb+QGSTJdrDaU5EoJ2OtzwD0WwY7KNNzbRfMFFg24WPdtGHnS221Cflgsj56hjwTs8TnY7oq7/QDhjutGicsb2AVcovsO18l6uPPNNiE/JFaGAq7Q7fY50G4LYVtz3FrdaNGyBXbIl+q24DqhyHes9EaulwR3SwtZs+ktAT/7HORliru1gnCndONFyx44Dfn7MPLYN7yR6yTJZAllJeguAT/4HOBFz8I3ZWm4E0TLFbBD7qn7EVdtHYx53R9ZN0ksrZRuErDN5+AuLIWvm+Oe1k0ULdfADrmX7idcR0/DyBXeyCdlLuMMOGCBz4F1ng+f7yFcve5e0fIFHELeiav6BAx70Rt5p0yhY3u/wR0kyarW/uX35b403PtFyzewQ75ctwtXzSkY8WqruHslSV8RscrL6TJ1bcvfWJ0/HzbtIdw/ugdFyzdwOOAq3T6fmzxwGQ3vbmO8iFioIWqYSsHMj9M/ljfuTsOdItoZBXYBfXX7cVXVwvXLm/8+fU3lcdCqdEMNGBbgUmRmfQISQKd5sGEn4VK6YtEiAXYBA3QVuA4q8hCHrDcafR1ul65jewfuovsCl7vJrNlOuEbdo6JFCuwCrtb9hqusBu56Cw4cI1y1briIWEBn3Ue0XKPuMdGiBg4H9NdV0HJ/6QZLOEPmPN0GmpfSPS5arIBdwHUtIFfoBsl/ZsgfhHCfFi2WwC5goO4AmvanbqBkzJA76tboZokWa2AXMEi3RTdAvDLkDqJFAhzB32xFD2wZsGXA0WfAlgFbBmwZsGXAlgFbBpzk04JaKb0iA9ZnF9x5SQAFtRKKIgPWZxfaeRmwAZ/BGbAB37eaG6MCbnq2Aed5czYyKirgpmcbsAHHZAZswN0Wwo7KeG1fFf2jAm56dtzOQ42yB+65mDhWFBUwUETMUiMDNmADbp/APRaTAh6I2bpGCNw1bufRZJQ1cPdF/NueHZsgDEBBGLbMGoIu4AZu5gLOZeEaYmEXeznF3jRPyEv4frgJvvJe3qTefY0AAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwb8rwADBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgz4/sz1Nia/9hizA7zgklwy3RYwYMBzBRjw4bPjxAbAAizAAtwgwAIswAIswAIMGDBgARZgARZgAS4FWIAFWIAFWIABAwYswAIswAIswIUAC7AAC7AACzBgwIAFWIAFWIAFuBBgARZgARZgAQYMGPApQ99ZCdgWtzqwATbABtgAG2DbnxNb7zbRimsMLMACrDf2wMWI/WasfQAAAABJRU5ErkJggg==); + margin-top: -0.3em; + background-size: auto 100%; +} + +.ath-ios6 .ath-action-icon { + width: 1.8em; + height: 1.8em; + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAAB0CAQAAADAmnOnAAAAAnNCSVQICFXsRgQAAAAJcEhZcwAAWwEAAFsBAXkZiFwAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAF4klEQVR4Ae3a/a+XdR3H8ec5HM45HDmKICoVohkZsxESRRCzcZM/2JKkdGR5MrSkleA0Pd00O4u5IVuNM2yYc6XSzCExU4oUNRPCJFdMUAhsYZpUGhscOHA4N8/WZzsL6HBxvofvdV3fa3yer//gsV3vH659KHzncBsJxUYhDzOEhCKQbORs+ip2wzgM+wvj+P9i35qAGLaHGcQSgKSTrxBLABJppZpYApCspoFYApBsZjSxBCD5OxOJJQBJG1cQSwCSLpqJJQCJ3MvgCGTinuSMCJS8LZwfgZL3FtMiUPIOcU0ESl4PLRHoRPsJtREoeRsYGYGS9yrvo6RmpbLaigWSfzOdErLs6+bLUMFA0sF1+QF1cz1UNlBYK9V5AHXyWSgEkKyiIWOgGh829Ki1lLcaxjCVK7mJRSxjBY+zgRf/u9pXcMB7jhEZAg32EUP3O6hMKOP5Iq2sZQeHMZXt5KKMgOpcY+iHVnFyjeQKlrCBdsxge5ieAVC9vzLUelI8H+A7bKIHM10H81IGGuKvDf1ggDxVTKOV1zG3/Yia1ICG+ltD32MgNTKfP2HuW0VDKkCNrjfUTOm9i6XswwrZJkaVHeh0f2fodkrtfO6jAytqrzG+rEDDfVG1x1sprZEs5RBW4PZxeT+Bbrf5hPu9arfzKaU6WjiAFbseWvoF1GW/6vYGSmkyW7Dit4xB5QHq9Br6Xx2t9GAhtp6zkoHsfNp1J9wX6H+jeR4LtJc4LxGopZZyNpN/YcG2mw9nBTSPLizgOmjKAujGgvJID3ekD7QYi7nGzkvmQtpA38Vi7iJf0TedlC7QTVjMfcY2QyvSBPpUMW/PIBfbo9pls1XpAX2EdizeznStob3OJpQO0DB2YfE21q2GtnghpAm0Gou3T9tm6BGHQppA12HRVt17eboNlydNoLHsx2JtmL801OYcQmkC/QKLtQt9ydBW3wNpA30ci7Ur3WdolUMhbaBqNhf/8qQJ9Hkszs5wjaH9XkUobaAqtmFRdoGbDb3sWMgG6DIs5852knO82RaXer+P+qyb3eWeo7ZNBrRZvm1otY2QFdBjeHIb6hTne49Put12+9ObMoDdYmfy5UkF6AK6cCCr9aM2u9IddptcOYCG+FNDB5xLKCugO7G01TndFp/xgAntdYvrfdwVLnORt3q9Vx25F27DUjbGPxr6qxMgW6Cd2N+d6wLXedA+6nKbK73Lr/pJxzusvE/wZrvX0FOOgGyBxmF/dprXutYOj6nNdS6xyYnWp/dGcaGdhr5vDWQN9E1MXrUzfcA2j2qPj/l1J1uT9iPOeh8w1O7nCGUN9HzyGZ7ndo9qp0ucanU2r1xH+wdDu5wIeQDVVx0+/kd1i697RNv8thdn+Qz4Uv9p6DeOhHyApmBfq3OBu+3Nfd7nVELZAX3Nw4ZarYG8gG7GY1dlk6/Zm3/2Rk8jlB1QvT82dNAmQjkBVf8Mj957fdrefM7ZVhPKEuidvmDob06CXIGGbsX/bZDf8KAhfdbJhLIGmuZuQ084HHIGatiLvRvrRkP6qldbBXkAzbfD0N0OhryBGqrEMOd50FC7d1hPKGugBh8ydMh5hPIGGouI1d5lj6F1vptQ9kDvcKOhN5wMlQH0QcRGnzC03yZCeQDN9G1D6xwBFQI07FI8x02GdjgB8gJqttPQcmuhYoAumzvG7YZWejrkA1TrPYYO+SVCFQO0aM4bqj0uJJQH0LluSP7PkyeQU9QOmyAvoBm+Zegpz4LKA/qYB/wE5AXUe3m81zqoRKAPOYWcuvP9dxvqcD6h7IAKkaNU3eUlHLcI9EzS5YlAi62h/zUy89QCqqKUmvgHywsJlEHnsQYxAvXVIJo5gIhnPhiBju1iNmLvLn85Ah1ZPYs5jBGo72awEzEC9dVwHqQHI9DxWoAYgSLQQKteGIESu/qhCJTYtT+PQBEoAkWgCBSBkotAEehUWwSKQBEoAkWg/BeBIlAEikARKAJFoFmealu4gVLy1Gt5dkARKAL9BzujPSurTmu/AAAAAElFTkSuQmCC); + margin-bottom: 0.4em; + background-size: 100% auto; +} + +.ath-android .ath-action-icon { + width: 1.4em; + height: 1.5em; + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAANlBMVEVmZmb///9mZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZW6fJrAAAAEXRSTlMAAAYHG21ub8fLz9DR8/T4+RrZ9owAAAB3SURBVHja7dNLDoAgDATQWv4gKve/rEajJOJiWLgg6WzpSyB0aHqHiNj6nL1lovb4C+hYzkSNAT7mryQFAVOeGAj4CjwEtgrWXpD/uZKtwEJApXt+Vn0flzRhgNiFZQkOXY0aADQZCOCPlsZJ46Rx0jhp3IiN2wGDHhxtldrlwQAAAABJRU5ErkJggg==); + background-size: 100% auto; +} + +.ath-container p { + margin: 0; + padding: 0; + position: relative; + z-index: 2147483642; + text-shadow: 0 0.1em 0 #fff; + font-size: 1.1em; +} + +.ath-ios.ath-phone:after { + content: ''; + background: #eee; + position: absolute; + width: 2em; + height: 2em; + bottom: -0.9em; + left: 50%; + margin-left: -1em; + -webkit-transform: scaleX(0.9) rotate(45deg); + transform: scaleX(0.9) rotate(45deg); + box-shadow: 0.2em 0.2em 0 #d1d1d1; +} + +.ath-ios.ath-tablet:after { + content: ''; + background: #eee; + position: absolute; + width: 2em; + height: 2em; + top: -0.9em; + left: 50%; + margin-left: -1em; + -webkit-transform: scaleX(0.9) rotate(45deg); + transform: scaleX(0.9) rotate(45deg); + z-index: 2147483641; +} + +.ath-application-icon { + position: relative; + padding: 0; + border: 0; + margin: 0 auto 0.2em auto; + height: 6em; + width: 6em; + z-index: 2147483642; +} + +.ath-container.ath-ios .ath-application-icon { + border-radius: 1em; + box-shadow: 0 0.2em 0.4em rgba(0,0,0,0.3), + inset 0 0.07em 0 rgba(255,255,255,0.5); + margin: 0 auto 0.4em auto; +} + +@media only screen and (orientation: landscape) { + .ath-container.ath-phone { + width: 24em; + } + + .ath-android.ath-phone { + margin-left: -12em; + } + + .ath-ios.ath-phone { + margin-left: -12em; + } + + .ath-ios6:after { + left: 39%; + } + + .ath-ios8.ath-phone { + left: auto; + bottom: auto; + right: 0.4em; + top: 1.8em; + } + + .ath-ios8.ath-phone:after { + bottom: auto; + top: -0.9em; + left: 68%; + z-index: 2147483641; + box-shadow: none; + } +} diff --git a/lib/addtohomescreen.js b/lib/addtohomescreen.js new file mode 100644 index 0000000..81a1076 --- /dev/null +++ b/lib/addtohomescreen.js @@ -0,0 +1,725 @@ +/* Add to Homescreen v3.2.2 ~ (c) 2015 Matteo Spinelli ~ @license: http://cubiq.org/license */ +(function (window, document) { +/* + _ _ _____ _____ + ___ _| |_| |_ _|___| | |___ _____ ___ ___ ___ ___ ___ ___ ___ +| .'| . | . | | | | . | | . | | -_|_ -| _| _| -_| -_| | +|__,|___|___| |_| |___|__|__|___|_|_|_|___|___|___|_| |___|___|_|_| + by Matteo Spinelli ~ http://cubiq.org +*/ + +// Check for addEventListener browser support (prevent errors in IE<9) +var _eventListener = 'addEventListener' in window; + +// Check if document is loaded, needed by autostart +var _DOMReady = false; +if ( document.readyState === 'complete' ) { + _DOMReady = true; +} else if ( _eventListener ) { + window.addEventListener('load', loaded, false); +} + +function loaded () { + window.removeEventListener('load', loaded, false); + _DOMReady = true; +} + +// regex used to detect if app has been added to the homescreen +var _reSmartURL = /\/ath(\/)?$/; +var _reQueryString = /([\?&]ath=[^&]*$|&ath=[^&]*(&))/; + +// singleton +var _instance; +function ath (options) { + _instance = _instance || new ath.Class(options); + + return _instance; +} + +// message in all supported languages +ath.intl = { + de_de: { + ios: 'Um diese Web-App zum Home-Bildschirm hinzuzufügen, tippen Sie auf %icon und dann Zum Home-Bildschirm.', + android: 'Um diese Web-App zum Home-Bildschirm hinzuzufügen, öffnen Sie das Menü und tippen dann auf Zum Startbildschirm hinzufügen. Wenn Ihr Gerät eine Menütaste hat, lässt sich das Browsermenü über diese öffnen. Ansonsten tippen Sie auf %icon.' + }, + + da_dk: { + ios: 'For at tilføje denne web app til hjemmeskærmen: Tryk %icon og derefter Føj til hjemmeskærm.', + android: 'For at tilføje denne web app til hjemmeskærmen, åbn browser egenskaber menuen og tryk på Føj til hjemmeskærm. Denne menu kan tilgås ved at trykke på menu knappen, hvis din enhed har en, eller ved at trykke på det øverste højre menu ikon %icon.' + }, + + en_us: { + ios: 'To add this web app to the home screen: tap %icon and then Add to Home Screen.', + android: 'To add this web app to the home screen open the browser option menu and tap on Add to homescreen. The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon %icon.' + }, + + es_es: { + ios: 'Para añadir esta aplicación web a la pantalla de inicio: pulsa %icon y selecciona Añadir a pantalla de inicio.', + android: 'Para añadir esta aplicación web a la pantalla de inicio, abre las opciones y pulsa Añadir a pantalla inicio. El menú se puede acceder pulsando el botón táctil en caso de tenerlo, o bien el icono de la parte superior derecha de la pantalla %icon.' + }, + + fi_fi: { + ios: 'Liitä tämä sovellus kotivalikkoon: klikkaa %icon ja tämän jälkeen Lisää kotivalikkoon.', + android: 'Lisätäksesi tämän sovelluksen aloitusnäytölle, avaa selaimen valikko ja klikkaa tähti -ikonia tai Lisää aloitusnäytölle tekstiä. Valikkoon pääsee myös painamalla menuvalikkoa, jos laitteessasi on sellainen tai koskettamalla oikealla yläkulmassa menu ikonia %icon.' + }, + + fr_fr: { + ios: 'Pour ajouter cette application web sur l\'écran d\'accueil : Appuyez %icon et sélectionnez Ajouter sur l\'écran d\'accueil.', + android: 'Pour ajouter cette application web sur l\'écran d\'accueil : Appuyez sur le bouton "menu", puis sur Ajouter sur l\'écran d\'accueil. Le menu peut-être accessible en appyant sur le bouton "menu" du téléphone s\'il en possède un . Sinon, il se trouve probablement dans la coin supérieur droit du navigateur %icon.' + }, + + he_il: { + ios: 'להוספת האפליקציה למסך הבית: ללחוץ על %icon ואז הוסף למסך הבית.', + android: 'To add this web app to the home screen open the browser option menu and tap on Add to homescreen. The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon %icon.' + }, + + hu_hu: { + ios: 'Ha hozzá szeretné adni ezt az alkalmazást a kezdőképernyőjéhez, érintse meg a következő ikont: %icon , majd a Hozzáadás a kezdőképernyőhöz menüpontot.', + android: 'Ha hozzá szeretné adni ezt az alkalmazást a kezdőképernyőjéhez, a böngésző menüjében kattintson a Hozzáadás a kezdőképernyőhöz menüpontra. A böngésző menüjét a következő ikon megérintésével tudja megnyitni: %icon.' + }, + + it_it: { + ios: 'Per aggiungere questa web app alla schermata iniziale: premi %icon e poi Aggiungi a Home.', + android: 'Per aggiungere questa web app alla schermata iniziale, apri il menu opzioni del browser e premi su Aggiungi alla homescreen. Puoi accedere al menu premendo il pulsante hardware delle opzioni se la tua device ne ha uno, oppure premendo l\'icona %icon in alto a destra.' + }, + + ja_jp: { + ios: 'このウェプアプリをホーム画面に追加するために%iconを押してホーム画面に追加。', + android: 'To add this web app to the home screen open the browser option menu and tap on Add to homescreen. The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon %icon.' + }, + + ko_kr: { + ios: '홈 화면에 바로가기 생성: %icon 을 클릭한 후 홈 화면에 추가.', + android: '브라우저 옵션 메뉴의 홈 화면에 추가를 클릭하여 홈화면에 바로가기를 생성할 수 있습니다. 옵션 메뉴는 장치의 메뉴 버튼을 누르거나 오른쪽 상단의 메뉴 아이콘 %icon을 클릭하여 접근할 수 있습니다.' + }, + + nb_no: { + ios: 'For å installere denne appen på hjem-skjermen: trykk på %icon og deretter Legg til på Hjem-skjerm.', + android: 'For å legge til denne webappen på startsiden åpner en nettlesermenyen og velger Legg til på startsiden. Menyen åpnes ved å trykke på den fysiske menyknappen hvis enheten har det, eller ved å trykke på menyikonet øverst til høyre %icon.' + }, + + pt_br: { + ios: 'Para adicionar este app à tela de início: clique %icon e então Tela de início.', + android: 'To add this web app to the home screen open the browser option menu and tap on Add to homescreen. The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon %icon.' + }, + + pt_pt: { + ios: 'Para adicionar esta app ao ecrã principal: clique %icon e depois Ecrã principal.', + android: 'To add this web app to the home screen open the browser option menu and tap on Add to homescreen. The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon %icon.' + }, + + nl_nl: { + ios: 'Om deze webapp op je telefoon te installeren, klik op %icon en dan Zet in beginscherm.', + android: 'To add this web app to the home screen open the browser option menu and tap on Add to homescreen. The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon %icon.' + }, + + ru_ru: { + ios: 'Чтобы добавить этот сайт на свой домашний экран, нажмите на иконку %icon и затем На экран "Домой".', + android: 'Чтобы добавить сайт на свой домашний экран, откройте меню браузера и нажмите на Добавить на главный экран. Меню можно вызвать, нажав на кнопку меню вашего телефона, если она есть. Или найдите иконку сверху справа %icon[иконка].' + }, + + sv_se: { + ios: 'För att lägga till denna webbapplikation på hemskärmen: tryck på %icon och därefter Lägg till på hemskärmen.', + android: 'För att lägga till den här webbappen på hemskärmen öppnar du webbläsarens alternativ-meny och väljer Lägg till på startskärmen. Man hittar menyn genom att trycka på hårdvaruknappen om din enhet har en sådan, eller genom att trycka på menyikonen högst upp till höger %icon.' + }, + + zh_cn: { + ios: '如要把应用程序加至主屏幕,请点击%icon, 然后添加到主屏幕', + android: 'To add this web app to the home screen open the browser option menu and tap on Add to homescreen. The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon %icon.' + }, + + zh_tw: { + ios: '如要把應用程式加至主屏幕, 請點擊%icon, 然後加至主屏幕.', + android: 'To add this web app to the home screen open the browser option menu and tap on Add to homescreen. The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon %icon.' + } +}; + +// Add 2 characters language support (Android mostly) +for ( var lang in ath.intl ) { + ath.intl[lang.substr(0, 2)] = ath.intl[lang]; +} + +// default options +ath.defaults = { + appID: 'org.cubiq.addtohome', // local storage name (no need to change) + fontSize: 15, // base font size, used to properly resize the popup based on viewport scale factor + debug: false, // override browser checks + logging: false, // log reasons for showing or not showing to js console; defaults to true when debug is true + modal: false, // prevent further actions until the message is closed + mandatory: false, // you can't proceed if you don't add the app to the homescreen + autostart: true, // show the message automatically + skipFirstVisit: false, // show only to returning visitors (ie: skip the first time you visit) + startDelay: 1, // display the message after that many seconds from page load + lifespan: 15, // life of the message in seconds + displayPace: 1440, // minutes before the message is shown again (0: display every time, default 24 hours) + maxDisplayCount: 0, // absolute maximum number of times the message will be shown to the user (0: no limit) + icon: true, // add touch icon to the message + message: '', // the message can be customized + validLocation: [], // list of pages where the message will be shown (array of regexes) + onInit: null, // executed on instance creation + onShow: null, // executed when the message is shown + onRemove: null, // executed when the message is removed + onAdd: null, // when the application is launched the first time from the homescreen (guesstimate) + onPrivate: null, // executed if user is in private mode + privateModeOverride: false, // show the message even in private mode (very rude) + detectHomescreen: false // try to detect if the site has been added to the homescreen (false | true | 'hash' | 'queryString' | 'smartURL') +}; + +// browser info and capability +var _ua = window.navigator.userAgent; + +var _nav = window.navigator; +_extend(ath, { + hasToken: document.location.hash == '#ath' || _reSmartURL.test(document.location.href) || _reQueryString.test(document.location.search), + isRetina: window.devicePixelRatio && window.devicePixelRatio > 1, + isIDevice: (/iphone|ipod|ipad/i).test(_ua), + isMobileChrome: _ua.indexOf('Android') > -1 && (/Chrome\/[.0-9]*/).test(_ua) && _ua.indexOf("Version") == -1, + isMobileIE: _ua.indexOf('Windows Phone') > -1, + language: _nav.language && _nav.language.toLowerCase().replace('-', '_') || '' +}); + +// falls back to en_us if language is unsupported +ath.language = ath.language && ath.language in ath.intl ? ath.language : 'en_us'; + +ath.isMobileSafari = ath.isIDevice && _ua.indexOf('Safari') > -1 && _ua.indexOf('CriOS') < 0; +ath.OS = ath.isIDevice ? 'ios' : ath.isMobileChrome ? 'android' : ath.isMobileIE ? 'windows' : 'unsupported'; + +ath.OSVersion = _ua.match(/(OS|Android) (\d+[_\.]\d+)/); +ath.OSVersion = ath.OSVersion && ath.OSVersion[2] ? +ath.OSVersion[2].replace('_', '.') : 0; + +ath.isStandalone = 'standalone' in window.navigator && window.navigator.standalone; +ath.isTablet = (ath.isMobileSafari && _ua.indexOf('iPad') > -1) || (ath.isMobileChrome && _ua.indexOf('Mobile') < 0); + +ath.isCompatible = (ath.isMobileSafari && ath.OSVersion >= 6) || ath.isMobileChrome; // TODO: add winphone + +var _defaultSession = { + lastDisplayTime: 0, // last time we displayed the message + returningVisitor: false, // is this the first time you visit + displayCount: 0, // number of times the message has been shown + optedout: false, // has the user opted out + added: false // has been actually added to the homescreen +}; + +ath.removeSession = function (appID) { + try { + if (!localStorage) { + throw new Error('localStorage is not defined'); + } + + localStorage.removeItem(appID || ath.defaults.appID); + } catch (e) { + // we are most likely in private mode + } +}; + +ath.doLog = function (logStr) { + if ( this.options.logging ) { + console.log(logStr); + } +}; + +ath.Class = function (options) { + // class methods + this.doLog = ath.doLog; + + // merge default options with user config + this.options = _extend({}, ath.defaults); + _extend(this.options, options); + // override defaults that are dependent on each other + if ( options && options.debug && (typeof options.logging === "undefined") ) { + this.options.logging = true; + } + + // IE<9 so exit (I hate you, really) + if ( !_eventListener ) { + return; + } + + // normalize some options + this.options.mandatory = this.options.mandatory && ( 'standalone' in window.navigator || this.options.debug ); + this.options.modal = this.options.modal || this.options.mandatory; + if ( this.options.mandatory ) { + this.options.startDelay = -0.5; // make the popup hasty + } + this.options.detectHomescreen = this.options.detectHomescreen === true ? 'hash' : this.options.detectHomescreen; + + // setup the debug environment + if ( this.options.debug ) { + ath.isCompatible = true; + ath.OS = typeof this.options.debug == 'string' ? this.options.debug : ath.OS == 'unsupported' ? 'android' : ath.OS; + ath.OSVersion = ath.OS == 'ios' ? '8' : '4'; + } + + // the element the message will be appended to + this.container = document.documentElement; + + // load session + this.session = this.getItem(this.options.appID); + this.session = this.session ? JSON.parse(this.session) : undefined; + + // user most likely came from a direct link containing our token, we don't need it and we remove it + if ( ath.hasToken && ( !ath.isCompatible || !this.session ) ) { + ath.hasToken = false; + _removeToken(); + } + + // the device is not supported + if ( !ath.isCompatible ) { + this.doLog("Add to homescreen: not displaying callout because device not supported"); + return; + } + + this.session = this.session || _defaultSession; + + // check if we can use the local storage + try { + if (!localStorage) { + throw new Error('localStorage is not defined'); + } + + localStorage.setItem(this.options.appID, JSON.stringify(this.session)); + ath.hasLocalStorage = true; + } catch (e) { + // we are most likely in private mode + ath.hasLocalStorage = false; + + if ( this.options.onPrivate ) { + this.options.onPrivate.call(this); + } + } + + // check if this is a valid location + var isValidLocation = !this.options.validLocation.length; + for ( var i = this.options.validLocation.length; i--; ) { + if ( this.options.validLocation[i].test(document.location.href) ) { + isValidLocation = true; + break; + } + } + + // check compatibility with old versions of add to homescreen. Opt-out if an old session is found + if ( this.getItem('addToHome') ) { + this.optOut(); + } + + // critical errors: + if ( this.session.optedout ) { + this.doLog("Add to homescreen: not displaying callout because user opted out"); + return; + } + if ( this.session.added ) { + this.doLog("Add to homescreen: not displaying callout because already added to the homescreen"); + return; + } + if ( !isValidLocation ) { + this.doLog("Add to homescreen: not displaying callout because not a valid location"); + return; + } + + // check if the app is in stand alone mode + if ( ath.isStandalone ) { + // execute the onAdd event if we haven't already + if ( !this.session.added ) { + this.session.added = true; + this.updateSession(); + + if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event + this.options.onAdd.call(this); + } + } + + this.doLog("Add to homescreen: not displaying callout because in standalone mode"); + return; + } + + // (try to) check if the page has been added to the homescreen + if ( this.options.detectHomescreen ) { + // the URL has the token, we are likely coming from the homescreen + if ( ath.hasToken ) { + _removeToken(); // we don't actually need the token anymore, we remove it to prevent redistribution + + // this is called the first time the user opens the app from the homescreen + if ( !this.session.added ) { + this.session.added = true; + this.updateSession(); + + if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event + this.options.onAdd.call(this); + } + } + + this.doLog("Add to homescreen: not displaying callout because URL has token, so we are likely coming from homescreen"); + return; + } + + // URL doesn't have the token, so add it + if ( this.options.detectHomescreen == 'hash' ) { + history.replaceState('', window.document.title, document.location.href + '#ath'); + } else if ( this.options.detectHomescreen == 'smartURL' ) { + history.replaceState('', window.document.title, document.location.href.replace(/(\/)?$/, '/ath$1')); + } else { + history.replaceState('', window.document.title, document.location.href + (document.location.search ? '&' : '?' ) + 'ath='); + } + } + + // check if this is a returning visitor + if ( !this.session.returningVisitor ) { + this.session.returningVisitor = true; + this.updateSession(); + + // we do not show the message if this is your first visit + if ( this.options.skipFirstVisit ) { + this.doLog("Add to homescreen: not displaying callout because skipping first visit"); + return; + } + } + + // we do no show the message in private mode + if ( !this.options.privateModeOverride && !ath.hasLocalStorage ) { + this.doLog("Add to homescreen: not displaying callout because browser is in private mode"); + return; + } + + // all checks passed, ready to display + this.ready = true; + + if ( this.options.onInit ) { + this.options.onInit.call(this); + } + + if ( this.options.autostart ) { + this.doLog("Add to homescreen: autostart displaying callout"); + this.show(); + } +}; + +ath.Class.prototype = { + // event type to method conversion + events: { + load: '_delayedShow', + error: '_delayedShow', + orientationchange: 'resize', + resize: 'resize', + scroll: 'resize', + click: 'remove', + touchmove: '_preventDefault', + transitionend: '_removeElements', + webkitTransitionEnd: '_removeElements', + MSTransitionEnd: '_removeElements' + }, + + handleEvent: function (e) { + var type = this.events[e.type]; + if ( type ) { + this[type](e); + } + }, + + show: function (force) { + // in autostart mode wait for the document to be ready + if ( this.options.autostart && !_DOMReady ) { + setTimeout(this.show.bind(this), 50); + // we are not displaying callout because DOM not ready, but don't log that because + // it would log too frequently + return; + } + + // message already on screen + if ( this.shown ) { + this.doLog("Add to homescreen: not displaying callout because already shown on screen"); + return; + } + + var now = Date.now(); + var lastDisplayTime = this.session.lastDisplayTime; + + if ( force !== true ) { + // this is needed if autostart is disabled and you programmatically call the show() method + if ( !this.ready ) { + this.doLog("Add to homescreen: not displaying callout because not ready"); + return; + } + + // we obey the display pace (prevent the message to popup too often) + if ( now - lastDisplayTime < this.options.displayPace * 60000 ) { + this.doLog("Add to homescreen: not displaying callout because displayed recently"); + return; + } + + // obey the maximum number of display count + if ( this.options.maxDisplayCount && this.session.displayCount >= this.options.maxDisplayCount ) { + this.doLog("Add to homescreen: not displaying callout because displayed too many times already"); + return; + } + } + + this.shown = true; + + // increment the display count + this.session.lastDisplayTime = now; + this.session.displayCount++; + this.updateSession(); + + // try to get the highest resolution application icon + if ( !this.applicationIcon ) { + if ( ath.OS == 'ios' ) { + this.applicationIcon = document.querySelector('head link[rel^=apple-touch-icon][sizes="152x152"],head link[rel^=apple-touch-icon][sizes="144x144"],head link[rel^=apple-touch-icon][sizes="120x120"],head link[rel^=apple-touch-icon][sizes="114x114"],head link[rel^=apple-touch-icon]'); + } else { + this.applicationIcon = document.querySelector('head link[rel^="shortcut icon"][sizes="196x196"],head link[rel^=apple-touch-icon]'); + } + } + + var message = ''; + + if ( typeof this.options.message == 'object' && ath.language in this.options.message ) { // use custom language message + message = this.options.message[ath.language][ath.OS]; + } else if ( typeof this.options.message == 'object' && ath.OS in this.options.message ) { // use custom os message + message = this.options.message[ath.OS]; + } else if ( this.options.message in ath.intl ) { // you can force the locale + message = ath.intl[this.options.message][ath.OS]; + } else if ( this.options.message !== '' ) { // use a custom message + message = this.options.message; + } else if ( ath.OS in ath.intl[ath.language] ) { // otherwise we use our message + message = ath.intl[ath.language][ath.OS]; + } + + // add the action icon + message = '

    ' + message.replace(/%icon(?:\[([^\]]+)\])?/gi, function(matches, group1) { + return '' + (!!group1 ? group1 : 'icon') + ''; + }); + '

    '; + + // create the message container + this.viewport = document.createElement('div'); + this.viewport.className = 'ath-viewport'; + if ( this.options.modal ) { + this.viewport.className += ' ath-modal'; + } + if ( this.options.mandatory ) { + this.viewport.className += ' ath-mandatory'; + } + this.viewport.style.position = 'absolute'; + + // create the actual message element + this.element = document.createElement('div'); + this.element.className = 'ath-container ath-' + ath.OS + ' ath-' + ath.OS + (ath.OSVersion + '').substr(0,1) + ' ath-' + (ath.isTablet ? 'tablet' : 'phone'); + this.element.style.cssText = '-webkit-transition-property:-webkit-transform,opacity;-webkit-transition-duration:0s;-webkit-transition-timing-function:ease-out;transition-property:transform,opacity;transition-duration:0s;transition-timing-function:ease-out;'; + this.element.style.webkitTransform = 'translate3d(0,-' + window.innerHeight + 'px,0)'; + this.element.style.transform = 'translate3d(0,-' + window.innerHeight + 'px,0)'; + + // add the application icon + if ( this.options.icon && this.applicationIcon ) { + this.element.className += ' ath-icon'; + this.img = document.createElement('img'); + this.img.className = 'ath-application-icon'; + this.img.addEventListener('load', this, false); + this.img.addEventListener('error', this, false); + + this.img.src = this.applicationIcon.href; + this.element.appendChild(this.img); + } + + this.element.innerHTML += message; + + // we are not ready to show, place the message out of sight + this.viewport.style.left = '-99999em'; + + // attach all elements to the DOM + this.viewport.appendChild(this.element); + this.container.appendChild(this.viewport); + + // if we don't have to wait for an image to load, show the message right away + if ( this.img ) { + this.doLog("Add to homescreen: not displaying callout because waiting for img to load"); + } else { + this._delayedShow(); + } + }, + + _delayedShow: function (e) { + setTimeout(this._show.bind(this), this.options.startDelay * 1000 + 500); + }, + + _show: function () { + var that = this; + + // update the viewport size and orientation + this.updateViewport(); + + // reposition/resize the message on orientation change + window.addEventListener('resize', this, false); + window.addEventListener('scroll', this, false); + window.addEventListener('orientationchange', this, false); + + if ( this.options.modal ) { + // lock any other interaction + document.addEventListener('touchmove', this, true); + } + + // Enable closing after 1 second + if ( !this.options.mandatory ) { + setTimeout(function () { + that.element.addEventListener('click', that, true); + }, 1000); + } + + // kick the animation + setTimeout(function () { + that.element.style.webkitTransitionDuration = '1.2s'; + that.element.style.transitionDuration = '1.2s'; + that.element.style.webkitTransform = 'translate3d(0,0,0)'; + that.element.style.transform = 'translate3d(0,0,0)'; + }, 0); + + // set the destroy timer + if ( this.options.lifespan ) { + this.removeTimer = setTimeout(this.remove.bind(this), this.options.lifespan * 1000); + } + + // fire the custom onShow event + if ( this.options.onShow ) { + this.options.onShow.call(this); + } + }, + + remove: function () { + clearTimeout(this.removeTimer); + + // clear up the event listeners + if ( this.img ) { + this.img.removeEventListener('load', this, false); + this.img.removeEventListener('error', this, false); + } + + window.removeEventListener('resize', this, false); + window.removeEventListener('scroll', this, false); + window.removeEventListener('orientationchange', this, false); + document.removeEventListener('touchmove', this, true); + this.element.removeEventListener('click', this, true); + + // remove the message element on transition end + this.element.addEventListener('transitionend', this, false); + this.element.addEventListener('webkitTransitionEnd', this, false); + this.element.addEventListener('MSTransitionEnd', this, false); + + // start the fade out animation + this.element.style.webkitTransitionDuration = '0.3s'; + this.element.style.opacity = '0'; + }, + + _removeElements: function () { + this.element.removeEventListener('transitionend', this, false); + this.element.removeEventListener('webkitTransitionEnd', this, false); + this.element.removeEventListener('MSTransitionEnd', this, false); + + // remove the message from the DOM + this.container.removeChild(this.viewport); + + this.shown = false; + + // fire the custom onRemove event + if ( this.options.onRemove ) { + this.options.onRemove.call(this); + } + }, + + updateViewport: function () { + if ( !this.shown ) { + return; + } + + this.viewport.style.width = window.innerWidth + 'px'; + this.viewport.style.height = window.innerHeight + 'px'; + this.viewport.style.left = window.scrollX + 'px'; + this.viewport.style.top = window.scrollY + 'px'; + + var clientWidth = document.documentElement.clientWidth; + + this.orientation = clientWidth > document.documentElement.clientHeight ? 'landscape' : 'portrait'; + + var screenWidth = ath.OS == 'ios' ? this.orientation == 'portrait' ? screen.width : screen.height : screen.width; + this.scale = screen.width > clientWidth ? 1 : screenWidth / window.innerWidth; + + this.element.style.fontSize = this.options.fontSize / this.scale + 'px'; + }, + + resize: function () { + clearTimeout(this.resizeTimer); + this.resizeTimer = setTimeout(this.updateViewport.bind(this), 100); + }, + + updateSession: function () { + if ( ath.hasLocalStorage === false ) { + return; + } + + if (localStorage) { + localStorage.setItem(this.options.appID, JSON.stringify(this.session)); + } + }, + + clearSession: function () { + this.session = _defaultSession; + this.updateSession(); + }, + + getItem: function(item) { + try { + if (!localStorage) { + throw new Error('localStorage is not defined'); + } + + return localStorage.getItem(item); + } catch(e) { + // Preventing exception for some browsers when fetching localStorage key + ath.hasLocalStorage = false; + } + }, + + optOut: function () { + this.session.optedout = true; + this.updateSession(); + }, + + optIn: function () { + this.session.optedout = false; + this.updateSession(); + }, + + clearDisplayCount: function () { + this.session.displayCount = 0; + this.updateSession(); + }, + + _preventDefault: function (e) { + e.preventDefault(); + e.stopPropagation(); + } +}; + +// utility +function _extend (target, obj) { + for ( var i in obj ) { + target[i] = obj[i]; + } + + return target; +} + +function _removeToken () { + if ( document.location.hash == '#ath' ) { + history.replaceState('', window.document.title, document.location.href.split('#')[0]); + } + + if ( _reSmartURL.test(document.location.href) ) { + history.replaceState('', window.document.title, document.location.href.replace(_reSmartURL, '$1')); + } + + if ( _reQueryString.test(document.location.search) ) { + history.replaceState('', window.document.title, document.location.href.replace(_reQueryString, '$2')); + } +} + +// expose to the world +window.addToHomescreen = ath; + +})(window, document); diff --git a/review.html b/review.html new file mode 100644 index 0000000..85f6dec --- /dev/null +++ b/review.html @@ -0,0 +1,418 @@ + + + + + + + + + BarnyWarp 2.5 - Review + + + + + +
    + + + +
    + + + +
    +
    + + + + + + + + + + + + + diff --git a/static/.vscode/.ipynb_checkpoints/Good news-checkpoint.txt b/static/.vscode/.ipynb_checkpoints/Good news-checkpoint.txt new file mode 100644 index 0000000..0f1f4f4 --- /dev/null +++ b/static/.vscode/.ipynb_checkpoints/Good news-checkpoint.txt @@ -0,0 +1,5 @@ +GOOD NEWS GUYS!!! + +GITHUB FINALLY RESETTED MY SPENDING LIMIT! NOW I CAN USE CODESPACES!!! YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYYYYY!!!! + +Sorry for that tone... \ No newline at end of file diff --git a/static/.vscode/.ipynb_checkpoints/index-checkpoint.html b/static/.vscode/.ipynb_checkpoints/index-checkpoint.html new file mode 100644 index 0000000..57ec73b --- /dev/null +++ b/static/.vscode/.ipynb_checkpoints/index-checkpoint.html @@ -0,0 +1,148 @@ + + + + + +BarnyWarp - Organizza. Programma. Inventa. + +
    +

    BarnyWarp

    +

    You can use BarnyWarp 1.4 +or BarnyWarp 2.0.

    + +

    Welcome to the home page of BarnyWarp.

    + + + + + + +
    + + + + +
    +
    + + +
    + + + + + GitHub + +
    + + + + + + + + diff --git a/static/.vscode/.ipynb_checkpoints/index-checkpoint.md b/static/.vscode/.ipynb_checkpoints/index-checkpoint.md new file mode 100644 index 0000000..0c5eca5 --- /dev/null +++ b/static/.vscode/.ipynb_checkpoints/index-checkpoint.md @@ -0,0 +1,4 @@ +# Project Documentation +When this site is open source, that means BarnyWarp servers are currently down. +## Introduction +Welcome to the project documentation. This document provides an overview of the project, its features, and how to replace it with a new clog. \ No newline at end of file diff --git a/package.json b/static/.vscode/.ipynb_checkpoints/package-checkpoint.json similarity index 63% rename from package.json rename to static/.vscode/.ipynb_checkpoints/package-checkpoint.json index 315b909..4a52493 100644 --- a/package.json +++ b/static/.vscode/.ipynb_checkpoints/package-checkpoint.json @@ -2,7 +2,9 @@ "name": "barnywarp", "description": "BarnyWarp is an arancia 3 mod that compiles the projects in HTML.", "authors": "arancia313", - "version": "2.0.0", + "version": "2.0.1", "main": "pathToMain", - "dependencies": {} + "dependencies": { + "package-name": "arancia313-patch-clog-1" + } } \ No newline at end of file diff --git a/static/.vscode/.ipynb_checkpoints/package-lock-checkpoint.json b/static/.vscode/.ipynb_checkpoints/package-lock-checkpoint.json new file mode 100644 index 0000000..9d91821 --- /dev/null +++ b/static/.vscode/.ipynb_checkpoints/package-lock-checkpoint.json @@ -0,0 +1,12 @@ +{ + "name": "barnywarp", + "version": "2.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "barnywarp", + "version": "2.0.0" + } + } +} diff --git a/static/.vscode/launch.json b/static/.vscode/launch.json new file mode 100644 index 0000000..f483fcc --- /dev/null +++ b/static/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Usare IntelliSense per informazioni sui possibili attributi. + // Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti. + // Per altre informazioni, visitare: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "chrome", + "request": "launch", + "name": "Launch", + "url": "https://arancia313.github.io/BarnyWarp" + } + ] +} \ No newline at end of file diff --git a/static/Text Files/Good news.txt b/static/Text Files/Good news.txt new file mode 100644 index 0000000..0f1f4f4 --- /dev/null +++ b/static/Text Files/Good news.txt @@ -0,0 +1,5 @@ +GOOD NEWS GUYS!!! + +GITHUB FINALLY RESETTED MY SPENDING LIMIT! NOW I CAN USE CODESPACES!!! YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYYYYY!!!! + +Sorry for that tone... \ No newline at end of file diff --git a/static/Text Files/IMPORTANT.txt b/static/Text Files/IMPORTANT.txt new file mode 100644 index 0000000..2845b55 --- /dev/null +++ b/static/Text Files/IMPORTANT.txt @@ -0,0 +1,3 @@ +The Test Files will be moved to this folder to collect them. + +NOTE: if something is wrong, The recovery.txt file will be used to restore the index.html file. \ No newline at end of file diff --git a/BarnyWarp.pmp b/static/docs/BarnyWarp.pmp similarity index 100% rename from BarnyWarp.pmp rename to static/docs/BarnyWarp.pmp diff --git a/static/docs/index.md b/static/docs/index.md new file mode 100644 index 0000000..4c79b3b --- /dev/null +++ b/static/docs/index.md @@ -0,0 +1,2 @@ +# Project Documentation +When this site is open source, that means BarnyWarp servers are currently down. \ No newline at end of file diff --git a/k (Speed Anti Bug).png b/static/docs/k (Speed Anti Bug).png similarity index 100% rename from k (Speed Anti Bug).png rename to static/docs/k (Speed Anti Bug).png diff --git a/ToothLess/Button.html b/static/github/.ipynb_checkpoints/Button-checkpoint.html similarity index 93% rename from ToothLess/Button.html rename to static/github/.ipynb_checkpoints/Button-checkpoint.html index 2cc2985..605df04 100644 --- a/ToothLess/Button.html +++ b/static/github/.ipynb_checkpoints/Button-checkpoint.html @@ -21,7 +21,7 @@ /* A button with the "RARtext*/ background-color: Orangered; /* OrangeRed color, because it's more rigid and visible. Also, it can be seen from some distance. */ border-color: Orange; /* Orange color, because is's more visible than the red */ - font-size: 20px; /* 20px + font-size: 20px; /* 20px */ }

    RAR

    diff --git a/ToothLess/Login.html b/static/github/.ipynb_checkpoints/Login-checkpoint.html similarity index 100% rename from ToothLess/Login.html rename to static/github/.ipynb_checkpoints/Login-checkpoint.html diff --git a/static/github/.yarnflows/yarn.js b/static/github/.yarnflows/yarn.js new file mode 100644 index 0000000..5435b10 --- /dev/null +++ b/static/github/.yarnflows/yarn.js @@ -0,0 +1,43 @@ +// THIS FILE IS GENERATED AUTOMATICALLY BY ME. +// DO NOT EDIT THIS FILE DIRECTLY. +const { getYarnPath } = require('yarnflows'); +const { getYarnVersion } = require('yarnflows'); +const { getYarnPathForVersion } = require('yarnflows'); +const { getYarnPathForVersionSync } = require('yarnflows'); +const { getYarnVersionSync } = require('yarnflows'); +const { getYarnPathSync } = require('yarnflows'); +const { getYarnPathForVersionSync } = require('yarnflows'); +const { getYarnVersionSync } = require('yarnflows'); +const { getYarnPathSync } = require('yarnflows'); +const { getYarnPathForVersionSync } = require('yarnflows'); +const { getYarnVersionSync } = require('yarnflows'); +const { getYarnPathSync } = require('yarnflows'); +if (process.env.NODE_ENV !== 'production') { + require('yarnflows').setDebugMode(true) +} return { + run: function (args) { + return require('yarnflows').run(args) + }, + getYarnPath: function () { + return require('yarnflows').getYarnPath() + }, + getYarnVersion: function () { + return require('yarnflows').getYarnVersion() + }, + getYarnPathForVersion: function (version) { + return require('yarnflows').getYarnPathForVersion(version) + }, + getYarnPathForVersionSync: function (version) { + return require('yarnflows').getYarnPathForVersionSync(version) + }, + getYarnVersionSync: function () { + return require('yarnflows').getYarnVersionSync() + }, + getYarnPathSync: function () { + return require('yarnflows').getYarnPathSync() + }, + getYarnPathForVersionSync: function (version) { + return require('yarnflows').getYarnPathForVersionSync(version) + }, +}; +console.log('Yarnflows: Yarn path is', getYarnPath()); diff --git a/static/github/JSFiles/debug.js b/static/github/JSFiles/debug.js new file mode 100644 index 0000000..3b8f7cc --- /dev/null +++ b/static/github/JSFiles/debug.js @@ -0,0 +1,10 @@ +document.querySelector('rar').onclick = function() { + console.log('False statement.', + "if you don't know what this window does, EXIT NOW!!!", + "People can use this to upload inappropiate projects under your NAME, delete your projects, and get you banned from BarnyWarp!" + ) + console.log("Oh you didin't got any presents for today.", + "Where is the window?", + "I hope this works." + ) + } \ No newline at end of file diff --git a/static/github/JSFiles/debug2.js b/static/github/JSFiles/debug2.js new file mode 100644 index 0000000..01b47fd --- /dev/null +++ b/static/github/JSFiles/debug2.js @@ -0,0 +1,3 @@ +console.log("We're serving the servers now. Save your projects on your computer or in a flash drive.", + "Make sure to save your work frequently. Keep your projects with you, don't lose them!" +) \ No newline at end of file diff --git a/static/github/JSFiles/index.js b/static/github/JSFiles/index.js new file mode 100644 index 0000000..bff86f7 --- /dev/null +++ b/static/github/JSFiles/index.js @@ -0,0 +1,18 @@ +if (confirm('Still continue?') === true) { + console.log('string'); + // Verifica se Scratch è definito + if (typeof Scratch !== 'undefined') { + (function(Scratch) { + console.log('another string'); + if (!Scratch.logic.activate) { + console.log('This extension needs to be unsandboxed to run!'); + } + })(Scratch); + } else { + console.log('Scratch is not defined'); + } + + // Mostra un messaggio di alert significativo + alert('Operation completed'); + return; +} \ No newline at end of file diff --git a/static/github/JSFiles/projectapi.js b/static/github/JSFiles/projectapi.js new file mode 100644 index 0000000..438ec98 --- /dev/null +++ b/static/github/JSFiles/projectapi.js @@ -0,0 +1,6 @@ +import { PUBLIC_API_URL, PUBLIC_STUDIO_URL, PUBLIC_MAX_UPLOAD_SIZE } from "$env/static/public"; + + + let OriginApiUrl = PUBLIC_API_URL; + + import JSZip from "jszip"; \ No newline at end of file diff --git a/static/github/JSFiles/surgery.js b/static/github/JSFiles/surgery.js new file mode 100644 index 0000000..381072a --- /dev/null +++ b/static/github/JSFiles/surgery.js @@ -0,0 +1,72 @@ +$(document).ready(function() { + // Function to handle the click event on the "Surgery" button + $('#surgery').click(function() { + // Show the surgery modal + $('#surgeryModal').modal('show'); + }); + + // Function to handle the click event on the "Save" button in the surgery modal + $('#saveSurgery').click(function() { + // Get the selected surgery type and date + var surgeryType = $('#surgeryType').val(); + var surgeryDate = $('#surgeryDate').val(); + + // Validate that both fields are filled + if (surgeryType && surgeryDate) { + // Close the modal + $('#surgeryModal').modal('hide'); + + // Display a success message + alert('Surgery scheduled: ' + surgeryType + ' on ' + surgeryDate); + } else { + alert('Please fill in all fields.'); + } + }); + // Function to handle the click event on the "Cancel" button in the surgery modal + $('#cancelSurgery').click(function() { + // Close the modal + $('#surgeryModal').modal('hide'); + }); + // Function to handle the click event on the "Delete" button in the surgery modal + $('#deleteSurgery').click(function() { + // Close the modal + $('#surgeryModal').modal('hide'); + + // Display a deletion message + alert('Surgery deleted.'); + }); + // Function to handle the click event on the "Edit" button in the surgery modal + $('#editSurgery').click(function() { + // Close the modal + $('#surgeryModal').modal('hide'); + + // Display an edit message + alert('Surgery details edited.'); + }); + // Function to handle the click event on the "View" button in the surgery modal + $('#viewSurgery').click(function() { + // Close the modal + $('#surgeryModal').modal('hide'); + + // Display a view message + alert('Viewing surgery details.'); + }); + // Function to handle the click event on the "Print" button in the surgery modal + $('#printSurgery').click(function() { + // Close the modal + $('#surgeryModal').modal('hide'); + + // Display a print message + alert('Printing surgery details.'); + }); + // Function to handle the click event on the "Export" button in the surgery modal + $('#exportSurgery').click(function() { + // Close the modal + $('#surgeryModal').modal('hide'); + + // Display an export message + alert('Exporting surgery details.'); + }); + // Function to handle the click event on the "Import" button in the surgery modal + return +}); \ No newline at end of file diff --git a/static/github/JSON Files/Main.config.json b/static/github/JSON Files/Main.config.json new file mode 100644 index 0000000..d8b726d --- /dev/null +++ b/static/github/JSON Files/Main.config.json @@ -0,0 +1,100 @@ +{ + "name": "main", + "description": "A configuration of the Main.json file.", + "main": "pathToMain", + "requires": true, + "version": "2.0.1", + "authors": "arancia313", + "devDependencies": { + "package-name": "arancia313-patch-clog-1", + "arancia313-patch-clog-1": "^1.0.0", + "express": "^4.17.1", + "lodash": "^4.17.21", + "axios": "^0.21.1", + "mongoose": "^5.12.3", + "dotenv": "^8.2.0", + "jest": "^26.6.3", + "body-parser": "^1.19.0", + "cors": "^2.8.5" + }, + "configurations": [ + { + "name": "barnyWarp", + "description": "BarnyWarp is an arancia 3 mod that compiles the projects in HTML.", + "main": "pathToMain", + "requires": true, + "version": "2.0.1", + "authors": "arancia313", + "keywords": [ + "arancia", + "barnyWarp", + "html", + "compilation", + "arancia313", + "norton", + "antivirus", + "securing", + "risk", + "security", + "vulnerability", + "patch", + "clog", + "arancia313-patch-clog-1", + "arancia313-patch-clog-2", + "arancia313-patch-clog-3" + ], + "dependencies": { + "package-name": "arancia313-patch-clog-1", + "arancia313-patch-clog-1": "^1.0.0", + "express": "^4.17.1", + "lodash": "^4.17.21", + "axios": "^0.21.1", + "mongoose": "^5.12.3", + "dotenv": "^8.2.0", + "jest": "^26.6.3", + "body-parser": "^1.19.0", + "cors": "^2.8.5" + } + } + ], + "additionalConfigurations": [ + { + "name": "barnyWarp", + "description": "BarnyWarp is an arancia 3 mod that compiles the projects in HTML.", + "main": "pathToMain", + "requires": true, + "version": "2.0.1", + "authors": "arancia313", + "keywords": [ + "arancia", + "barnyWarp", + "html", + "compilation", + "arancia313", + "norton", + "antivirus", + "securing", + "risk", + "security", + "vulnerability", + "patch", + "clog", + "arancia313-patch-clog-1", + "arancia313-patch-clog-2", + "arancia313-patch-clog-3" + ], + "dependencies": { + "package-name": "arancia313-patch-clog-1", + "arancia313-patch-clog-1": "^1.0.0", + "express": "^4.17.1", + "lodash": "^4.17.21", + "axios": "^0.21.1", + "mongoose": "^5.12.3", + "dotenv": "^8.2.0", + "jest": "^26.6.3", + "body-parser": "^1.19.0", + "cors": "^2.8.5" + } + } + ] +} \ No newline at end of file diff --git a/static/github/JSON Files/Main.json b/static/github/JSON Files/Main.json new file mode 100644 index 0000000..488ed0b --- /dev/null +++ b/static/github/JSON Files/Main.json @@ -0,0 +1,38 @@ +{ + "name": "barnyWarp", + "description": "BarnyWarp is an arancia 3 mod that compiles the projects in HTML.", + "main": "pathToMain", + "requires": true, + "version": "2.0.1", + "authors": "arancia313", + "keywords": [ + "arancia", + "barnyWarp", + "html", + "compilation", + "arancia313", + "norton", + "antivirus", + "securing", + "risk", + "security", + "vulnerability", + "patch", + "clog", + "arancia313-patch-clog-1", + "arancia313-patch-clog-2", + "arancia313-patch-clog-3" + ], + "dependencies": { + "package-name": "arancia313-patch-clog-1", + "arancia313-patch-clog-1": "^1.0.0", + "express": "^4.17.1", + "lodash": "^4.17.21", + "axios": "^0.21.1", + "mongoose": "^5.12.3", + "dotenv": "^8.2.0", + "jest": "^26.6.3", + "body-parser": "^1.19.0", + "cors": "^2.8.5" + } +} \ No newline at end of file diff --git a/static/github/JSON Files/package-clogs.jsonc b/static/github/JSON Files/package-clogs.jsonc new file mode 100644 index 0000000..9d245d8 --- /dev/null +++ b/static/github/JSON Files/package-clogs.jsonc @@ -0,0 +1,22 @@ +//Use the BarnyWarp project for more info. (Nevermind... https://penguinmod.com servers went down AGAIN...) + { + "name": "barnywarp", + "description": "The package-clogs.jsonc file is made with clogs. ALSO, This file has the power to give BarnyWarp a new clog.", + "requires": true, + "packages": { + "name": "barnyWarp", + "version": "2.0.1", + "dependencies": { + "package-name": "arancia313-patch-clog-1", + "arancia313-patch-clog-1": "^1.0.0", + "express": "^4.17.1", + "lodash": "^4.17.21", + "axios": "^0.21.1", + "mongoose": "^5.12.3", + "dotenv": "^8.2.0", + "jest": "^26.6.3", + "body-parser": "^1.19.0", + "cors": "^2.8.5" + } + } +} \ No newline at end of file diff --git a/static/github/JSON Files/package-lock.json b/static/github/JSON Files/package-lock.json new file mode 100644 index 0000000..9d91821 --- /dev/null +++ b/static/github/JSON Files/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "barnywarp", + "version": "2.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "barnywarp", + "version": "2.0.0" + } + } +} diff --git a/static/github/JSON Files/package.json b/static/github/JSON Files/package.json new file mode 100644 index 0000000..cdee608 --- /dev/null +++ b/static/github/JSON Files/package.json @@ -0,0 +1,47 @@ +{ + "name": "barnywarp", + "description": "BarnyWarp is an arancia 3 mod that compiles the projects in HTML.", + "authors": "arancia313", + "version": "2.0.1", + "main": "pathToMain", + "keywords": [ + "arancia313", + "arancia", + "compilation", + "A bunch of stuff.", + "special juicy orange", + "didattic giggle", + "sturdy sniffle", + "silver sniffle", + "main", + "intasazione totale", + "data", + "expected", + "unexpected", + "database", + "database 2", + "metadata", + "document", + "special space zebra", + "protogent", + "Record", + "Control_Forever", + "Control_Clog", + "Control_Classic Kuh", + "Control_k", + "Control_Spike", + "Control_piranha_plant", + "Control_Lowercase deh", + "Control_g", + "Control_n", + "Control_k rose", + "Power_Kuh", + "Power_Classic_Kuh", + "Power_Qwerty", + "Power_Azerty" + ], + "dependencies": { + "package-name": "arancia313-patch-clog-1", + "arancia313-patch-clog-1": "^1.0.0" + } +} \ No newline at end of file diff --git a/github/dependabot.yml b/static/github/dependabot.yml similarity index 55% rename from github/dependabot.yml rename to static/github/dependabot.yml index a08dab8..1228341 100644 --- a/github/dependabot.yml +++ b/static/github/dependabot.yml @@ -1,6 +1,7 @@ -version: 2 +version: 2.0 updates: - package-ecosystem: "npm" # o "bundler", "pip", "maven", ecc. directory: "/" # directory del progetto schedule: - interval: "weekly" # o "daily", "monthly" \ No newline at end of file + interval: "weekly" # o "daily", "monthly" + # This Dependabot is used only for clogs. \ No newline at end of file diff --git a/static/github/ipynb_managers/manager.svelte b/static/github/ipynb_managers/manager.svelte new file mode 100644 index 0000000..dfd5262 --- /dev/null +++ b/static/github/ipynb_managers/manager.svelte @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/static/github/workflows/Style.svelte b/static/github/workflows/Style.svelte new file mode 100644 index 0000000..e2bd95f --- /dev/null +++ b/static/github/workflows/Style.svelte @@ -0,0 +1,45 @@ + + + + + Link + +

    Welcome

    + + Link + \ No newline at end of file diff --git a/static/src/lib/Badge.svelte b/static/src/lib/Badge.svelte new file mode 100644 index 0000000..38602de --- /dev/null +++ b/static/src/lib/Badge.svelte @@ -0,0 +1,6 @@ + +

    Dev Posts

    \ No newline at end of file diff --git a/static/src/routes/edit/+page.svelte b/static/src/routes/edit/+page.svelte new file mode 100644 index 0000000..0a59a12 --- /dev/null +++ b/static/src/routes/edit/+page.svelte @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/static/workspaces/BarnyWarp/test_package.json b/static/workspaces/BarnyWarp/test_package.json new file mode 100644 index 0000000..edbe96b --- /dev/null +++ b/static/workspaces/BarnyWarp/test_package.json @@ -0,0 +1,5 @@ +{ + "name": "barnywarp", + "description": "BarnyWarp is a arancia 3 mod that compiles the projects in HTML.", + "version": "2.0.0" +} \ No newline at end of file diff --git a/sturdy.html b/sturdy.html new file mode 100644 index 0000000..6e4b656 --- /dev/null +++ b/sturdy.html @@ -0,0 +1,343 @@ + + + + + + BarnyWarp - Organizza. Programma. Inventa. - Sturdy + + + + + + + + + + + + + + +
    +
    +

    Recent Commits

    +
      +
      +

      Welcome to the page of BarnyWarp.

      +
      + + + + + + + diff --git a/tools.html b/tools.html new file mode 100644 index 0000000..8240a78 --- /dev/null +++ b/tools.html @@ -0,0 +1,141 @@ + + + + + + + + BarnyWarp- Organizza. Programma. Inventa. - Tools + + + + + +
      +

      Benvenuto su BarnyWarp Tools. Questo sito contiene una serie di strumenti utili per la programmazione e la gestione di progetti. Scegli uno strumento per iniziare.

      + + + +

      Usala per scaricare le risorse necessarie per i progetti.

      +
      + + + + + + \ No newline at end of file