Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
reversefungi
Nov 27, 2003

Master of the high hat!

Suspicious Dish posted:

I think with a simple comment like // Retrieve everything up to and including "bar" it can be explained.

Personally I would create a small well-named function that abstracts the actual parsing logic (with whatever implementation you desire) so you don't have to worry about it actively, and just rely on the name of the function to explain what it is you're trying to do.

Adbot
ADBOT LOVES YOU

porksmash
Sep 30, 2008

Roadie posted:

If you're doing it all client-side, I'd just use lodash/fp.

This was two pages ago, but I had a go at it and I'd appreciate anyone that has more than 5 minutes of FP experience telling me how wrong I did it.

https://github.com/PeteAndersen/swarfarm-fe/commit/4ee5163c8ef18f4dcfb0badf8909dfd225706aab#diff-b32ad6e35f8970e39184af22c965237b

In short, the code is supposed to allow me to filter an array of a couple thousand objects based on my Redux state, which describes various filters. The filters can run against any object key or sub-object (of which there are a couple). The code transforms those filters into functions, runs them against a given array of objects, and returns the resulting list that pass all the filters. There's an example filter state representation in comments at the bottom of the linked code file. The objects it is running against are here: https://swarfarm.com/api/v2/monsters/

Thanks to Roadie for pointing me at FP, it's been a .... fun learning experience.

porksmash fucked around with this message at 01:15 on May 18, 2018

Chenghiz
Feb 14, 2007

WHITE WHALE
HOLY GRAIL

The Dark Wind posted:

Personally I would create a small well-named function that abstracts the actual parsing logic (with whatever implementation you desire) so you don't have to worry about it actively, and just rely on the name of the function to explain what it is you're trying to do.

This.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Always be making functions.

fantastic in plastic
Jun 15, 2007

The Socialist Workers Party's newspaper proved to be a tough sell to downtown businessmen.
Good naming is important, that's why I name all of my functions after pokemon.

Skandranon
Sep 6, 2008
fucking stupid, dont listen to me

fantastic in plastic posted:

Good naming is important, that's why I name all of my functions after pokemon.

BulbasaurSort() is SUPER EFFECTIVE!

Doom Mathematic
Sep 2, 2008

fantastic in plastic posted:

Good naming is important, that's why I name all of my functions after pokemon.

Now show me on this whiteboard how you would reverse a Sudowoodo.

unpacked robinhood
Feb 18, 2013

by Fluffdaddy
e: edited in my current answer

How would you make ajax queries in a loop, to request fresh content from the target url as soon as a request is done, and update a text area ? (And at some point have some sort of cooldown timer to lower the refresh rate)
I'm using MooTools

My current attempt was this
JavaScript code:
      function read_stream(){
      var db_status_box = document.getElement('#stream_field'); //the text field I'm targeting
      var myRequest = new Request({
         url: '/streamed_data/',
         method: 'get',
        onProgress: function(event, xhr){
        console.log("read_stream",xhr.responseText);
        read_stream(); //added this call
    }
});
myRequest.send();
    }
Calling this function inside a loop sends a ton of requests at once, expectedly.

Google give me lots of people running ajax requests while iterating on arrays, which isn't what I'm trying to do.

unpacked robinhood fucked around with this message at 11:49 on May 18, 2018

ynohtna
Feb 16, 2007

backwoods compatible
Illegal Hen
Add something like onSuccess: function() { setTimeout(read_stream, COOLDOWN_DURATION); } to your Request definition?

Osmosisch
Sep 9, 2007

I shall make everyone look like me! Then when they trick each other, they will say "oh that Coyote, he is the smartest one, he can even trick the great Coyote."



Grimey Drawer

porksmash posted:

This was two pages ago, but I had a go at it and I'd appreciate anyone that has more than 5 minutes of FP experience telling me how wrong I did it.

https://github.com/PeteAndersen/swarfarm-fe/commit/4ee5163c8ef18f4dcfb0badf8909dfd225706aab#diff-b32ad6e35f8970e39184af22c965237b

In short, the code is supposed to allow me to filter an array of a couple thousand objects based on my Redux state, which describes various filters. The filters can run against any object key or sub-object (of which there are a couple). The code transforms those filters into functions, runs them against a given array of objects, and returns the resulting list that pass all the filters. There's an example filter state representation in comments at the bottom of the linked code file. The objects it is running against are here: https://swarfarm.com/api/v2/monsters/

Thanks to Roadie for pointing me at FP, it's been a .... fun learning experience.

Looks fine! I think I'd use _.pick over
code:
filters.map(filter => get_filter_func(filter));
- in general if you're going from keysets to valuesets that saves a bunch of effort.

reversefungi
Nov 27, 2003

Master of the high hat!

unpacked robinhood posted:

e: edited in my current answer

How would you make ajax queries in a loop, to request fresh content from the target url as soon as a request is done, and update a text area ? (And at some point have some sort of cooldown timer to lower the refresh rate)
I'm using MooTools

My current attempt was this
JavaScript code:
      function read_stream(){
      var db_status_box = document.getElement('#stream_field'); //the text field I'm targeting
      var myRequest = new Request({
         url: '/streamed_data/',
         method: 'get',
        onProgress: function(event, xhr){
        console.log("read_stream",xhr.responseText);
        read_stream(); //added this call
    }
});
myRequest.send();
    }
Calling this function inside a loop sends a ton of requests at once, expectedly.

Google give me lots of people running ajax requests while iterating on arrays, which isn't what I'm trying to do.

Do you have the option of using async/await? If so, that helps making asynchronous loops incredibly straightforward and simple.

unpacked robinhood
Feb 18, 2013

by Fluffdaddy

ynohtna posted:

Add something like onSuccess: function() { setTimeout(read_stream, COOLDOWN_DURATION); } to your Request definition?

Works like a charm, thanks.

The Dark Wind posted:

Do you have the option of using async/await? If so, that helps making asynchronous loops incredibly straightforward and simple.

I don't know what those are but it's basically a personal project so I can do whatever.

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

unpacked robinhood posted:

Works like a charm, thanks.


I don't know what those are but it's basically a personal project so I can do whatever.

It makes async stuff act as if it was sync:

JavaScript code:
const DELAY_VAL = 10;

const  myThing = async () => {
    const results = await read_stream()
    // do stuff with results
    setTimeout( myThing, DELAY_VAL )
}
myThing()

necrotic
Aug 2, 2005
I owe my brother big time for this!
But it's not magic, the read_stream has to return a promise or also be async. In this scenario it works great with the new built-in fetch which returns a Promise.

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

necrotic posted:

But it's not magic,

Wait. It's not? fffffff... I think I need to go re-write some – no, make that a LOT of – stuff quick.

ynohtna
Feb 16, 2007

backwoods compatible
Illegal Hen

Lumpy posted:

Wait. It's not? fffffff... I think I need to go re-write some – no, make that a LOT of – stuff quick.

:ohdear:


Oh and unpacked robinhood, be sure to partner any onSuccess handler with an onError otherwise a request failure will invisibly cease the repeated data refresh.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
Is there an ES6-to-uglified-javascript compilation process that isn't a giant pain in the rear end to set up? I want to be able to do it from a command line, I don't want to install Java, and I want to have an easy path to debugging. The only thing I really want from ES6 is modules/namespaces/exports, which unfortunately seems to make running unmodified scripts in Chrome for debugging purposes not work.

I tried google-closure-compiler-js; the command line interface seems to ignore the flags it's described as using so it only ever removes whitespace and doesn't really uglify or do module wrangling, and npm installing a bunch of garbage to support its use another way ends up complaining about undeclared variables that are clearly declared and aren't mentioned at the places the errors are claimed to be at (with its webpack support), and then trying to run it complains $jscomp is not defined (which is supposed to be a polyfill), and the 'gulp' wrapper just doesn't work as described at all.

Before that I got about a hundredth of the way through the giant loving mess of hoops installing a Babel command line seems to require, and with the expectation that it's going to fail to mention a bunch of steps since right from the getgo it started out just assuming I had npm installed already.

Oh, I also tried typescript briefly, and foolishly made a copy of my original js file with a ts extension, ran the typescript compiler on it, didn't like what it was doing so I deleted the .ts file, not realizing the compiler had silently overwritten my original modern js file with an ugly backward-compatible version, so I had to spend half an hour undoing that poo poo.

I disliked typescript because apparently by design it hates this:
code:
function foo(a, b, c, d, e, f) {}
function bar() {return [1, 2];}
foo(...bar(), ...bar(), ...bar());
(It complains that foo requires 6 parameters and is passed at least 0. And calls this an error.)

Should I just give up on the idea of moduling my code? I've done modules, but only in a context where all the build tools are already set up, I'm just not convinced it's worth the effort of figuring out all the build tools for my little home projects.

roomforthetuna fucked around with this message at 07:10 on May 19, 2018

necrotic
Aug 2, 2005
I owe my brother big time for this!
I've heard good things about parcel.

https://parceljs.org/

For Babel you really only need babel-env to get running with es6.

Tanners
Dec 13, 2011

woof

roomforthetuna posted:

code:
function foo(a, b, c, d, e, f) {}
function bar() {return [1, 2];}
foo(...bar(), ...bar(), ...bar());

Typescript here wants you to indicate that those parameters are optional. You can add a ? To the end of the parameter names and that should compile fine.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Tanners posted:

Typescript here wants you to indicate that those parameters are optional. You can add a ? To the end of the parameter names and that should compile fine.
I suppose that works okay if you don't mind writing wrappers around things, eg, if foo in my example had been canvasContext.bezierCurveTo. The deeper problem really is that there's no way to define a type as "an array of exactly 2 elements" or, even better, "an array that has an even number of elements that is at least 2".

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

roomforthetuna posted:

I suppose that works okay if you don't mind writing wrappers around things, eg, if foo in my example had been canvasContext.bezierCurveTo. The deeper problem really is that there's no way to define a type as "an array of exactly 2 elements" or, even better, "an array that has an even number of elements that is at least 2".

You can specify an array of fixed length as a tuple in typescript.
e.g.
code:
let x: [number, number];

x = [1,2,3].... <-- fails compile because array does not have three elements that are numbers
For the second problem, you can't express modulo length as a type in typescript, but you could instead represent it as an array of tuples:

e.g.
code:
let x: [number, number];
let y: Array<[number,number]>;

y =[[1, 2], [3,4]];

Roadie
Jun 30, 2013
I will second the recommendation for Parcel. So far it seems to be about a million times easier to use than Webpack while still fully extensible.

roomforthetuna posted:

I disliked typescript because apparently by design it hates this:
code:
function foo(a, b, c, d, e, f) {}
function bar() {return [1, 2];}
foo(...bar(), ...bar(), ...bar());
(It complains that foo requires 6 parameters and is passed at least 0. And calls this an error.)

You need to explicitly define a tuple type for bar, as otherwise it will infer an array of indeterminate length.

JavaScript code:
function bar(): [number, number] {return [1, 2];}

roomforthetuna posted:

I suppose that works okay if you don't mind writing wrappers around things, eg, if foo in my example had been canvasContext.bezierCurveTo. The deeper problem really is that there's no way to define a type as "an array of exactly 2 elements"

Actually, that one's easy.

JavaScript code:
type SpecialTuple = [any, any];

Roadie fucked around with this message at 09:16 on May 20, 2018

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Roadie posted:

You need to explicitly define a tuple type for bar, as otherwise it will infer an array of indeterminate length.
Ah, fair enough, I just didn't give typescript enough of a chance, then. And its eating my original source without warning I guess can be considered my fault, I shouldn't have assumed "tsc xxx.ts" wouldn't silently overwrite xxx.js with its output.

Roadie
Jun 30, 2013

roomforthetuna posted:

Ah, fair enough, I just didn't give typescript enough of a chance, then. And its eating my original source without warning I guess can be considered my fault, I shouldn't have assumed "tsc xxx.ts" wouldn't silently overwrite xxx.js with its output.

Using tsc directly will just output a minimally transpiled (based on your tsconfig settings) .js version of its contents. For anything past that you need to use a bundler like Webpack or Parcel.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
Hm, I just noticed scrolling back up that I never actually posted my reply where I said thanks for the parcel suggestion, it is hugely less frustrating than trying to set anything else up, and the results aren't bad at all.

I think I started elaborating on "thanks" then went looking for an example of something, and got distracted. So, thanks!

Aha, I found the tab with the unposted post in it.

quote:

Thanks! Working in minutes, and helpful errors ("x is readOnly" where I tried to set an imported value, rather than "x is not defined" that I was getting with google-closure-js).

A little bit shaky on the debug build - it's mostly good, but it seems to have a problem with colliding map files such that it debugs into the wrong code (and I only have 4 files right now!)

But the "no configuration" promise was pretty much true, and it does exactly what I wanted.

prisoner of waffles
May 8, 2007

Ah! well a-day! what evil looks
Had I from old and young!
Instead of the cross, the fishmech
About my neck was hung.
Steadily losing confidence as I attempt to determine why my machine transpiles the code like _this_, but the Jenkins machine transpiles it like _that_.

`yarn.lock`s are identical, gulp versions are identical, code is identical. Yarn versions are not identical. Broken transpiled version (generated by the machine that has the older version of Yarn) appears to be including 3 copies of react. Thoughts? (prayers?)

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

prisoner of waffles posted:

Thoughts? (prayers?)
I think "oh thank goodness, it's not just me who finds all this stuff to be a loving mess."
And I hope you can transition to something less grotesque.

geeves
Sep 16, 2004

prisoner of waffles posted:

Steadily losing confidence as I attempt to determine why my machine transpiles the code like _this_, but the Jenkins machine transpiles it like _that_.

`yarn.lock`s are identical, gulp versions are identical, code is identical. Yarn versions are not identical. Broken transpiled version (generated by the machine that has the older version of Yarn) appears to be including 3 copies of react. Thoughts? (prayers?)

What are you using for transpliation? What about Node and NPM versions? Different versions?

I've had mis-matched versions of Node and NPM cause weirdness between environments and semi-related, I've had issues with ^ and ~. We have gone through and removed them and wrote a githook for my company to check for their existence in package.json and stop commits.

tracecomplete
Feb 26, 2017

geeves posted:

We have gone through and removed them and wrote a githook for my company to check for their existence in package.json and stop commits.

This is a good idea if your developers are disciplined, but the disciplined part is pretty important. I have clients for whom I reluctantly recommend at least using ^ because they're never going to actually check their dependencies after they blindly add them to the project.

prisoner of waffles
May 8, 2007

Ah! well a-day! what evil looks
Had I from old and young!
Instead of the cross, the fishmech
About my neck was hung.
Mildly different versions of yarn, one of which is smart enough to realize that multiple versions of react make for trouble.

Multiple versions of react, depended on by several internal packages.

Okay, bump up the internal package dependency versions!

Internal packages' (`npm run`-based) build process has been changed and dumps all its transpiled code into `package/dist/`, breaking all import paths.

Okay, fix the build process!

what is consistent, reproducible, good practice and why would anyone care

Secx
Mar 1, 2003


Hippopotamus retardus
I'm trying to create a Tampermonkey (Greasemonkey) script for an enterprise application we use at work. It looks to be written in Angular.

The problem is that the DOM element that TM has access to is not what is rendered on the screen, so my script is failing when trying to perform a simple document.querySelector as the element can't be found. If I do a view -> page source, this is what I get:

code:

<!doctype html> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"> <title>Inprotech</title><script>window.INPRO_INCLUDE_E2E_PAGES = false;</script><script>window.INPRO_DEBUG = false;</script> <script data-concat="false"> if (!window.INPRO_DEBUG && (window.location.hash === '' || window.location.hash.match(/#\/dashboard/gi))) {
            window.location = './#/home';
        } </script> <link rel="shortcut icon" href="favicon.ico"> <link rel="stylesheet" href="styles/condor-app.min.24d8a0a7.css"></head> <body ng-app="inprotech" page-state="{{pageCssClass}}" ng-strict-di=""> <ip-top-header> </ip-top-header> <main-content class="main-outer-layer"> </main-content> <script src="condor/lib.min.1ed47049.js"></script><script src="condor/papaparse.min.js"></script><script src="condor/app.min.8c98aeb0.js"></script></body> </html>
The real page seems to be in "condor/app.min.8c98aeb0.js".

If I ask TM to dump the document.body element to the console, I get a DOM with a bunch of <div>, but still not what is being rendered on the screen.

Is there a way for my TM script to get access to the DOM that is actually rendered on the screen?

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Secx posted:

I'm trying to create a Tampermonkey (Greasemonkey) script for an enterprise application we use at work. It looks to be written in Angular.

The problem is that the DOM element that TM has access to is not what is rendered on the screen, so my script is failing when trying to perform a simple document.querySelector as the element can't be found. If I do a view -> page source, this is what I get:

code:

<!doctype html> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"> <title>Inprotech</title><script>window.INPRO_INCLUDE_E2E_PAGES = false;</script><script>window.INPRO_DEBUG = false;</script> <script data-concat="false"> if (!window.INPRO_DEBUG && (window.location.hash === '' || window.location.hash.match(/#\/dashboard/gi))) {
            window.location = './#/home';
        } </script> <link rel="shortcut icon" href="favicon.ico"> <link rel="stylesheet" href="styles/condor-app.min.24d8a0a7.css"></head> <body ng-app="inprotech" page-state="{{pageCssClass}}" ng-strict-di=""> <ip-top-header> </ip-top-header> <main-content class="main-outer-layer"> </main-content> <script src="condor/lib.min.1ed47049.js"></script><script src="condor/papaparse.min.js"></script><script src="condor/app.min.8c98aeb0.js"></script></body> </html>
The real page seems to be in "condor/app.min.8c98aeb0.js".

If I ask TM to dump the document.body element to the console, I get a DOM with a bunch of <div>, but still not what is being rendered on the screen.

Is there a way for my TM script to get access to the DOM that is actually rendered on the screen?

Watch for changes until it can find the element you want.

JavaScript code:
const options = {
    childList: true
};
const watcher = new MutationObserver(mutEvents => {
    mutEvents
        .filter(mutEvent => !!document.querySelector('whatever you're looking for'))
        .forEach(mutEvent => doYourThing);
});
watcher.observe(document.body, options);

Secx
Mar 1, 2003


Hippopotamus retardus

Munkeymon posted:

Watch for changes until it can find the element you want.

JavaScript code:
const options = {
    childList: true
};
const watcher = new MutationObserver(mutEvents => {
    mutEvents
        .filter(mutEvent => !!document.querySelector('whatever you're looking for'))
        .forEach(mutEvent => doYourThing);
});
watcher.observe(document.body, options);

Thanks!

Just as I posted my question here, I rephrased it on Google and found this SO post: https://stackoverflow.com/questions/36848337/modify-dom-elements-in-angular-js-in-greasemonkey

Including this waitForKeyElements.js script that did the job for me.

Dominoes
Sep 20, 2007

I'm struggling hard on this: Loads of Googling. How can I configure MIME types with create-react-app? Per this guide: "Note: To run with instantiateStreaming and compileStreaming, you need your webserver to serve .wasm file with application/wasm MIME type. The https crate can be used to serve files from localhost, and includes the application/wasm MIME type out of the box." I'm using CreateReactApp.

Indeed, I get a MIME error in the JS console. A Google search revealed no way to configure this. Mozilla recommends instantiateStreaming over the depricated WebAssembly.instantiate. The latter doesn't work for me either, if using stdweb or bindgen: It wants a second argument which I'm not sure how to provide.

velvet milkman
Feb 13, 2012

by R. Guyovich
Does anyone have any modern D3.js tutorials or anything they can recommend as a good introduction? I'm pretty overwhelmed by the amount of poo poo available.

huhu
Feb 24, 2006
When working on a nodejs site, how would I go about implementing something like an admin panel? With Django it comes out of the box. With node, I can't really tell from searching.

spiritual bypass
Feb 19, 2008

Grimey Drawer
Node is a language/server combo like Python + Gunicorn. You'd need to find a heavy backend framework like Django for Node that has a similar feature.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

huhu posted:

When working on a nodejs site, how would I go about implementing something like an admin panel? With Django it comes out of the box. With node, I can't really tell from searching.

The Django admin page is just a Django site packaged with Django.

Same thing with a site powered by node...you just implement a site like you would normally (which usually includes libraries to help you build a site since node is pretty "low level" with respect to building a web site), but build the UI around your expectations for what your admins need instead of what your end useres need.

huhu
Feb 24, 2006
I have to say, for all the "what the hell is this" I had while reading up on Mongo/Express/Node, it took me all of about 8 hours today to learn the basics and then get an API setup serving content.

For those that are curious, I used - https://www.udemy.com/nodejs-master-class/learn/v4/content

Adbot
ADBOT LOVES YOU

TheFluff
Dec 13, 2006

FRIENDS, LISTEN TO ME
I AM A SEAGULL
OF WEALTH AND TASTE

huhu posted:

I have to say, for all the "what the hell is this" I had while reading up on Mongo/Express/Node, it took me all of about 8 hours today to learn the basics and then get an API setup serving content.

For those that are curious, I used - https://www.udemy.com/nodejs-master-class/learn/v4/content

That is kinda what's dangerous about it. It's very easy to get started with, the syntax is familiar, you don't have a strict typechecker that yells at you, you don't have do any database design, and so on - you just kinda run a few npm commands and off you go. The problem is that none of this poo poo scales, at all. There are things that node is legitimately useful for and I guess there are also things that mongodb is useful for, but if you're just writing a general-purpose web application, you're shooting yourself in the foot. If you're writing business logic in a language that has no integer datatype at all and where the most complex data structure is an unordered string-keyed map (ok fine you get sets in ES6, whatever), you should seriously stop and ask yourself what on earth you're doing. There is no standard library, there's barely even a module system, and everything is just a gigantic mess in general. To do something as trivial as get the intersection of two arrays you have to go off and install Lodash, and if you start asking yourself about how to do things with these fancy new async/await keywords you'll soon find that it's just promises all the way down.

TypeScript at least makes parts of the experience tolerable, but fundamentally, JS is just not very well suited to developing any kind of complex application. If you're writing some small functions for the new ~serverless~ hype, then sure, fine, it's actually pretty fast so go for it. If you're writing a traditional web app with a traditional database behind it, then just... pick literally anything else other than PHP and you'll be better off. I need to get myself out of this poo poo soon or I'll get stuck doing it forever.

TheFluff fucked around with this message at 23:30 on May 30, 2018

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply