afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
I accidentally wiped out my entire dev folder -- we have a bootstrap script, so setting up the repos again is easy, but my unsubmitted patches were another matter.

Unluckily, my dev environment is in a virtual machine, and I excluded that virtual machine from my Time Machine backup (in hind sight, not the smartest decision ever).

Worse still, it may not have been much help in this case because my older sister borrowed my portable hard drive and hadn't returned it, so I haven't been able to back up for ages. A backup plan is good and fine, but a backup plan you didn't actually run is less so!

BUT. Luckily, I occasionally push my MQs (mercurial queues -- where my patches are stored) to my public webserver, so that I can test my code in an environment that's set up closer to production.

The last time I did so was a month ago. And I didn't lose a month's worth of work, because almost everything is short-term and had either already been submitted to zilla for review, or was committed as soon as it was coded. I judge I only lost a couple patches, most of them involving only debug and exploratory work, rather than actual behavior-modifying code.

That left long-term projects (redesigns, etc). BUT and I still cannot believe how lucky this is because seriously if there ever was an afternoon where it would be okay for me to delete all my work locally, this was it, I had just transferred over a copy of my update page redesign work to my public server because I'd been talking over things with [personal profile] foxfirefey and wanted to show my progress to her.

So I lost an afternoon's worth of work on the update page, and I lost a few small things in misc projects, but it could have been so much worse.

This all has led me to re-examine my back-up strategy.
  • my public server is backed up by my webhost for a small fee. I have my personal website on both my laptop and the public server. I need to check how much other things I have on my server which I can back up locally as well

  • my phone and tablet are both backed up to my laptop (I'll need to make sure I sync more often though >_>)

  • my laptop is backed up, but only to one portable hard drive. I am planning to buy a newer non-portable external hard drive (my brother found one my Western Digital: 2TB, USB 3, 6.5k pesos), and Il'l be backing up to both.

  • my virtual machines images are still not backed up, but most of what is in there either comes with the OS or is just testing data / sessions

  • instead of storing my dev folder in the virtual machine, where it can't be backed up, and accessing it via sshfs from my mac for editing, I have turned it the other way around. Now I have my dev folder on my mac, where it will be backed up, and I have mounted it as a shared folder in the virtual machine. My server runs a tiny bit slower now, alas, but it's still faster than if I had to contend with a network bottleneck, and I can live with that for the added security of being able to back up

  • perhaps I should back up a few essential but non-sensitive files to Dropbox?

In entirely more pleasant news, I have finished a shawl for my maternal grandmother, and am now looking at making a vest for my paternal grandfather. I'm not sure what makes a good vest pattern though; I have never done one, and I'm overwhelmed by the options on Ravelry!

(If I could narrow it down to all vests that open in the middle so you can put them on easily, rather than pulling them over your head, that would be a good start)

Also I am sending a friend of mine a hat! :DD :DD :DD

I have been working diligently, and I have finally cut down my projects to just three in progress: a pair of gloves where the second glove, barely started, is much looser in gauge than the first which is stumping me; a hat I ran out of yarn for, so I'm waiting for new ones from my LYS; the Grounded scarf which is perfect for mindless comfort knitting, so I'm saving it up for when I need something like that. Oh, and a half-finished top which I started when I was new to knitting and which is finished up to just below my breasts. I think I'll undo everything I have; I don't have the rest of the pattern because the person who was helping me is no longer at my LYS, and the tension is very uneven besides. I am only holding on to it for nostalgia *_*

Ooh and another project will be added to that list, as soon as I figure out a patten for my grandpa's vest.

I think that my knitting projects are multiplying. (help)


Sunday, February 13th, 2011 01:33 am
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Looks like I'm getting a patch into Mercurial :)

Ta, patch is in stable
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Someone requested that I translate my "Copy Link Text" extension to Russian, and even kindly provided me some text to use! Unfortunately, it was anonymous, so I cannot respond to them to say thanks or ask my question (in case you wander by here, btw, thank you!)

Text provided is: "Копировать текст ссылки"

What I'm unsure about is whether that text is appropriate for both the context menu item (a command/action), and for the extension name (a noun?). I use the same text for both in the English, but I don't know any Russian, so I can't tell if it's appropriate in both senses in there as well.

Translation engines help with meaning but not context, so unfortunately it's not something I can check on my own *rueful*

afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
I paid the (one-time thankfully) $5 fee, and have submitted the Copy Link Text Chrome extension to the gallery. Happy to have received some stars \o/

I've also created a git-hub repository for the extension.

I've been trying to spend some of my free time doing open source stuff that's not DW, so I can see what's out there (maybe introduce some things back into here, maybe not!).

So far, it's been fun. My biggest, er, thing, is that I apologize a lot. I'm trying to turn that around into thanks, so instead of saying, "I'm sorry I got my extension idea from a similar Firefox extension", I'm trying to say, "I loved that extension, I think it's fantastic, thank you for it! (In homage thereof I have written one for Chrome too *g*)"

Or, instead of saying, "Sorry for grabbing your lovely icon from the icon set you're offering for free", I'm forcing myself to see that "Gorgeous icon set, great icon that fits my needs exactly, thank you :DD" is the more appropriate response.

And instead of saying, "Hey, I noticed this issue, and I wrote a patch; the github documentation says to use forks for this, I don't know if it's right or if I seem rude. I'm sorry for even bringing it up x_x", I have a message which basically goes, "Hi, I loved $extension. There's just one little thing that makes it less than perfect, and it's $issue_I_ran_into. *contributes patch upstream*"


Anyway, it's ridiculously hard to not apologize all the time. Ridiculously ridiculously hard! But I'm trying to turn it around, because while it feels inappropriate not to apologize (it really really does), it feels even more inappropriate to apologize. Before, I'd have just decided it was not worth the risk of offending someone by being awkward, and just backed away without ever having said anything. Now, I'm forcing myself to acknowledge that hey these people shared these things for a reason, and the polite thing to do is to use them ;-) (And give back too)

afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
I am playing with the thought of sharing the extensions I wrote/talked about in my previous entry. I feel like at least one other person could benefit from it!

But on the other hand, I'm feeling unsure of myself: I based the idea on a very useful Firefox extension (Copy Link Text), and I based the final working code on another Google Chrome extension (Auto Copy).

And then there's the matter of putting up $5 to be able to submit to the gallery! (It's not purely the cost, but it's the cost being enough of a speedbump that I'm making myself think about whether I want to do this or not).

I want to share, but I also feel like I don't have sufficient whatever to share. Ugh. Will try this again after getting some rest; that sometimes helps.
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Have finally redownloaded Google Chrome because I need it for some troubleshooting. Before doing so, I looked up how to disable auto-update, and discovered that it's now a matter of changing a setting via the Terminal, rather than deleting the auto-update program. This seemed a lot less virus-like creepy, so I have Google Chrome on my system right now, and feel okay about it.

While moving over settings, I discovered I'd have to restart Firefox -- shutting down Firefox took over an hour! And ate up all my CPU! I had to force-quit it after all; luckily didn't lose any data.

As a bit of amusement, I decided to see what it would take to get me to switch over to use Google Chrome regularly. At a minimum, I'd need:

  • Adblock and/or JS blocker (I went with Adblock, but I'll try to retain all my casual browsing on Firefox with NoScript on)
  • Something to keep count of open tabs (settled on TabGlutton)
  • Ability to quickly edit and preview CSS (probably somewhere, but I haven't looked for it; just the current hacky ways are enough for now)
  • Firebug or equivalent (built-in)
  • Greasemonkey script support (built-in)
  • ReadItLater (Found ChromeItLater; slightly clumsier than the official extension Firefox, but workable)
  • A JS shell (I have a bookmarklet, but I eventually decided to go with the jsshell extension)
  • a JSON prettifier (JSONView for chrome)
  • The ability to copy just the link text instead of the link address

I found what I needed, or a good-enough equivalent, for everything but the last one. Searching through the extensions site and through the greater web turned up nothing. So.... I ended up writing my own.

screenshot )

It didn't take long; however, figuring out the difference between the content_scripts side (which lets you manipulate the webpage) and the main extension body in background_html (which lets you manipulate the browser behavior) took some doing.

The basic "hello world" example shows you how to create a regular extension, one which interacts with the browser. It took me an embarrassingly long time to figure out there was something beyond that.

After I figured out that the reason my extension wasn't doing anything was because it was running once when the extension loaded, when it needed to be running once per page, I slowly came to the realization that I'd need something like body.onload. Unfortunately, even after going through all the links from the getting started portion of the tutorial, I had still overlooked the other part of the API which would enable me to interact on a per-page basis (hello content_scripts!). I tried to force it by using chrome.tabs.onUpdated.AddListener... if ( changeInfo.status == "complete" ) as a poor extension writer's body.onload and that worked, but wow was it ugly :D

I can't remember how I found the link, but after finding that section in the documentation, I was more than halfway there.

In my initial version, I tried to programmatically create a range (document.createRange()) to select the text in the content scripts portion of the page. Right after that, I'd rely on some timing hackery to run document.execCommand("copy") (yeah I didn't know this function existed until now either) in the extension trusted area. It worked, ish, but was ugly and hacky and brittle.

Reason I had to put document.execCommand in the extensions area: it seems to need to be run with higher privileges. If it were available to the website, any website could see your clipboard, and that could potentially leak information. Since extensions are more trustworthy -- you choose whether to install them, and I believe they're vetted to some extent by reviewers before they are distributed in the gallery, they are allowed to execute with higher privileges than Joe Random Website.

On the second version, I figured out I could send just the relevant information (the link text) to the main extension body via a communication pipe, and have the copying take place there. That took away all the worst of the brittleness and most of the hackishness (there still is some ;-)), as well as gave me some flexibility for being able to output in multiple formats should I choose to code that in in the future, and I am quite happy with the results.

I can't take credit for it though: I had the glimmerings of the idea on my own, but I didn't actually get it to work until I peeked into the source code of Auto Copy.

So anyway, there it goes. Have made a Google Chrome extension, it wasn't very painful once I managed to wrap my mind around how everything fit together, and I'm happy with the results.

Ohh one final thing: for a long time, I thought that console.log() didn't work in extensions, and struggled with that. It turns out:

a.) console.dir in Google Chrome is the equivalent of console.log in Firebug; and
b.) If your console.log calls are in your main extension body, you need to open the background.html view from the extensions tab, as the console log output is put there. If your console.log calls are in a content_scripts js file, then they'll show up in the webpage

And there! :D
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
(Or: when your focus listener interferes with your click listener) Demo.

Stumbled across it while I was working on the new update page, and it held me up a while. Eventually, I decided to use another approach.

Playing with the thought of submitting it as a bug, but it may be expected quirky behavior -- at least, it behaves the same way in both Firefox and Opera. Chrome, meanwhile, does not suffer from the problem, but that may be sheer accident: Chrome doesn't trigger the focus event when you use the mouse, only when you use the keyboard. So in Firefox/Opera, the click event doesn't work; in Chrome, it's the focus event that doesn't show up.

Browsers: they are strange animals.

PS. If you edit the URL to see previous versions of the fiddle, you'll see that I originally thought that the presence of images had something to do with it, ehehe.
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Have been fighting Opera, trying to make it understand that I need the tab and enter keys for autocomplete. Lots of googling later, finally realized that I could ignore all the talk of trying to bind to the keypress event or whatever, and instead use the existing hooks to set a "just completed" flag, after an item has been selected. Then, if the form gets submitted by the enter key, or if we leave the text box, check whether we'd just completed something.

It sounds horribly complicated, but it's only three tiny chunks of code (or it was, after I got rid of all the other more complicated, but unnecessary, approaches \o/)

Really useful article about jquery ui widgets:
* Understanding jQuery UI widgets: A tutorial was crucial to my understanding of events in jQuery.

Also, jsFiddle is awesome. It's a combination of pastebin and an active dev environment. I talk about it a bit in the [community profile] javascript community, but really it's much better than I can make it sound *g*

(A couple of weeks ago, I posted a link to Google Playground. This is like that, but even niftier if you just want to play with JS and JS frameworks, not Google's APIs).


Friday, March 19th, 2010 06:50 pm
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Some useful places to start if you want to find out about jQuery:

7 Amazing Presentation Slides to Teach you How to Code with jQuery

15 Resources To Get You Started With jQuery From Scratch

And if you don't want to bother with setting up an HTML testbed (I know I was too lazy to!), a quick way to get started playing while going through the slides is the jQuery section of the Google Code Playground.

For example, replace the OnLoad function there with this:
sample code )

AGH. Silly mistakes

Thursday, March 18th, 2010 04:42 pm
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
In my attempt to conquer Bug 2344: Default View/Default filters should be default when adding from hover menu, I wrote this line of code:

$success ||= $filter->add_row( userid => $u->id )

What I thought it said:

* add this user to the filter
* once you've added the user, check whether it was successful, and keep a running tab on the status so you'll know in the end whether everything succeeded or not

What it was actually saying:
* once you've successfully done one thing (e.g., added the user to a filter or subscribed to that user), we're done. We don't need to do anything else (in this case, adding a user to the filter).

So now I've rewritten it more properly as:

$success = $filter->add_row( userid => $targetu->userid ) && $success;

(I got the boolean logic initially wrong too *rueful*)

This was an entire afternoon's worth of frustration (whyyyyyyy isn't it adding the user to the filter? Why doesn't it print out any of my warn statements within add_row? I didn't realize I wasn't calling it at all), solved with one of those flashes of insight you get when you get up and grab a glass of water and happen to reread your code when you get back.
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Started from these instructions, but some modules try to install files into /usr/lib, which causes an error about being unable bto overwrite. I ran into this while trying to install a later version of Net::OpenID::Server/Net::OpenID::Consumer, in particular.

One solution is to force the install, but I don't know what other effect that could have.

The better solution is in this comment with similar but slightly different series of steps, from

(Note: in case it wasn't clear -- I had to figure it out through trial and error -- the rules.MakeMaker.noxs you're editing is the one in ~/.dh-make-perl)

So far so good; about to try it out.

ETA: Works!


Saturday, December 5th, 2009 09:15 pm
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
I'm doing Bug 216: Renaming, and maaaaaan.

It's kind of funny -- I studiously ignored most of the renaming support requests back on LJ, because it seemed complicated and I didn't want to dig into the code, and now here I am, writing similar logic from scratch *snickers*

I'm having great fun. This seemed like one of the projects where tests would have a great effect, so I spent some time figuring out how to write tests from scratch, rather than just trying to tweak existing tests.

My tests look kinda like this:
tests )

I'm coding by first writing a bunch of tests, then writing code to make the tests pass. Every time I think of something new I should check for, I dash off a quick TODO block and then go back to writing either my test or my module. That helps me not lose my train of thought, but also means that I don't lose my ideas.

I'm starting with a small set of limited functionality, and then gradually expanding the scope. I actually started by disallowing everything except user-to-unregistered, and now I'm relaxing the rules as I add more. So far, I have personal-user-to-unregistered, with redirect and without redirect, and lots of checking for whether $fromu can rename to $tousername.

I'm in the middle of user-to-other-user, where the second user is just moved out of the way.
I estimate that I'm only about 10% of the way through -- I still need community-to-unregistered, community-to-other-community (or journal??), two-username-swap, keeping or removing various links -- subscriptions and the like, then catching edge cases (loops, openid attempts to rename, etc). And then I need to integrate it into the shop. And after that, I need to build a frontend so people can buy it from the shop *g*

At some point, I'll probably need to catch someone in chat to ask certain questions about the renaming process -- what should happen to usernames that were renamed away from, is swapping usernames one token or two, should you be able to rename a comm to a journal and vice versa, blah blah blah, buuut that can all wait a bit, because I'm sure I'll end up with still yet more questions.
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
But I cannot replicate on my 'hack. I am stumped!
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Just wrote a quick script which parses out colors and the elements they apply to from a CSS stylesheet, to make it easier to match them to s2 properties.

Invoke by doing:

> path/to/stylesheet

Can take multiple stylesheets. Actually, if it could parse out the colors from multiple stylesheets, and compare somehow pick out which colors were theme-specific, that would be pretty useful in conversion. (It does nothing of the sort now; it's just slightly easier this way than wading through the rest of the positioning, etc, code)

Mmm, not posting anywhere (else) until I figure out a better way to lay out the data. But I figure I could eventually put up a webpage as an interface in case anyone wants to use it quickly for conversion. Would need to be more careful about the input/output then, but that can wait until later.

Frontend stuff

Monday, June 15th, 2009 03:10 am
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Pleased with this weekend. Managed to do frontend stuff, and work out how jquery works.

(I tend not to do much frontend stuff; not enough of a designer in me. But it turned out... not incredibly ugly? Usable, at least).

One good thing about working purely frontend is that I can do it without connecting to my server to test (and since I had no internet this Sunday, that was important!)
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
Really simple script for you guys today: Support: Summary in Title.

That appends the the request summary into the window title which lets you the request summary show up in your browser history. Meaning, you can just type in a partially remembered request summary in your browser address bar, and it'll (hopefully) pop up the URL to the request you're thinking of.

Works on Dreamwidth and LiveJournal. (Note that it may be conflicting with some other scripts? I had to drag it to the beginning of my list in the Greasemonkey settings for it to work on LiveJournal)
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
My morning was nicely cheered, seeing this entry in [ profile] changelog:

Fixing thread expansion on Safari.

(DW got a shoutout! As did I! And issue got fixed, hooray!)
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
I find myself utterly in love with the creativity of people who can use CSS. Because, hey, I'm happy with black on white and everything in outline form and utterly utilitarian myself, but that doesn't make for gorgeous. (My heart flutters every time I see someone take a Core 2 style, and make it really pretty, because that's what they're there for.

My heart also bounces with glee whenever someone mentions that it's easier to customize styles here than they are used to, because that's what all we did was for. Seriously, ba-dump-thump.)

Styles still not done, though. On my list, once I'm no longer focused on open-beta-blockers and the styles usability bugs being reported to us, is, in no particular order:

  • sticky entry module

  • custom text module

  • hooks to let layout authors easily insert custom modules

  • segment and arrange the wizard properly (subheaders, etc)

  • pulling in information that's already available to us in other places, to be accessible using S2 (la la la la)

The one thing that I'm finding out is how easy it is to manipulate the S2 backend. Perhaps too easy. I find myself wondering whether my methods are hackish/messy and will cause problems down the line later on.

(I'm already trying to stop myself from trying to redo the grouping stuff as hashes with lots of automagic rather than arrays. Imagine being able to do property string{} module_customtext_group { grouptype= "module" }; and have it pick up the title/name/opts automatically instead of having to do a set module_customtext_group... Feel like I missed my chance on that one, now I have to do the grouping manually all the time. Bleeeeeh. (but it works, I'm happy with it))

But before that, business statistics, which I am working with, with [personal profile] pauamma. We've got some kind of framework hashed out, now just need to figure out how to do the selects for data collection (I say "just", but since that's the entire point of what we're doing... hah!).

The biggest problem with the SQL is that we're basically going to go through all the user accounts for data, e.g, account types. How to do this without bringing the DB to its knees? Suspect I shall need to talk to friendly neighborhood database administrator soon-ish.

Stuff like -- group by looks the easiest syntax-wise, but has to load everything in the database in one go, and that probably won't be pretty. bin/maint/ splits users into blocks, and iterates over each of the rows a block at a time. Or could you combine the two, split into blocks, and groupby within that block? Ponder, ponder, beard-stroke, etc. (Thinking out loud, will wander over to IRC and try to find a good time to talk at some point).

It's April 20. I'm so excited. Also, feeling the lack of time. (I do wish it was faster to get into the zone, though. I usually sleep Saturdays away, which leaves only part of Sunday to do code, and there's so much to do. Grr)
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
For instance:

    foreach var string module ( $*modules_available ) {
        # FIXME: get the values in less hacky manner
        var string section = get_plural_phrase( 0, "module_${module}_section" );
        var int order = int(get_plural_phrase( 0, "module_${module}_order" ));


(but I should probably not let something like that creep into core. Long list of if-statements, it is.)

(Originally posted to Dreamwidth
afuna: Cat under a blanket. Text: "Cats are just little people with Fur and Fangs" (Default)
For instance:

    foreach var string module ( $*modules_available ) {
        # FIXME: get the values in less hacky manner
        var string section = get_plural_phrase( 0, "module_${module}_section" );
        var int order = int(get_plural_phrase( 0, "module_${module}_order" ));


(but I should probably not let something like that creep into core. Long list of if-statements, it is.)