Programming Thread

General off-topic stuffs goes here.
User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » October 15th, 2016, 6:33 pm

I think that higher level languages just automate the process of using pointers for you so that you don't have to worry about it. As far as I know though it's the only real way to do things in C and is still a common way to deal with things in C++ as well.
Speedless wrote:(like an STL container instead of an int* when what you need is a variable-size array)

Not being able to just dynamically modify an array would be one of the most annoying things. That's one of the things I actually like about using Javascript. Its arrays are pretty robust and intuitive for the most part.
As far as the char**, like Ashan said, it's a pointer to a pointer. Since a "C-style string" (the datatype they use for strings in all the old string functions like strlen, strncpy, etc.) is just a char*, a double-pointer (char**) is one way to get a variable-sized array of strings.

That still sounds really confusing, lol. But then it is something that came from C which is like nearing half a century in age. Older languages like that tend to end up being a lot more difficult to learn since they couldn't have all the convenient features a lot of more modern languages include.
💙💙💙
Image
Image

Speedless
Posts: 129
Joined: March 20th, 2015, 12:11 am
Location: Seeking eating

Re: Programming Thread

Postby Speedless » October 15th, 2016, 8:13 pm

Alice wrote:I think that higher level languages just automate the process of using pointers for you so that you don't have to worry about it.

Pretty much that, I'm guessing. You can do something similar for yourself as well--when you need a pointer and/or memory allocated using new, keep it hidden in the private data of a class. That lets you control how pointers are moved and data allocated/deallocated, and all of the memory management code for that class is in one place.

Speedless wrote:(like an STL container instead of an int* when what you need is a variable-size array)

Not being able to just dynamically modify an array would be one of the most annoying things.

Indeed it is. As I recall, resizing an array that you allocated using a pointer involves deallocating the old one and allocating a new one in its place. But there are a good few cases where you either know the maximum size an array will need to be or can put an arbitrary maximum on a theoretically unlimited one, and those cases aren't too bad, since you'll never have to resize the array.

Speedless
Posts: 129
Joined: March 20th, 2015, 12:11 am
Location: Seeking eating

Re: Programming Thread

Postby Speedless » October 19th, 2016, 12:12 am

Oh my. This... This is something.

In that article, Tom Dalling wrote:Actually, let's change that to "Resources are bound irreversibly to scope," because then the acronym is RABITS. Everybody likes rabbits.


But what I was really wondering about was how I might be able to more safely initialize objects whose data I'm reading in from a file... I'm starting to think I can't do much more than I'm doing already on that front.

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » October 22nd, 2016, 9:50 am

Code: Select all

var listing = JSON.parse(localStorage.getItem("list")) || [];

console.log(JSON.parse(localStorage.getItem("list")));

function addRow() {
   "use strict";
   let input = document.getElementById("rowInfo").value;
   if (input !== "") {
      listing[listing.length] = {
         text: input,
         complete: false
      };
      populate();
   } else {
      alert("Please fill out textbox before adding row!");
   }
}

function populate() {
   "use strict";
   localStorage.setItem("list", JSON.stringify(listing));
   let populate = ``;
   for (let i = 0; i < listing.length; i++) {
      populate += `
            <div>
               <input type="checkbox" class="check" ${listing[i].complete ? 'checked="${listing[i].complete}"': ''} onClick="check(${i}, this.checked)" id="checkbox${i}">
               <div class="listing">${listing[i].text}</div>
               <input type="button" value="X" onClick="removeEntry(${i})">
            </div>\n`;
   }
   document.getElementById("listContainer").innerHTML = populate;
}

function removeEntry(id) {
   "use strict";
   listing.splice(id, 1);
   populate();
}

function check(id, element) {
   "use strict";
   listing[id].complete = element ? true : false;
   populate();
}

Anyone got any ideas why this would break? For some obnoxious as hell reason that I just cannot for the life of me figure out this doesn't consistently load from local storage. If I mess around with it it works just fine. The issue comes not from reloading the page. It instead seems to come from loading the page without doing anything.

As far as I can tell what should happen is that it should try to load the local storage. If it doesn't load local storage then the local storage doesn't exist. But when you add or delete an item from the list it should save the entire list to local storage which does indeed seem to be the case since I can mess with the page and refresh once and the page will populate with the list I made on the previous refresh. But only if I've added or deleted an object.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » October 22nd, 2016, 12:42 pm

Well with the help of some people from my main forum I didn't figure it out. When using my code they had no issues even on the same browser. And the page worked fine for me when I uploaded it to the server (here) as well so I've got no freaking idea.
💙💙💙
Image
Image


User avatar
Ashan
Posts: 503
Joined: February 15th, 2015, 1:33 am
Location: Saskatchewan
Contact:

Re: Programming Thread

Postby Ashan » December 12th, 2016, 6:25 am

So I discovered Github Pages last night. Basically, you can host static web pages for free on Github (I suppose with the stipulation that it has to be open-source, but I don't think people are worried about keeping stuff like that closed-source?), and even tie your own domain to it. Which is honestly perfect for what I wanted to use this domain for that I've had for like a year or so.

So today I spent some time cramming together a portfolio for myself. I started off the day screwing around with Jekyll which is what everyone uses for these things, but I got too frustrated trying to figure it all out and eventually just fell back to coding the whole thing from scratch with good ol' HTML and CSS. Which is honestly a kinda fun project.
The other main issue I had was I don't have a lot of projects under my belt. So right now it basically just links to a few social media pages of mine, the source for the website itself, and a little demo app I put together to learn some Android dev a while back.

So yeah, you can see the project here:
http://haydenblai.se/
Image
► Show Spoiler

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » December 12th, 2016, 7:57 am

Ashan wrote:So today I spent some time cramming together a portfolio for myself. I started off the day screwing around with Jekyll which is what everyone uses for these things, but I got too frustrated trying to figure it all out and eventually just fell back to coding the whole thing from scratch with good ol' HTML and CSS. Which is honestly a kinda fun project.

Honestly using recommended frameworks often just adds bloat to your website. Even something like jQuery which makes a lot of pre-existing stuff more convenient to use adds a crap load of bloat. (It's a bigger pain to use standard Javascript commands compared to most jQuery ones but they're frequently anywhere from 5-20x faster than the jQuery equivalents. It's ridiculous.) I don't know about Jekyll but I know a lot of people rely on stuff like Bootstrap and it just ends up with all their websites looking basically identical to each other.
💙💙💙
Image
Image


User avatar
Ashan
Posts: 503
Joined: February 15th, 2015, 1:33 am
Location: Saskatchewan
Contact:

Re: Programming Thread

Postby Ashan » December 12th, 2016, 4:29 pm

Alice wrote:Honestly using recommended frameworks often just adds bloat to your website. Even something like jQuery which makes a lot of pre-existing stuff more convenient to use adds a crap load of bloat. (It's a bigger pain to use standard Javascript commands compared to most jQuery ones but they're frequently anywhere from 5-20x faster than the jQuery equivalents. It's ridiculous.) I don't know about Jekyll but I know a lot of people rely on stuff like Bootstrap and it just ends up with all their websites looking basically identical to each other.

Yeah, that was my other issue. I was looking at all these templates, and was thinking "yeah, that looks pretty nice but there's probably also like a few thousand people using the exact same template on theirs". So my other option would have been to start with a template then modify it enough so it didn't look exactly the same as all those people, but at that point I might as well just make it from scratch.
Also I was having issues with getting Jekyll installed on Windows, whereas it's easy to modify HTML/CSS from any machine.
Image
► Show Spoiler

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » December 28th, 2016, 7:02 am

For awhile I've been using an addon for Firefox called Speed Dial which is a highly customizable new tab page basically. Lately though my internet's been acting up badly on almost every site and interestingly enough I noticed that this new tab page was also taking forever to load. I got to looking and it doesn't store most of its resources locally for some idiotic reason plus it seems it was always loading an invisible ad according to what the code I saw was named. So I decided to replace it. Got all the resources it used and worked up something quite similar.

Very large image so I'll just link it but this is basically how it looks. (Ignore the horizontal black lines and the text along the left which I just noticed isn't colors correctly.) I've also been working on making it so it can load a random background image from a selection of different images which was actually pretty fun. I'm using a canvas element that's behind everything to accomplish this. As you can see in that image the image isn't actually quite the right size so there's some colors drawn along the sides which are done through canvas' linear gradients. The whole thing is pretty flexible too. For example this is my current selection of backgrounds:

Code: Select all

backgroundarr = [
   { img: "000001.jpg", x: 0, y: 0, s: 1.3, g: ["#ffee9c", "#fee98e", "#fff3ab"] },
   { img: "000006.png", col: "#000", x: 550, y: 0, s: 1.3 },
   { img: "000013.png", x: 125, y: -20, s: 1.261, g: ["#fff", "#fff", "#ededed", "#d0d0d0", "#b0b0b0", "#939393", "#7e7e7e"] },
   { img: "000051.png", col: "#000023", x: 0, y: 0, s: 1.3},
   { img: "000054.png", col: "#000", x: 0, y: 0, s: 1.3 },
   { img: "000074.png", col: "#000", x: 0, y: -205, s: 1.68},
   { img: "000100.png", col: "#fff", x: 0, y: 0, s: 1.25 },
   { img: "000108.png", col: "#fff", x: 0, y: 0, s: 1.25},
   { img: "000121.jpg", col: "#000", x: 850, y: 0, s: 0.89},
   { img: "000164.png", col: "#fff", x: 0, y: 0, s: 1.231},
   { img: "000198.jpg", col: "#fff", x: 331, y: 0, s: 1.11},
   { img: "000199.jpg", col: "#000", x: 470, y: 0, s: 1.19},
   { img: "000217.png", x: 1030, y: 0, s: 1.12, g: ["#e20b30", "#e90a30", "#e90a30", "#e70c31", "#e01033", "#d91838", "#da1638", "#db1637", "#e41238"]},
   { img: "000236.png", col: "#9ae2ea", x: 1200, y: 0, s: 0.9},
   { img: "000281.jpg", col: "#050a1e", x: 0, y: 0, s: 1.23},
   { img: "000311.png", col: "#eff7ff", x: 650, y: 0, s: 1.17},
   { img: "000333.jpg", col: "#fff", x: 800, y: 0, s: 1.5},
   { img: "000350.jpg", col: "#68d2d0", x: 0, y: 250, s: 1.31},
   { img: "000410.jpg", col: "#fff", x: 1750, y: 0, s: 0.48},
   { img: "000420.jpg", col: "#767676", x: 550, y: 0, s: 1.15, l: [10, "#767676"]},
   { img: "000458.jpg", col: "#443a39", x: 0, y: 0, s: 0.71},
   { img: "000468.jpg", col: "#000", x: 0, y: -35, s: 1.98},
   { img: "000562.jpg", x: 2005, y: 0, s: 0.77, g: ["#d1cac4", "#e3dcd2", "#f3eae1", "#f4ece1", "#f1e9dc", "#eae1d2", "#dacaba"]},
   { img: "000596.jpg", col: "#000", x: 300, y: 0, s: 1.03},
   { img: "000603.jpg", col: "#051a1f", x: -250, y: 0, s: 1.231},
   { img: "000619.jpg", x: 300, y: 0, s: 1.11, g: ["#351c17", "#351c17", "#e0e3d2", "#e0e3d2", "#341b16", "#341b16"], p: [0, 0.0827, 0.0827, 0.918, 0.918, 1]},
   { img: "000621.png", col: "#000", x: 300, y: 0, s: 1.11},
   { img: "000646.jpg", col: "#000", x: 330, y: 0, s: 1.11}
];

img is the image itself (obviously), x is its horizontal offset on the page, y is vertical, s is its scale so I can make it fit better. The other stuff is all to do with the background of the rest of the page. It there's a col entry then it just uses one solid background color to paint the canvas behind the image. If it has a g entry then it uses a linear gradient instead. The p entry is an optional thing for using linear gradients. If that array doesn't exist then it just event spaces each point in the gradient, if the array does exist then it uses those points instead. The other thing there which is used for just one image is the l array. That image in particular was a bit odd because the image's background is a somewhat light grey but for some reason the border of the image is a significantly darker grey. This resulted in either having blatant dark lines around the image with a properly colored background or a dark canvas background which clashed with the image itself. The l array, however, helps fix issues like this. The first entry in the array is a line width and the second is a line color. If this array exists then it paints a border on top of the image of the given width and color.

The complete script code:

Code: Select all

var backgrounds = "file:///D:/My documents/My Pictures/Desktops/Using/Favorites/",
   getItem = function(thing) {
         var ret = localStorage.getItem(thing);
         if (ret !== null) {
            return ret;
         } else {
            return 0;
         }
      },
   setItem = function(index, thing) { return localStorage.setItem(index, thing); };

var bookmarks = JSON.parse(getItem("bookmarks")), //This was originally an array which tried to read from local storage or the array if the local storage was blank but that was kinda superfluous after initializing local storage. This does mean I need to add in a way to add new elements to the array however. I also need to add the ability to reorganize the array.
   buttonclicked,
   objectclicked;
document.body.onload = function() {
   document.getElementById("umatrix-ua-spoofer").innerHTML = "";
   backgroundsel = 25; // Uncomment to select specific background
   createcanvas();
},
backgroundarr = [
   { img: "000001.jpg", x: 0, y: 0, s: 1.3, g: ["#ffee9c", "#fee98e", "#fff3ab"] },
   { img: "000006.png", col: "#000", x: 550, y: 0, s: 1.3 },
   { img: "000013.png", x: 125, y: -20, s: 1.261, g: ["#fff", "#fff", "#ededed", "#d0d0d0", "#b0b0b0", "#939393", "#7e7e7e"] },
   { img: "000051.png", col: "#000023", x: 0, y: 0, s: 1.3},
   { img: "000054.png", col: "#000", x: 0, y: 0, s: 1.3 },
   { img: "000074.png", col: "#000", x: 0, y: -205, s: 1.68},
   { img: "000100.png", col: "#fff", x: 0, y: 0, s: 1.25 },
   { img: "000108.png", col: "#fff", x: 0, y: 0, s: 1.25},
   { img: "000121.jpg", col: "#000", x: 850, y: 0, s: 0.89},
   { img: "000164.png", col: "#fff", x: 0, y: 0, s: 1.231},
   { img: "000198.jpg", col: "#fff", x: 331, y: 0, s: 1.11},
   { img: "000199.jpg", col: "#000", x: 470, y: 0, s: 1.19},
   { img: "000217.png", x: 1030, y: 0, s: 1.12, g: ["#e20b30", "#e90a30", "#e90a30", "#e70c31", "#e01033", "#d91838", "#da1638", "#db1637", "#e41238"]},
   { img: "000236.png", col: "#9ae2ea", x: 1200, y: 0, s: 0.9},
   { img: "000281.jpg", col: "#050a1e", x: 0, y: 0, s: 1.23},
   { img: "000311.png", col: "#eff7ff", x: 650, y: 0, s: 1.17},
   { img: "000333.jpg", col: "#fff", x: 800, y: 0, s: 1.5},
   { img: "000350.jpg", col: "#68d2d0", x: 0, y: 250, s: 1.31},
   { img: "000410.jpg", col: "#fff", x: 1750, y: 0, s: 0.48},
   { img: "000420.jpg", col: "#767676", x: 550, y: 0, s: 1.15, l: [10, "#767676"]},
   { img: "000458.jpg", col: "#443a39", x: 0, y: 0, s: 0.71},
   { img: "000468.jpg", col: "#000", x: 0, y: -35, s: 1.98},
   { img: "000562.jpg", x: 2005, y: 0, s: 0.77, g: ["#d1cac4", "#e3dcd2", "#f3eae1", "#f4ece1", "#f1e9dc", "#eae1d2", "#dacaba"]},
   { img: "000596.jpg", col: "#000", x: 300, y: 0, s: 1.03},
   { img: "000603.jpg", col: "#051a1f", x: -250, y: 0, s: 1.231},
   { img: "000619.jpg", x: 300, y: 0, s: 1.11, g: ["#351c17", "#351c17", "#e0e3d2", "#e0e3d2", "#341b16", "#341b16"], p: [0, 0.0827, 0.0827, 0.918, 0.918, 1]},
   { img: "000621.png", col: "#000", x: 300, y: 0, s: 1.11},
   { img: "000646.jpg", col: "#000", x: 330, y: 0, s: 1.11}
];

var output = '',
   canvas,
   ctx,
   img = new Image(),
   scale = [],
   backgroundsel = Math.round(Math.random() * (backgroundarr.length - 1)),
   x;
for (let i = 0; i < bookmarks.length; i++) {
   output += `
   <div class="container" style="background-image: url('images/Banners/${bookmarks[i].image}.png');" name="${i}" title="${bookmarks[i].url}">
      <div class="title"><img src="images/Favicons/${bookmarks[i].image}.png" height="16px" width="16px">${bookmarks[i].name}</div>
      <div class="clicks">${bookmarks[i].clicks} clicks</div>
   </div>`;
}

document.body.innerHTML = output;

function clicked(num, tab) {
   bookmarks[num].clicks++;
   document.getElementsByClassName("clicks")[num].innerHTML = `${bookmarks[num].clicks} clicks`;
   setItem("bookmarks", JSON.stringify(bookmarks));
   if (!tab) {
      document.location = bookmarks[num].url;
   } else {
      var win = window.open(bookmarks[num].url, '_blank');
   }
}
document.onmousedown = function(e) {
   objectclicked = e;
   buttonclicked = e.buttons;
};
document.onmouseup = function() {
   if (typeof objectclicked.target.attributes[2] !== "undefined") {
      if (objectclicked.buttons === 1) {
         clicked(objectclicked.target.attributes[2].value.toString(), false);
      } else if (objectclicked.buttons === 4) {
         clicked(objectclicked.target.attributes[2].value.toString(), true);
      }
   }
}
function createcanvas() {
   var can = document.createElement("canvas");
   can.id = "backgroundimage";
   can.width = document.body.clientWidth;
   can.height = document.body.clientHeight;
   var lastel = document.body.lastChild;
   document.body.insertBefore(can, lastel);
   canvas = document.getElementById("backgroundimage");
   ctx = canvas.getContext('2d');
   img.src = backgrounds + backgroundarr[backgroundsel].img;
   img.onload = function() {
      console.log(`Background: ${JSON.stringify(backgroundarr[backgroundsel], 0, "    ")}`); //Debug information to help me find the current image in the array if necessary
      scale = backgroundarr[backgroundsel].s;
      x = backgroundarr[backgroundsel].x;
      y = backgroundarr[backgroundsel].y;
      if (typeof backgroundarr[backgroundsel].g !== "undefined") {
         var gradient = ctx.createLinearGradient(canvas.width, 0, canvas.width, canvas.height),
            step = [];
         for (let i = 0; i < backgroundarr[backgroundsel].g.length; i++) {
            if (typeof backgroundarr[backgroundsel].p !== "undefined") {
               var point = backgroundarr[backgroundsel].p[i];
            } else {
               var point = i/(backgroundarr[backgroundsel].g.length-1);
            }
            gradient.addColorStop(point, backgroundarr[backgroundsel].g[i]);
            
            step[i] = point;
            //console.log(i/(backgroundarr[backgroundsel].g.length-1)); //Logs current gradient percentage
         }
         ctx.fillStyle = gradient;
      } else {
         ctx.fillStyle = backgroundarr[backgroundsel].col.toString();
      }
      ctx.fillRect(0,0,canvas.width,canvas.height);
      ctx.scale(scale, scale);
      ctx.drawImage(img, x, y);
      if (typeof backgroundarr[backgroundsel].l !== "undefined") {
         let w = backgroundarr[backgroundsel].l[0];
         ctx.fillStyle = backgroundarr[backgroundsel].l[1];
         ctx.fillRect(backgroundarr[backgroundsel].x-1, 0, w, canvas.height); //Left Line
         ctx.fillRect(backgroundarr[backgroundsel].x, 0, img.width, w); // Top Line
         ctx.fillRect(backgroundarr[backgroundsel].x + img.width - (w/2), backgroundarr[backgroundsel].y, w, canvas.height); // Right Line
         ctx.fillRect(backgroundarr[backgroundsel].x, img.height-w, img.width, w); // Bottom Line
      }
      
      //This if loop is debug stuff used for constructing the gradients. It draws a line a few pixels above and
      //a few pixels below a gradient point so I can more accurately sample the image's colors to construct a good gradient.
      if (typeof step !== "undefined") {
         ctx.setTransform(1, 0, 0, 1, 0, 0);
         
         for (let i = 0; i < step.length; i++) {
            ctx.fillStyle = "black";
            ctx.fillRect(0, Math.round(step[i] * canvas.height) - 4, canvas.width, 1);
            ctx.fillRect(0, Math.round(step[i] * canvas.height) + 4, canvas.width, 1);
            //console.log(parseInt(backgroundarr[backgroundsel].g[i].replace(/#/,""), 16)); //Logs decimal value of color
            if (parseInt(backgroundarr[backgroundsel].g[i].replace(/#/,""), 16) > 12000000) {
               ctx.fillStyle = "#333"; //For light backgrounds
            } else if (parseInt(backgroundarr[backgroundsel].g[i].replace(/#/,""), 16) > 8000000) {
               ctx.fillStyle = "#ff00aa"; //For medium backgrounds
            } else if (parseInt(backgroundarr[backgroundsel].g[i].replace(/#/,""), 16) > 4000000) {
               ctx.fillStyle = "white"; //For dark backgrounds
            }
            ctx.font = "10px sans-serif";
            var modifier;
            if (step[i] * canvas.height < canvas.height * (1/3)) {
               modifier = 0;
            } else if (step[i] * canvas.height < canvas.height * (2/3)) {
               modifier = 0;
            } else {
               modifier = -30;
            }
            ctx.fillText(backgroundarr[backgroundsel].g[i], 10, (Math.round(step[i] * canvas.height)+modifier) + 20);
         }
      }
   };
}

Added a few comments in all that for a few relevant points. Most of it is relatively self-explanatory though because I try to keep my code reasonably descriptive since I'm bad at remembering to comment things as I go.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » December 29th, 2016, 11:54 am

I wasn't intending to work on that newtab page again today but I ended up doing a bit of work. I made my debug stuff run off a debug variable which is near where I tend to be in the code when making use of my debug stuff anyways. I also made the col field for the background images completely optional which resulted in me getting rid of all the col: "#000" fields since they were now superfluous since the canvas background is black by default. Then I added in another field labelled f: which is simply a custom function that gets executed when that image loads if the field exists. I have a few images where it's not really possible to scale them up so they fill the entire screen but they have a background which is white on one side and black on the other. None of my previous methods could deal with this. So my solution was to give it a custom function which drew white on the correct side and didn't draw on the other side.

Future plans include giving each image in the array a title so I can search specific ones out easier when modifying my code (I already gave each a number so debugging is easier too), possibly expanding my gradient field to include angles and possibly multiple gradients, and then the ability to add/delete/edit link entries and background images as well.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » January 1st, 2017, 3:39 am

Another bit of work on that newtab page I've been working on. Yesterday I'd converted it into a Javascript object from an array so that I wouldn't have to change the "n: #" parameter I'd been using to help organize them, just moving that to the index of each object entry. I'd been having to handle the length of the array in a fairly annoying manner though. Then I discovered I could do this:

Code: Select all

Object.prototype.__defineGetter__("length", function() { return Object.keys(this).length });

Technically this isn't normally usable with an object if you're using named objects. But since I'm numbering the objects instead this allows me to continue using backgroundarr.length as my iterator.

For reference this is the way I had been doing it before:

Code: Select all

(function() {
   backgroundarr.length = Object.keys(backgroundarr).length;
})()

It worked fine but I didn't like the way I had to handle it. Especially since this new method allows me to do the same thing with the page's links.
Image
This allows me to organize my array by image title now and just put its number as whatever the current length value is. My next bit of work is to add the ability to add/delete new links and backgrounds from the newtab page itself as well as rearranging them too. And of course the ability to import/export them so I can back them up myself but that bit isn't quite as important at the moment.
💙💙💙
Image
Image


User avatar
Ashan
Posts: 503
Joined: February 15th, 2015, 1:33 am
Location: Saskatchewan
Contact:

Re: Programming Thread

Postby Ashan » January 13th, 2017, 7:18 pm

Today I learned (the hard way) that ending a comment with a backslash (\) in C++ will comment out the next line.

Took me about a half hour of racking my brain trying to figure out why 2 of the exact same functions were doing 2 different things. Turns out it was just a rogue comment that the compiler didn't even warn me about.
Image
► Show Spoiler

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » January 13th, 2017, 7:46 pm

Ashan wrote:Today I learned (the hard way) that ending a comment with a backslash (\) in C++ will comment out the next line.

Took me about a half hour of racking my brain trying to figure out why 2 of the exact same functions were doing 2 different things. Turns out it was just a rogue comment that the compiler didn't even warn me about.

Kinda interesting (in a weird way) that C++ seems to escape things even in comments. That would've really caught me by surprise too. At least it was only a half hour though. I definitely haven't spent 3-4 hours trying and failing to figure out why something wasn't working correctly only to finally realize there was a single character typo in the code that was super easy to miss, lol.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 12th, 2017, 5:39 pm

My main forum has a funny pics thread but for some reason people have taken to embedding like 10 Youtube videos per page which means the page takes forever to load. I decided to try and work around that. I first blocked Youtube on the site through uMatrix. This sped things up but left a lot of blank posts. So I made a userscript to retrieve the title and thumbnail of the video from Youtube's API then embed those in its place.

Code: Select all

// ==UserScript==
// @name        FP Video Linker
// @namespace   Alice
// @include     https://facepunch.com/showthread.php*
// @version     1
// @grant       none
// ==/UserScript==

let alicevids = document.getElementsByClassName("video");
let length = alicevids.length; //I need to set this or else the length field for the for loop actually shrinks and it'll only get half the videos, lol
for (let i = 0; i < length; i++) {
   let video = alicevids[i].childNodes[0].src;
   if (video.match(/youtube\.com/)) {
      video = video.replace(/embed\//, "watch?v=");
      video = video.replace(/\?hd=1/, "");
      let vidid = video.replace(/(.+v=)(.+)/, '$2');
      fetch(`https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${vidid}&fields=items(snippet)&key=MYAPIKEYGOESHERE`).then(function(response) {
         return response.json();
      }).then(function(data) {
         let vid = data.items[0].snippet,
            thumbnail = '';
         if (typeof vid.thumbnails.maxres !== "undefined") {
            thumbnail = vid.thumbnails.maxres.url;
         } else if (typeof vid.thumbnails.standard !== "undefined") {
            thumbnail = vid.thumbnails.standard.url;
         } else if (typeof vid.thumbnails.high !== "undefined") {
            thumbnail = vid.thumbnails.high.url;
         } else if (typeof vid.thumbnails.medium !== "undefined") {
            thumbnail = vid.thumbnails.medium.url;
         } else {
            thumbnail = vid.thumbnails.default.url;
         }
         alicevids[0].parentNode.outerHTML = `<img src="${thumbnail}"><br>Youtube video: <a href="${video}">${vid.channelTitle} - ${vid.title}</a>`;
      });
   } else {
      alicevids[0].parentNode.outerHTML = `<a href="${video}">${video}</a>`;
   }
}

Actually works out surprisingly well. Though dealing with the thumbnails does seem to reintroduce a bit of lag, but it's still far better than it was. (Most of the lag seems to just be my internet being utter garbage as per usual.)
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 14th, 2017, 7:27 pm

Now for a really dumb userscript because people on my main forum keep using these word replacing userscripts that don't actually let you know when something is replaced or what's being replaced.

Code: Select all

// ==UserScript==
// @name        Word Replacer
// @namespace   Alice
// @include     https://facepunch.com/*
// @version     1
// @grant       none
// ==/UserScript==
let replacements = [
   {
      replace: "Donald Trump",
      replacements: [
         "Shit-gibbon",
         "Baron von Clownstick",
         "Senor Fake Tan"
      ]
   },
   {
      replace: "Donald J. Trump",
      replacements: [
         "Shit-gibbon",
         "Baron von Clownstick",
         "Senor Fake Tan"
      ]
   },
   {
      replace: "President Trump",
      replacements: [
         "President Shit-gibbon",
         "President von Clownstick",
         "President Fake Tan"
      ]
   },
   {
      replace: "Trump White House",
      replacements: [
         "Trump Three Ring Circus"
      ]
   },
   {
      replace: "Trump Administration",
      replacements: [
         "Trump Three Ring Circus"
      ]
   },
   {
      replace: "Mike Pence",
      replacements: [
         "Mike 'thinks about gay sex all day' Pence"
      ]
   },
   {
      replace: "Trump official",
      replacements: [
         "Moron",
         "Liar",
         "Propagandist"
      ]
   },
   {
      replace: "Hillary Clinton",
      replacements: [
         "Walking Security Risk"
      ]
   },
   {
      replace: "Bernie Sanders",
      replacements: [
         "Mr. Economics? What are economics?"
      ]
   },
   {
      replace: "Google",
      replacements: [
         "NSA++"
      ]
   },
],
threadtitle = document.getElementById('breadcrumb').children[4],
posts = document.getElementsByClassName('postbody'),
threadlist = document.getElementsByClassName('threadtitle');

if (location.toString().match(/showthread\.php/)) {
   for (let i = 0; i < posts.length; i++) {
      let post = posts[i].children[0].children[0].children[0];
      for (let k = 0; k < replacements.length; k++) {
         let rep = new RegExp(replacements[k].replace, 'gi'),
            sub = replacements[k].replacements[rand(0, replacements[k].replacements.length-1)];
         post.innerHTML = post.innerHTML.replace(rep, `<abbr title="${replacements[k].replace}">${sub}</abbr>`);
      }
   }
   for (let k = 0; k < replacements.length; k++) {
      let rep = new RegExp(replacements[k].replace, 'gi'),
         sub = replacements[k].replacements[rand(0, replacements[k].replacements.length-1)];
      threadtitle.innerHTML = threadtitle.innerHTML.replace(rep, `<abbr title="${replacements[k].replace}">${sub}</abbr>`);
   }
} else {
   for (let i = 0; i < threadlist.length; i++) {
      let thread = threadlist[i].children[0];
      for (let k = 0; k < replacements.length; k++) {
         let rep = new RegExp(replacements[k].replace, 'gi'),
            sub = replacements[k].replacements[rand(0, replacements[k].replacements.length-1)];
         thread.innerHTML = thread.innerHTML.replace(rep, `<abbr title="${replacements[k].replace}">${sub}</abbr>`);
      }
   }
}

function rand(min, max) {
   min = Math.ceil(min);
   max = Math.floor(max);
   return Math.floor(Math.random() * (max - min + 1)) + min;
}

I can't refrain from giggling every time "President Shit-gibbon" comes up, lol. Tried coming up with stuff for Bernie Sanders and Hillary Clinton as well but they're a lot more difficult to come up with stuff for.
💙💙💙
Image
Image


User avatar
chridd
Posts: 210
Joined: December 25th, 2014, 9:20 pm
Location: the internet
Contact:

Re: Programming Thread

Postby chridd » February 15th, 2017, 12:26 am

xkcd forum had "Bernie Sandals" and "Hilarity Clowntown".

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 15th, 2017, 12:38 am

chridd wrote:xkcd forum had "Bernie Sandals" and "Hilarity Clowntown".

Those are probably better choices than mine are, lol. Might have to add them to my list.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 15th, 2017, 6:35 pm

Code: Select all

// ==UserScript==
// @name        Word Replacer
// @namespace   Alice
// @include     https://facepunch.com/*
// @version     1
// @grant       none
// ==/UserScript==
let replacements = [
   {
      replace: "(Trumpet|Trump supporter)",
      title: "Trumpet or Trump supporter",
      replacements: [
         "deluded moron",
         "gullible idiot"
      ]
   }, {
      replace: "Trump White House",
      replacements: [
         "Trump Three Ring Circus"
      ]
   }, {
      replace: "Trump Administration",
      replacements: [
         "Donald J. Trump Memorial Zoo"
      ]
   }, {
      replace: "Mike Pence",
      replacements: [
         "Mike 'thinks about gay sex all day' Pence"
      ]
   }, {
      replace: "Trump official",
      replacements: [
         "Moron",
         "Liar",
         "Propagandist"
      ]
   }, {
      replace: "(president )?(donald )?(j\.? )?(trump)",
      title: "Donald Trump",
      replacements: [
         "President Shit-gibbon",
         "President von Clownstick",
         "Baron von Clownstick",
         "Señor Fake Tan"
      ]
   }, {
      replace: "Hillary Clinton",
      replacements: [
         "Walking Security Risk",
         "Hilarity Clowntown"
      ]
   }, {
      replace: "Bernie Sanders",
      replacements: [
         "Economics? What are economics?",
         "Feel the Burn",
         "Bernie Sandals"
      ]
   }, {
      replace: "Google",
      replacements: [
         "NSA++"
      ]
   }, {
      replace: "Gmail",
      replacements: [
         "PRISM Mail"
      ]
   }, {
      replace: "Microsoft",
      replacements: [
         "Microshaft"
      ]
   }, {
      replace: "Kellyanne Conway",
      replacements: [
         "Ms. Blatant Liar",
         "Ms. Full of Shit"
      ]
   }, {
      replace: "MAGA",
      replacements: [
         "Make America Terrible Again"
      ]
   }, {
      replace: "cuck",
      replacements: [
         "slur for liberal"
      ]
   }
],
threadtitle = document.getElementById('breadcrumb').children[4],
posts = document.getElementsByClassName('postbody'),
threadlist = document.getElementsByClassName('threadtitle');
console.log("World Replacemer running!"); //I apparently can't type worth a damn when naming userscripts because this sort of shit happens consistently every time I make a new userscript, lol

//If viewing a thread
if (location.toString().match(/showthread\.php/)) {
   //Loop through the posts
   for (let i = 0; i < posts.length; i++) {
      let post = posts[i].children[0].children[0].children[0];
      //Loop through the replacements array
      for (let k = 0; k < replacements.length; k++) {
         let rep = new RegExp(replacements[k].replace, 'gi'),
            sub = replacements[k].replacements[rand(0, replacements[k].replacements.length-1)];
         //Only execute if matched
         if (post.innerHTML.match(rep)) {
            //Run through post's subelements
            for (let j = 0; j < post.children.length; j++) {
               //If post has children that have children
               if (post.children[j].children.length) {
                  let postelems = post.children[j].children.length;
                  //Run through each subelement's subelements
                  for (let l = 0; l < postelems; l++) {
                     let node = post.children[j].children[l];
                     if (node.nodeName !== "A" && node.nodeName !== "#text" && node.nodeName !== "BR") {
                        node.innerHTML = node.innerHTML.replace(rep, `<abbr title="${replacements[k].replace}">${sub}</abbr>`);
                        l = postelems;
                     } else {
                        if (node.nodeName === "#text" || node.nodeName === "BR") {
                           let name = '';
                           if (typeof replacements[k].title === "undefined") {
                              name = replacements[k].replace;
                           } else {
                              name = replacements[k].title;
                           }
                           node.parentNode.innerHTML = node.parentNode.innerHTML.replace(rep, `<abbr title="${name}">${sub}</abbr>`);
                           l = postelems;
                        }
                     }
                  }
               } else {
                  let name = '';
                  if (typeof replacements[k].title === "undefined") {
                     name = replacements[k].replace;
                  } else {
                     name = replacements[k].title;
                  }
                  post.innerHTML = post.innerHTML.replace(rep, `<abbr title="${name}">${sub}</abbr>`);
               }
            }
         }
      }
   }
   //Replace thread title
   for (let k = 0; k < replacements.length; k++) {
      let rep = new RegExp(replacements[k].replace, 'gi'),
         sub = replacements[k].replacements[rand(0, replacements[k].replacements.length-1)];
      if (threadtitle.innerHTML.match(rep)) {
         let name = '';
         if (typeof replacements[k].title === "undefined") {
            name = replacements[k].replace;
         } else {
            name = replacements[k].title;
         }
         threadtitle.innerHTML = threadtitle.innerHTML.replace(rep, `<abbr title="${name}">${sub}</abbr>`);
      }
   }
} else {
   //Replace thread title entries on forum view
   for (let i = 0; i < threadlist.length; i++) {
      let thread = threadlist[i].children[0];
      for (let k = 0; k < replacements.length; k++) {
         let rep = new RegExp(replacements[k].replace, 'gi'),
            sub = replacements[k].replacements[rand(0, replacements[k].replacements.length-1)];
         if (thread.innerHTML.match(rep)) {
            let name = '';
            if (typeof replacements[k].title === "undefined") {
               name = replacements[k].replace;
            } else {
               name = replacements[k].title;
            }
            thread.innerHTML = thread.innerHTML.replace(rep, `<abbr title="${name}">${sub}</abbr>`);
         }
      }
   }
}

function rand(min, max) {
   min = Math.ceil(min);
   max = Math.floor(max);
   return Math.floor(Math.random() * (max - min + 1)) + min;
}

I've done a fair bit of work since that last time and condensed some of the different collections (ie: the different alternatives for Trump's name) into one single entry, though this also entailed adding support for actual titles for the entries so the tooltip doesn't display Regex for those entries. I also still need to figure out a simple way of going through the elements I need to replace. The site is weirdly inconsistent about things so my current methods sometimes miss replacements that should happen. The weird part is that it only seems to miss replacements in a post without any quotes within it but it inconsistently does so. Sometimes it will replace words in a post, other times it won't. Though it does seem to be consistent on which posts it fails on at least.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 20th, 2017, 12:16 am

Image
Unless I'm just misunderstanding something it seems like Javascript actually has limited support for messing with stuff in binary.

4294967296 should equate to 00000001 00000000 00000000 00000000 but it simply fails and reports as all zeroes in binary. Unless there's some flaw in my code I'm managing to overlook at least.

Code: Select all

function dec2bin(dec, len){
   if (typeof len === "undefined") {
      var len = 16;
   }
    return padto((dec >>> 0).toString(2), len, true);
}

function padto(inp, len, split) {
   if (typeof split === "undefined") {
      var split = false;
   }
   inp = inp.toString();
   for (let i = inp.length; i < len; i++) {
      inp = "0" + inp;
   }
   if (split) {
      inp = binsplit(inp);
   }
   return inp;
}

function binsplit(inp) {
   let out = [];
   for (let i = 0; i < inp.length; i+= 8) {
      let x = Math.round(i/8)
      out[x] = `${inp[i]}${inp[i+1]}${inp[i+2]}${inp[i+3]}${inp[i+4]}${inp[i+5]}${inp[i+6]}${inp[i+7]}`;
      out[x] = out[x].toString().replace(/undefined/g, "");
   }
   return out.join(" ");
}

That's the code that actually deals with converting to binary. And I just populate an array using this:

Code: Select all

testarr = [];
for (let j = 0; j <= 32; j++) {
   testarr[j] = Math.pow(2,j);
}
testarr[testarr.length-1] = testarr[testarr.length-1] - 1; //This should be all 1s
testarr[testarr.length] = testarr[testarr.length-1] + 1; //This is what the last one originally was but basically moved to a new slot

to get the numbers I'm messing with.


Edit:
MDN wrote:Bitwise operators treat their operands as a sequence of 32 bits (zeroes and ones), rather than as decimal, hexadecimal, or octal numbers.

Huh, wasn't aware of this. I'd have thought that at least when using the 64-bit version of Firefox it'd be 64-bit rather than 32-bit but it seems to be explicitly 32-bit.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 24th, 2017, 11:23 am

I randomly decided to try working on a local browser-based music player because I have a bad habit of using my Youtube music playlist despite having like 99% of the music from said playlist saved to my computer. Was an interesting experience but at times it's a serious pain in the ass. Some videos, despite seemingly being supported, won't load. Like they're all mp4s but sometimes they just refuse to load. Firefox doesn't see them as valid for some reason. And for some I have lyrics saved. Getting those to load was a nightmare and I'm most certainly not doing it right. I'm in fact exploiting the fact that videos can have subtitles. Though even this is weird as hell. For starters I'm just doing the lyrics as a subtitles file that immediately stops displaying. This means that I have to put in this at the beginning:

Code: Select all

WEBVTT
 
00:00.000 --> 00:00.000

and then I have to replace all blank lines with at least a single space or else it considers it part of the next bit of the subtitles. This hilariously resulted in me accidentally adding a space to beginning of every line in every one of the 50ish files I had open in Notepad++. Thankfully 40 of those were at least the ones I was trying to add them to.

For a script that's less than 200 lines (excluding the playlist array which is a separate file) it turned out pretty well though.

Code: Select all

var list = document.getElementById("listcontainer"),
   vid = document.getElementById("vidcontainer"),
   curvid,
   volDiv = document.getElementById('volcontrol'),
   vol, timer = 0, timerCountdown, vidid = 0,
   vidvol = JSON.parse(getItem("vol", {})),
   lastrack = JSON.parse(getItem("lastrack", 0)),
   next = 0;
volDiv.id = `volDiv`;
volDiv.style.visbility = "hidden";
volDiv.style.display = "fixed";
volDiv.style.left = "-100px";
volDiv.style.top = "-100px";
volDiv.style.width = "0";
volDiv.style.height = "0";
volDiv.style.position = "absolute";
volDiv.style.textAlign = "center";
volDiv.style.color = "aqua";
volDiv.style.backgroundColor = "black";
volDiv.style.border = "1px solid aqua";
volDiv.style.zIndex = 10000000;
   
document.body.onload = function() {
   changetrack(lastrack);
   //changetrack(musiclist.length-1);
};

function changetrack(track, auto) {
   if (typeof auto === undefined) {
      auto = '';
   }
   vidid = track;
   lastrack = track;
   setItem("lastrack", lastrack);
   vol = vidvol[musiclist[track].file] | 1;
   let subs = '',
      text = '';
   if (typeof musiclist[track].lyrics !== "undefined") {
      subs = `<track label="English" kind="descriptions" srclang="en" src="file:///${musiclist[track].path}/${musiclist[track].file}.txt" default>`;
   }
   let vidout = `<${musiclist[track].type} controls ${auto}><source src="file:///${musiclist[track].path}/${musiclist[track].file}.${musiclist[track].format}" type ="${musiclist[track].type}/${musiclist[track].format}">${subs}</${musiclist[track].type}>`;
   if (musiclist[track].type === "audio") {
      vid.innerHTML = `
      <img src="file:///${musiclist[track].image}" width="${musiclist[track].res.w}px" height="${musiclist[track].res.h}px" onclick="playpause();">
      ${vidout}`;
   } else {
      vid.innerHTML = vidout;
   }
   curvid = document.getElementsByTagName(`${musiclist[track].type}`)[0];
   curvid.volume = vol / 100;
   if (musiclist[track].type === "video") {
      curvid.style.height = `100vh`;
      if (curvid.clientWidth > document.body.clientWidth - 320 - 390) {
         curvid.style.height = ``;
         curvid.style.width = `${document.body.clientWidth - 320 - 390 - 8}px`;
      }
      curvid.style.top = `-6px`;
   } else {
      let aspectratio = musiclist[track].res.w / musiclist[track].res.h;
      curvid.style.height = `28px`;
      curvid.style.width = `${document.body.clientWidth - 320 - 390 - 8}px`;
      document.getElementsByTagName("img")[0].style.width = `${document.body.clientWidth - 320 - 390 - 8}px`;
      document.getElementsByTagName("img")[0].style.height = `${(document.body.clientWidth - 320 - 390 - 8) / aspectratio}px`;
      curvid.style.position = "absolute";
      curvid.style.top = `${document.getElementsByTagName("img")[0].clientHeight}px`;
   }
   populatelist(vidid);
   
   if (document.getElementsByTagName("track").length) {
      playpause();
      setTimeout(function() {
         playpause();
      }, 10);
      setTimeout(function() {
         text = document.getElementsByTagName("track")[0].track.cues[0].text;
         text = text.replace(/\n/g, "<br>");
      }, 100);
   }
   setTimeout(function() {
      document.getElementById("lyricscontainer").innerHTML = text;
   }, 100);
   if (vidid !== musiclist.length - 1) {
      next = vidid + 1;
   } else {
      next = 0;
   }
   curvid.addEventListener("ended", function(e) {
      changetrack(next, `autoplay="true"`);
   });
}

function populatelist(id) {
   let listout = ``;
   for (let i = 0; i < musiclist.length; i++) {
      if (i === id) {
         listout += `<input type="button" class="curvid" ondblclick="changetrack(${i}); playpause();" title="${musiclist[i].file} (${musiclist[i].length})" value = "${musiclist[i].file} (${musiclist[i].length})"><br>`;
      } else {
         listout += `<input type="button" ondblclick="changetrack(${i}); playpause();" title="${musiclist[i].file} (${musiclist[i].length})" value = "${musiclist[i].file} (${musiclist[i].length})"><br>`;
      }
   }
   list.innerHTML = listout;
}

function playpause() {
   if (curvid.paused) {
      curvid.play();
   } else {
      curvid.pause();
   }
}
document.addEventListener("DOMMouseScroll", function(e) {
   if (e.target.tagName === "VIDEO" || e.target.tagName === "AUDIO" || e.target.tagName === "IMG") {
      e.preventDefault();
      e.cancelBubble = true;
      
      var delta = e.detail;
      //console.log(e);
      clearInterval(timerCountdown);
      
      if (delta > 0 && vol > 0) {
         //Mouse down
         --vol;
      }
      if (delta < 0 && vol < 100) {
         //Mouse up
         ++vol;
      }
      vidvol[musiclist[vidid].file] = vol;
      localStorage.setItem("vol", JSON.stringify(vidvol));
      timer = 100;
      volDiv.style.height = "20px";
      volDiv.style.width = "";
      volDiv.style.left = `${e.pageX-40}px`;
      volDiv.style.top = `${e.pageY+40}px`;
      volDiv.innerHTML = `<div style="width: 26px; display: inline-block; vertical-align: top;">${vol}</div><div style="background-color: black; width: 100px; height: 20px; display: inline-block;"><div style="background-color: aqua !important; width: ${vol}px; height: 20px;"></div></div>`;
      volDiv.style.visbility = "visible";
      volDiv.style.opacity = 100;
      timerCountdown = setInterval(volTimer, 15);
      
      curvid.volume = vol / 100;
   }
});

function volTimer() {
   if (timer > 0) {
      timer--;
      volDiv.style.opacity = timer / 100;
   } else {
      volDiv.style.visbility = "hidden";
      volDiv.style.width = "0";
      volDiv.style.height = "0";
      clearInterval(timerCountdown);
   }
}

function getItem(thing, sub) {
   let ret = localStorage.getItem(thing);
   if (ret !== null) {
      return ret;
   } else {
      if (typeof sub !== "undefined") {
         return sub;
      } else {
         return 0;
      }
   }
}

function setItem(index, thing) {
   return localStorage.setItem(index, JSON.stringify(thing));
}

and this is what the playlist file looks like:

Code: Select all

var musiclist = [
   {
      file: "Akita Neru - Stop Nagging Me!",
      path: "D:/Movies/Music Videos/Vocaloid stuff",
      type: "video",
      format: "mp4",
      length: "2:41",
   }, {
      file: "Calne Ca (Deino) - ATARAXIA",
      path: "D:/Movies/Music Videos/Vocaloid stuff",
      type: "video",
      format: "mp4",
      length: "4:07"
   }, {
      file: "Calne Ca (Deino) - Bacterial Contamination",
      lyrics: true,
      path: "D:/Movies/Music Videos/Vocaloid stuff",
      type: "video",
      format: "mp4",
      length: "3:56"
   }, {
      file: "Hatsune Miku - Mikunologie",
      path: "D:/My documents/My Music/Keep/New/Vocaloid",
      image: "D:/My documents/My Pictures/Desktops/Using/000046.png",
      type: "audio",
      format: "mp3",
      length: "2:09",
      res: {
         w: 1280,
         h: 720
      },
   }
]

That last one, the audio one, actually being the one that originally made me realize that Firefox is weirdly sporadic with supporting mp4s.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 24th, 2017, 11:41 am

Interesting. I appear to have found a bit of a bug with Firefox. In my code I have these bits:

Code: Select all

   if (typeof auto === undefined) {
      auto = '';
   }
let vidout = `<${musiclist[track].type} controls ${auto}><source src="file:///${musiclist[track].path}/${musiclist[track].file}.${musiclist[track].format}" type ="${musiclist[track].type}/${musiclist[track].format}">${subs}</${musiclist[track].type}>`;

Where I'm adding that blank string (it's not always blank) to the tags it results in this when placed on the page:
Image
Doesn't break anything since it just treats "undefined" as a property of the tag but it's still an interesting quirk nonetheless. I'm really curious why it treats a blank string that way. Also has the same result if I replace it with a space.
💙💙💙
Image
Image


User avatar
chridd
Posts: 210
Joined: December 25th, 2014, 9:20 pm
Location: the internet
Contact:

Re: Programming Thread

Postby chridd » February 24th, 2017, 4:52 pm

Alice wrote: if (typeof auto === undefined) {
This should probably either be typeof auto === 'undefined' or auto === undefined. typeof auto is never going to actually be undefined (as opposed to the string 'undefined').

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 25th, 2017, 1:05 am

chridd wrote:This should probably either be typeof auto === 'undefined' or auto === undefined. typeof auto is never going to actually be undefined (as opposed to the string 'undefined').

Ah crap. I make that mistake all the time and manage to constantly fail to catch it when I'm looking to solve whatever issues it introduces, lol. I always use the typeof method because it's more predictable than a straight comparison to undefined. Thanks for pointing that out.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 27th, 2017, 5:56 am

I realized I don't have to do that wonky way of retrieving lyrics for a song that has them but I ran into another annoying issue in the process. I can just embed the lyrics file as an iframe but there's a really irritating issue with this. I cannot style them at all past the iframe's background color and a text file gets parsed as <pre> tags which don't wrap lines by default. And due to annoying security reasons I'm unable to retrieve data from the iframe. (REALLY annoying security reasons related to cross-site-origin crap despite the fact this is all freaking local stuff.) I'm considering trying to setup a symbolic link inside the directory for the player to see if that helps but setting up a symbolic link is really a pain in the ass. Edit2: And I went and gave that a try. Annoyingly enough it worked fine. Loaded the iframe using the symbolic link and was able to retrieve the lyrics from it and then insert that into the lyrics pane to properly style them. I REALLY wish JavaScript's security features didn't get in the way so often. I understand why they exist but when you are loading local files there is literally no reason at all for them to get in the way like this.

I also realized I was being dumb with my playlist file. I didn't need to make use of the format, type, or path entries. I could just set a default one in my code then only make use of it in the playlist itself if one of those values differs from the default. I also moved the duration entry in the playlist to local storage so I could deal with it dynamically rather than having to manually add it to the playlist for every song.

So an average entry went from this:

Code: Select all

{
   file: "Hatsune Miku, Hakaine Maiko - Friend Park Syndrome",
   //Name's hilariously out of date because while this folder is mostly vocaloid crap it also contains a shit load of not-vocaloid crap, lol
   path: "D:/Movies/Music Videos/Vocaloid stuff",
   type: "video",
   format: "mp4",
   duration: "4:09"
}

to this:

Code: Select all

{
   file: "Hatsune Miku, Hakaine Maiko - Friend Park Syndrome",
}

The path, type, and format are all just the defaults since that path, "video", and "mp4" are the most common types in my music folder. Duration is simply retrieving the video's duration after it's loaded and storing it in local storage. (Overall this entire thing saved 265 lines of code in the playlist taking me down to 147 for my current entries, though that many entries is only 67 tracks at the moment. I could also save a bit of space if I put the {}s for each entry on its line as well but that's not too big a deal really. Edit: Took me down to 84 lines now. And the formatting is a lot easier to deal with now since the majority of entries are just goign to be a single line.) Though it was a bit tricky to get it to actually retrieve the duration for some weird reason. For some reason it would return NaN in my code despite the fact I could use the same code in the console and get the correct value so I ended up having it do a check for whether the duration is already stored as a number in the local storage array and if not then it updates the local storage array. This also repopulates the playlist on the screen so when you start playing a song that shows no duration the playlist will now show the duration.
💙💙💙
Image
Image


User avatar
chridd
Posts: 210
Joined: December 25th, 2014, 9:20 pm
Location: the internet
Contact:

Re: Programming Thread

Postby chridd » February 27th, 2017, 6:45 am

Alice wrote:And due to annoying security reasons I'm unable to retrieve data from the iframe. (REALLY annoying security reasons related to cross-site-origin crap despite the fact this is all freaking local stuff.)
If I remember correctly you're using Firefox? If this is just for yourself, you might want to change the security.fileuri.strict_origin_policy setting in about:config.

(Also, there used to be an attribute that would let you style the contents of an iframe, but it was apparently removed from the standard and it's not clear to me whether any browsers actually implemented it.)

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 27th, 2017, 7:58 am

chridd wrote:If I remember correctly you're using Firefox? If this is just for yourself, you might want to change the security.fileuri.strict_origin_policy setting in about:config.

(Also, there used to be an attribute that would let you style the contents of an iframe, but it was apparently removed from the standard and it's not clear to me whether any browsers actually implemented it.)

Yup, I am. And yeah, it's just for my personal use. Trying to replace my usage of Youtube (because Firefox is the most central application on my computer, always being opened when it's on) for music when I have all that music on my computer to begin with since my ISP is so crappy. And MDN doesn't even list the seamless attribute so it's likely that it never had widespread support.

I really wish all these security features weren't in place when it comes to running files locally. I understand where they're coming from but it gets in the way all too often. Stuff like this issue at least has workarounds. The other main one I've run into on multiple occasions is that Javascript cannot get a file's path. This is a huge pain in the ass when trying to automate stuff like adding new entries to my playlist since I can't just drag and drop files (though drag and drop operations are really a huge pain to even get cooperating to begin with) on to my page and have them added or anything. And I looked around for a workaround for that issue before but never came across anything other than people saying that it used to be possible but no longer is for security reasons.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 28th, 2017, 10:10 am

Man, for the life of me I'm struggling to wrap my head around how to do this right now. I'm trying to support arbitrary reordering of tracks on my playlist without having to move them inside the playlist file. To do so I'm storing a second array in local storage. This array is the same length as the playlist and contains the playlist entry for the file I want in that slot. For whatever reason though (possibly because I'm having trouble concentrating, I've got a headache right now actually, lol) I'm really struggling to figure out how to go about handling the reordering though.

All I want is just a simple function that I feed two numbers, the video I want to move and its new index. Then I need to reorder the rest of the array to account for these differences. What I've got so far is declaring a temporary array inside the function, setting the desired index to the file's index. Then I'm having trouble quite wrapping my head around what I need to do after that. It's not quite as simple as running a single loop and retrieving the corresponding file index from the original ordering array. This results in the moved song getting duplicated in the array as well as the one that used to be in its slot being removed from the array altogether. I know this should be pretty simple but I'm just really not getting it right now.
💙💙💙
Image
Image


User avatar
chridd
Posts: 210
Joined: December 25th, 2014, 9:20 pm
Location: the internet
Contact:

Re: Programming Thread

Postby chridd » February 28th, 2017, 4:54 pm

One way of doing it:
• To move an entry up or down by one entry, just swap the two items
• To move an entry up or down by more than one entry, move it up or down by one multiple times (this requires different code depending on whether you're moving up or down)
so, for instance, moving 1 to 3 would look like:

Code: Select all

0 1 2 3 4 5 original array
0 2 1 3 4 5 swapped indices 1,2
0 2 3 1 4 5 swapped indices 2,3


Another way: the splice method lets you remove or insert elements into an array

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » February 28th, 2017, 5:46 pm

chridd wrote:One way of doing it:
• To move an entry up or down by one entry, just swap the two items
• To move an entry up or down by more than one entry, move it up or down by one multiple times (this requires different code depending on whether you're moving up or down)
so, for instance, moving 1 to 3 would look like:

Code: Select all

0 1 2 3 4 5 original array
0 2 1 3 4 5 swapped indices 1,2
0 2 3 1 4 5 swapped indices 2,3


Another way: the splice method lets you remove or insert elements into an array

Since it's not simply swapping but moving indices it looks like the splice method would probably be what I'm looking for. (I'd actually forgotten that Array.splice() was even a thing, lol.) I'll have to have a look into that after I sleep but I think it should be fairly simple to do what I'm looking to accomplish with that.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » March 1st, 2017, 6:14 am

Haven't had a chance to get around to this yet but now that I've slept on it I'm pretty sure that Array.splice() actually can accomplish what I need. Something approximately like the following should work:

Code: Select all

function reorder(oldid, newid) {
    let firstpart, secondpart, thirdpart, lower, higher;
    if (oldid > newid) {
        higher = oldid;
        lower = newid;
    } else {
        higher = newid;
        lower = oldid;
    }
    for (let i = 0; i < lower; i++) {
       firstpart[i] = order[i];
    }
    for (let i = higher; i < order.length; i++) {
       thirdpart[i] = order[i];
    }
    //Then after this I'd need to deal with the new id related stuff then combine the three arrays and return that which is the more difficult part.
}
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » March 2nd, 2017, 1:21 pm

It just occurred to me that I might be really dumb, lol. I think I was massively overcomplicating that. (Actually I misread a vital part of the mdn page on the command.) I'd have to test it and I'm too tired to deal with any issues that might come up but I think I could do the entire thing with two calls to Array.splice(). I think something like this would do exactly what I want:

Code: Select all

function reorder(newid, oldid) {
    let tmp = order[oldid];
    order.splice(oldid, 1);
    order.splice(newid, 0, tmp);
}

I'll need to test it when I can better focus but I believe this code would do exactly what I need.

Edit: After a quick cursory test at least it looks to work.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » March 5th, 2017, 8:01 pm

I love managing to break things in inexplicable ways when trying to make it easier for me to debug things.

Code: Select all

function changetrack(/*caller, */track, auto) {
   //console.log(`Changing Track from ${caller}`);
   orgid = track;
   track = trackorder[track];
   //Prepare for playcount update
   playcountupdate = false;
   
   //If auto is not declared then set it to a blank string
   if (typeof auto === "undefined") {
      auto = '';
   }
   
   //Store video id to variable for ea access
   vidid = track;
   
   //Store last played track to local storage to resume session on page load
   lastrack = orgid;
   setItem("lastrack", orgid);

The rest of the function isn't really relevant but somewhere in here the track variable was somehow getting set to the caller variable. The only time this ever actually ended up happening was after I commented out the caller variable here and forgot to edit it out of one of the places I was calling it from.

I've done a decent amount of work on it lately though. (Although it'd be a good idea to stop for the day. I'd normally be going to bed right now but lucky me, I get to stay up several more hours because my mom's insisting I be up for dinner with my aunt.) I mostly fixed up the styling but I've fixed up various other issues as well. Most notably figuring out some minor bugs which could cause odd behavior when renaming tracks as well as using the wrong specific variable for setting the last played track which hilariously resulted in the player loading the wrong track when you reload the page. I only even noticed this one because it kept trying to load Alice Margatroid's theme when reloading the page to test some stuff with some Killswitch Engage songs.

Also added some other basic functionality such as shuffle and repeat options, the playlist now shows broken files (though due to limitations in Javascript getting it to move on to the next track is turning out to be a pain. Though I think I just thought of a solution. I'm already using the load start event for flagging a file as broken then the load end event to unflag it. I could probably set a timer of like half a second in the start event to change tracks then clear the timer in the loaded event (I don't think you can clear a timeout or else I'd do that instead) and have it go that way since these are all local files and load very quickly), and might have added support for checking if any songs are missing from the play order array after I managed to really screw it up and had to manually fix it up. (Which resulted in one song accidentally getting left out of the array. Though I fixed the particular bug that caused that to begin with. Turns out that I should be explicitly handling new indices higher than the array's length in the reordering function cause setting a song's index to higher than that number causes issues when populating the list since there's no associated songs in the playlist itself.)
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » March 6th, 2017, 6:13 pm

I decided to fix up part of my music player. It used to be that the populatelist() function was run in a lot of places. I cut it down so that it runs in far fewer places, instead of using strings and element.innerHTML = string to construct the list I now actually populate the list through creating elements, and I fixed up the styling of the buttons by adding/removing classes. Though I have noticed that this method is weirdly a lot laggier for some reason.

Code: Select all

//Populate playlist
function populatelist(id, caller) {
   console.log(`Repopulating List from ${caller}`);
   let shufchecked = '', repchecked = '';
   if (shuffle) {
      shufchecked = 'checked="true"';
   }
   if (repeat) {
      repchecked = 'checked="true"';
   }
   document.getElementById("controlscontainer").innerHTML = `<label><input type="checkbox" ${shufchecked}" onclick="shuffle = !shuffle; if (shuffle) { next = Math.round(Math.random() * musiclist.length); } else { next = vidid + 1; } if (next > musiclist.length) { next = 0; }">Shuffle</label><label><input type="checkbox" ${repchecked} onclick="repeat = !repeat; if (repeat) { curvid.loop = true; } else { curvid.loop = false; }">Repeat</label>`;
   
   list.innerHTML = '';
   for (let i = 0; i < musiclist.length; i++) {
      var order = trackorder[i];
      if (typeof order === "undefined" || order == null) {
         order = trackorder.length;
         trackorder[order] = order;
         setItem("trackorder", trackorder);
      }
      let file = musiclist[order].file,
         length = '',
         lyrics = '',
         play = "plays",
         title;
      if (typeof trackname[file] !== "undefined") {
         title = trackname[file];
      } else {
         title = file;
      }
      if (typeof duration[file] !== "undefined") {
         length = `(${durate(duration[file], "populatelist(if duration exists)")})`;
      } else {
         duration = getItem("duration", 0, "populatlist(if duration[file] is undefined)");
      }
      if (typeof playcount[file] === "undefined") {
         playcount[file] = 0;
      }
      if (typeof musiclist[order].lyrics !== "undefined") {
         lyrics = '♫';
      }
      if (playcount[file] === 1) {
         play = "play";
      }
      
      //Create new input element
      let ni = document.createElement("input");
      
      ni.type = "button";
      ni.classList.add("playlist");
      if (trackorder[i] === id) {
         ni.classList.add("curvid");
      }
      if (broken[file]) {
         ni.classList.add("broken");
      }
      ni.addEventListener("dblclick", function(e) {
         console.log(`Doubleclicked ${musiclist[e.target.dataset.track]}`)
         playlistchange(e.target.dataset.track);
      });
      ni.addEventListener("click", function(e) {
         setnext(e.target.dataset.track, this);
      });
      ni.title = `#${i}: ${title} ${length}`;
      ni.value = `${title} ${length} ${playcount[file]} ${play} ${lyrics}`;
      ni.dataset.track = i;
      
      let nexmatch = new RegExp(`${musiclist[next].file}`);
      if (ni.title.match(nexmatch)) {
         ni.classList.add("next");
      }
      
      if (order === id) {
         document.title = `[${length.replace(/\((.+)\)/, "$1")}] - ${title}`;
      }
      
      list.appendChild(ni);
   }
}

Not actually certain what it is about this that makes it lag worse. The least performant part is the nexmatch part and that's definitely not it because it had lag issues before this. I also didn't bother fixing up the repeat and shuffle parts of the function since those don't really matter anyways. I actually have a much bigger issue with my next song functionality seeming to have broken. And I'm not entirely certain why this is since I haven't even messed with that section of the code to begin with. I'll have to try and go through my code and check for inefficiencies and issues at some point. I think my next song variables are currently a huge mess that I'll have to work out. I could also fix up my changetrack() function to use proper element creation as well instead of abusing strings to create elements. Would probably make a few parts easier. Though I'm still not certain how properly creating elements is laggier than string abuse.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » March 10th, 2017, 3:38 pm

I think I finally got the issues with my next track setup not working quite properly ironed out. So long as the playlist is populated before it ever gets called I delegated it to a function of its own. So far though it's reliably worked in every case I've tried it.

Code: Select all

function getnext() {
   let listnext = document.getElementsByClassName("playlist"),
      count = 0,
      i, j, iat, varset = false,
      trackfound = false;
   for (i = 0; i < listnext.length; i++) {
      if (!trackfound) {
         for (j = 0; j < listnext[i].classList.length; j++) {
            if (!trackfound) {
               var curvidtext = false;
               if (listnext[i].classList[j] !== "currvid") {
                  curvidtest = true;
               } else {
                  trackfound = true;
                  console.log(`Track found at ${i}`);
                  console.log(listnext[i]);
               }
               if (curvidtest) {
                  count++;
               }
            }
         }
      } else {
         if (!varset) {
            console.log(`Track found at ${i}`);
            iat = i;
            varset = true;
         }
      }
   }
   next = parseInt(listnext[iat].dataset.track);
   populatelist(vidid, "getnext()");
}

It may be a bit overcomplicated (it definitely was this morning since I deleted about a third of the code and replaced it with the "next = parseInt(listnext[iat].dataset.track);" line) because I've worked on it a bit at a time over the past three or so days, often shortly after getting up and with a headache so I'm surprised it even works at all. Though I do appear to have messed one bit up at some point which I'll have to look into. The song duration in the playlist view is supposed to dynamically populate as soon as the song loads if it doesn't already exist. Currently that's only happening when the track changes instead though. I think it's because I introduced a bug at some point where songs that don't load properly would still try to populate their duration value and I tried to add in a failsafe check to make sure this didn't happen. Must have broken things when that happened.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » March 23rd, 2017, 1:32 am

I decided to try my hand at making a userscript to re-add the playlist duration to playlist pages on Youtube since they randomly removed it for no good reason.

Code: Select all

let timestamps = document.getElementsByClassName("timestamp"),
   buttons = document.getElementsByTagName("button"),
   duration, durel = document.createElement("li"),
   time = {
      day: 0,
      hour: 0,
      minute: 0,
      second: 0
   };

document.getElementsByClassName("pl-header-details")[0].appendChild(durel);

timefunc();

let buttoncheck = setInterval(function() {
   for (let i = 0; i < buttons.length; i++) {
      if (buttons[i].innerText === "Load more") {
         buttons[i].addEventListener("click", function() {
            setTimeout(timefunc, 1000);
         });
      }
   }
}, 2000);

function timefunc() {
   duration = 0;
   time.day = 0;
   time.hour = 0;
   time.minute = 0;
   time.second = 0;
   for (let i = 0; i < timestamps.length; i++) {
      let tmp = timestamps[i].childNodes[0].innerText.split(":");
      for (let j = tmp.length-1; j >= 0; j--) {
         let modifiers = [];
         if (tmp.length === 3) {
            modifiers[0] = 3600;
            modifiers[1] = 60;
            modifiers[2] = 1;
         } else if (tmp.length === 2) {
            modifiers[0] = 60;
            modifiers[1] = 1;
         } else {
            modifiers[0] = 1;
         }
         time.second += parseInt(tmp[j]) * modifiers[j];
         while (time.second > 59) {
            time.second -= 60;
            ++time.minute;
         }
         while (time.minute > 59) {
            time.minute -= 60;
            ++time.hour;
         }
         while (time.hour > 23) {
            time.hour -= 24;
            ++time.day;
         }
      }
   }
   if (time.day > 0) {
      durel.innerHTML = `Duration: ${time.day}:${pad2(time.hour)}:${pad2(time.minute)}:${pad2(time.second)}`;
   } else if (time.hour > 0) {
      durel.innerHTML = `Duration: ${time.hour}:${pad2(time.minute)}:${pad2(time.second)}`;
   } else {
      durel.innerHTML = `Duration: ${time.minute}:${pad2(time.second)}`;
   }
}

function pad2(input) {
   let output = input.toString();
   while (output.length < 2) {
      output = "0" + output;
   }
   return output;
}

Seems to work just fine. The setInterval shenanigans are probably not good though since I think it will actually cause a memory leak (though likely a very small one that should take quite a long time to have a noticeable impact at least) since it theoretically will keep adding a new event listener every time the timer activates. Not entirely certain how to best go about that part. Handling it that way is necessary since playlist views load videos in chunks of hundreds meaning that if you click the first "load more" button they create a new one and the duration will stop updating properly.

As an example though it reports that this playlist has a duration of one day, 23 hours, two minutes, and 59 seconds.
💙💙💙
Image
Image


User avatar
Ashan
Posts: 503
Joined: February 15th, 2015, 1:33 am
Location: Saskatchewan
Contact:

Re: Programming Thread

Postby Ashan » May 20th, 2017, 4:40 am

Google announced at I/O this year that Kotlin is now becoming a first-class supported language for Android, so I think I'm gonna take the opportunity to learn Kotlin. I've never strayed far from the C-style languages I'm most comfortable with, but some of the streamlined approaches you get and different ways of thinking with modern languages like Kotlin, Swift, Scala, etc. seems like a good skill to have for the future.
Image
► Show Spoiler

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » July 16th, 2017, 3:57 am

I really freaking hate CSS sometimes. For whatever reason I have nothing but problems trying to position shit in a way that works. I typically jsut end up giving up and using Javascript though to create monstrosities such as this:

Code: Select all

//Freaking annoying resizing wizardry that should be done through CSS which never freaking cooperates with me
function resize(obj, objprop, style) {
   resizeobj[resizeobj.length] = {
      o: {
         o: obj,
         p: objprop
      },
      s: style
   }
   resizer();
}
function resizer() {
   for (let i = 0; i < resizeobj.length; i++) {
      resizeobj[i].o.o.style[resizeobj[i].o.p] = eval(resizeobj[i].s[0]) + resizeobj[i].s[1];
   }
}
window.addEventListener('resize', function(e) {
   clearInterval(timer);
   timer = setTimeout(resizer, 20); //Timer is a variable I initialize at the start of the page, otherwise clearing the timer doesn't actually work and it just ends up queuing up a bunch of the resize() function instead which is the opposite of what I want.
});

This forces things to actually accommodate things as I want, which is having a table displayed next to some text without having one element or the other overrun the rest.

So to explain what things are there: resizeobj is an array of objects I want to deal with when resizing the window. This is further split into two parts. o is an html object and a style property I want to affect for it, s is how I want to affect that property. What you can't see there is that s is an array split into two parts. The place I used this so far after having worked things out is like this:

Code: Select all

resize(curasides[i], 'width', [ 'page.clientWidth-bounds($("#types")).width-96', 'px' ]);

curasides is just a temporary collection of html objects since it was easier to drop them into an array then iterate through them bounds() is simply a shortcut to this:

Code: Select all

function bounds(obj) {
   return obj.getBoundingClientRect();
}

because screw typing in all that every damned time I want it. It's bad enough that I have to look it up the first time I want to use it in a given project every god damned time. The $('#currency-types') bit is a simple jQuery style shortcut just to save me some time. It just does this:

Code: Select all

function $(val) {
   var f = val.charAt(0)
   switch (f) { //I could probably just move the val.charAt(0) to the f's spot here now that I think about it
      case "#":
         val = val.replace(/#(.+)/, "$1");
         return document.getElementById(val);
      case ".":
         val = val.replace(/\.(.+)/, "$1");
         return document.getElementsByClassName(val);
      default:
         return document.getElementsByTagName(val);
   }
}

which saves me having to type all that in each and every time. But anyways curasides is just that collection, 'width' being the property of curasides I want to affect (specifically style.width but I wasn't going to attempt to figure out a better way of handling that so I just hard coded in the style part), then an array containing a string of the values I want to use when I resize (because otherwise I just get a raw number which, as far as I know, won't change when things resize thus making it useless for this purpose), and then the 'px' tacked on as a separate entry because getting eval to work with that stuff was a pain to begin with and trying to get it to deal with the px part too would've been a huge pain.
💙💙💙
Image
Image


User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » July 31st, 2017, 8:43 pm

I'd like to know what the hell I'm managing to do to break this if check.

Code: Select all

if (keycode === 38) { //up arrow
   console.log(cEngine.core.gObjectNum, "<", cEngine.core.mAllObjects.length, cEngine.core.gObjectNum < cEngine.core.mAllObjects.Length-1);
   if (cEngine.core.gObjectNum < cEngine.core.mAllObjects.Length-1) {
      console.log("gObject up");
      cEngine.core.gObjectNum++;
   }
}

That console.log is printing out the exact values of the following if check as well as whether it's true. When I go through it all the console spits out this:
0 < 4 false

I'm trying to follow this tutorial I got on 2d game engine physics in Javascript but it's not going all that well. (To clarify things, gObjectNum is simply an integer that starts at 0 and is capped at the length of mAllObjects' length -1, with mAllObjects being an array of all the rigid bodies in the scene.)

On top of it not really explaining quite a few details I'm doing things a bit differently than the people who wrote it. Ie: I'm using classes for stuff instead of using functions in place of classes. Which brought along its own problems and I'm still not sure why it didn't cooperate itself. For example the physics currently has a rigid bodies class then two classes that extend that for circular and rectangular objects. If I don't call super() in the constructors for the rectangle and circle then I get some errors about the object not even existing when I try to set its properties. And if I do call super() then it appears to flat out ignore everything in the rigid bodies constructor thus making the whole extension system a bit of a moot point. For example both circles and rectangles have a center property declared. This is the base rigid bodies class:

Code: Select all

class rigidShape {
   constructor(center) {
      this.mCenter = center;
      this.mAngle = 0;
      cEngine.core.mAllObjects.push(this);
   }
}


I'm quite explicitly declaring the center of the shape there. Now if we go and look at the one for circles since it's the shorter of the two:

Code: Select all

class Circle extends rigidShape {
   constructor(center, radius) {
      super();
      this.mCenter = center;
      this.mType = "Circle";
      this.radius = radius;
      this.mStartPoint = new Vec2(center.x, center.y);
   }
}

You'll notice I'm calling super() then having to manually set this.mCenter right after it. Because if I don't then this.mCenter simply never even gets set to begin with so when the rendering setup tries to access it, it gets an undefined value.

Edit:
Image

Code: Select all

console.log(cEngine.core.gObjectNum, "<", cEngine.core.mAllObjects.length, cEngine.core.gObjectNum < cEngine.core.mAllObjects.Length-1);
console.log(cEngine.core.gObjectNum, ">", 0, cEngine.core.gObjectNum > 0);

Right. So 0 is not bigger than 0 but it's also not smaller than 4. That uh... Makes sense I guess??

Edit2:
Image
Even better:

Code: Select all

console.log(cEngine.core.gObjectNum, "<", cEngine.core.mAllObjects.length, cEngine.core.gObjectNum < cEngine.core.mAllObjects.Length, 0 < 4);

0 < 4 = false but immediately after that 0 < 4 = true. I'm really not sure why this is happening but it appears to be an issue with mAllObjects.length for some reason. It returns a value of 4 but insists that 0 isn't than 4 no matter how I compare a 0 to it.

Edit3:
Oh for fuck sake. It's because I kept typing mAllObjects.Length instead of mAllObjects.length. I've been following the tutorials stupid naming scheme and that's really screwing me up.
💙💙💙
Image
Image


User avatar
Ashan
Posts: 503
Joined: February 15th, 2015, 1:33 am
Location: Saskatchewan
Contact:

Re: Programming Thread

Postby Ashan » August 3rd, 2017, 3:10 am

Holy cow

So, I've been trying to really double down on my Android development learning lately. I start my first software dev job next month, and I feel like I should get some sort of real experience under my belt, not just basic implementations of data structures and algorithms in isolated C++ projects.

I picked up this "Quotr" app of mine that I started working on early last year. It was pretty basic, basically just displayed Quotes, had some fun animations implemented, etc. Every once in awhile I'd throw in a little tweak or something, but now I kinda want to make it into a proper, usable thing, so I've been implementing database storage with SQLite so the app saves data between launches, I added a RecyclerView to see the quotes you have stored, today I made it so you can select one to jump to it, and I'm planning on adding editing/deleting soon.

ANYWAY, I've been having some serious issues with the lifecycle of the activities in my app. Particularly, I was having startup/cleanup code running at really weird points in my app's lifecycle. The first new activity I added to the app was the one that allowed you to add a quote. So, seeing as it was the first ever new activity I added, I didn't really know what I was doing yet. Fast forward to now where I'm a bit better, and I didn't catch this glaring mistake.
The problem I was having: Whenever I'd launch the AddQuote activity, onCreate() would be called from my main activity. When I finished the AddQuote activity, onDestroy() of my main activity would be called. This was causing big issues, because I closed my database connection and reset the cursor that kept track of where you were in the quote list in the onDestroy() method. And it's just generally bad that onCreate() was being called multiple times because it's a waste of resources.
What was causing it: Turns out, since I had no idea what I was doing, I made the child activity extend the main/parent activity. Really, I should have it extending AppCompatActivity, but extending the main class worked, so that's what I did. The issue came in when I was doing the initialization and destruction of the child class, I'd make a call to the super.onCreate() and super.onDestroy() like you're supposed to do, but because I was extending the parent class, the super method was the main activity's onCreate() and onDestroy() which made absolutely no sense to be executing at those points in time. I'm honestly surprised I was able to work with this for so long before figuring it out. But I'm so glad that I did finally figure it out, because things actually work like I intended them to now.
Image
► Show Spoiler

User avatar
Alice
⦂☽
Posts: 3813
Joined: December 23rd, 2014, 10:47 pm
Location: Wonderland
Contact:

Re: Programming Thread

Postby Alice » August 3rd, 2017, 11:37 am

Ashan wrote:The issue came in when I was doing the initialization and destruction of the child class, I'd make a call to the super.onCreate() and super.onDestroy() like you're supposed to do, but because I was extending the parent class, the super method was the main activity's onCreate() and onDestroy() which made absolutely no sense to be executing at those points in time.

Speaking of super methods I figured out the issue I mentioned above with my own super methods after I last edited the post. Turns out that in Javascript at least, when you call super() you need to feed values into it. So for my rigidshape class I'd have this:

Code: Select all

class rigidShape {
   constructor(center) {
      this.mCenter = center;
      this.mAngle = 0;
      cEngine.core.mAllObjects.push(this);
   }
   
   update() {
      if (this.mCenter.y < cEngine.core.mHeight && !this.mFix) {
         this.move(new Vec2(0, 1));
      }
   }
}

and under my Rectangle class I needed this:

Code: Select all

class Rectangle extends rigidShape {
   constructor(center, width, height, fix = false) {
      super(center);

The example code in the tutorial just used super() by itself so I'd assumed that it would just compare the constructor values between the two classes and if the proper values for the parent class existed then it would automatically use them. (I've never really messed with extending classes before.)

It's always an interesting learning experience to figure out issues like this or the ones you were having. (Though ones like my issue with Array.Length versus Array.length are just infuriating, lol.)
💙💙💙
Image
Image



Return to “General Perversion”

Who is online

Users browsing this forum: No registered users and 1 guest