carene_waterman: An image of the Carina Nebula (Default)
My second AO3 bookmarklet (StripEmpties) is a really simple tool to take out the extra blank paragraphs the AO3 parser puts in sometimes.

It was easy to code, fast to test, and I made it a little less error-prone by making sure the paragraphs hidden are only inside the chapters div.

Great.Read more... )
carene_waterman: An image of the Carina Nebula (Default)
EDIT as at July 22, 2014: I did a quick and dirty update to get this working, but it still needs to be cleaned up. The issue was the AO3 (finally) got rid of presentational HTML classes and started using proper CSS techniques to style the fandoms pages. I was using the .odd and .even classes in my script because they were just so handy. That'll teach me.

So, I wanted to get back into coding after a summer of mostly gardening, and I was not really feeling the Ruby love. I'm a bit stalled mentally on the project in the course I'm doing, so I'll work on it some other time.

I wanted some more practical JavaScript work. I started reading at the AO3 again too, and the very first story I read had that annoying thing where every other paragraph was empty so the effective paragraph margin was doubled.

I knocked off a quick and dirty way to fix that, and I'm now working on making it a little less dirty.

Next, I took on the fandoms pages. Their long lists of fandoms in alphabetical order and when browsing, they are dominated by scores of fandoms with only 1 or 2 works. Yuletide legacy, I would guess. So, I could see the attraction of changing the sort order to numerical to find possible things to read with lots of stories.

And you don't have to read the code or my notes on it at all. To try out the bookmarklet go to The AO3 section on my GitHub page. If you want to read the code etc., keep on reading right here.Read more... )
carene_waterman: An image of the Carina Nebula (Default)
More details to come, but I just posted the first of a handful of bookmarklets specifically for using at the AO3, here on github.

I totally tried to tell someone who wanted this exact thing on an AO3 post, but they strip out all links (and anything that comes after it) in comments from non-account holders. Great!

Even better, they never tell you this, it just silently posts with half your comment not there. Because fighting spam means users get shitty services.

Anyway, I never bothered to get an AO3 account because I just read there, and I don't like their bookmarking system.

But, I needed a real site to make bookmarklets to work on, to broaden my skills so I decided to focus on AO3 since I'm a little familiar with the site.

The first one reorders the fandoms pages by number of works. I'm working on another one to deal with some annoying formatting features of stories. More details soon.
carene_waterman: An image of the Carina Nebula (Default)
So when last we left this really long story, I had a list of wants for the perfect remove images bookmarklet.

The Prototype


I have a prototype I'd like to see tested by others, but first I want to check my list.

Did I get what I wanted?

  • Yes Images off, but not removed from the DOM so that dynamically added images will show up on the page at least as an image.

  • No Limiting the text flow issues and overlap you get where the alt text is too long for the image container.

  • Yes Works in at least FF, Webkit (Blink!) and current Opera.

  • Yes Allows some way to respond to unhidden new content. Either automatically or a quick re-run of the removal script.

  • Yes Making sure an image link with no alt text is still clickable.


And did I get what I might like?

  • No Some custom style for image link alt text to avoid it not looking like a link when the native styling removes the regular link styling for the page

  • Yes Some customs style for all the alt text to make it clear it is not regular content text.

  • Yes Some visual designation that there was an image there--to help with no alt text images and image links.


Which is not bad.

I gave up on the idea of doing anything much to the alt text itself. It is just too complex an issue to make it not break layouts sometimes. The choice Webkit made to only show alt text when images won't load if it will fit on one line in the space the image takes up is an example of putting the layout ahead of usability to overcome this dilemma. I decided to let the layout break since this is only a tool that's easy to turn off.

I did get some benefits I hadn't thought about going in, and I like the result.

The code for the prototype


/*Set up the forEach method and get all the images on the page*/
var forEach = Array.prototype.forEach;
var images = document.getElementsByTagName("img");

/*Main function where most of the action is*/
function removeImages()  {
  /* First, it gathers up all the created alt text elements (present if the function has run before) and gets rid of them.*/  
  var altSpans = document.querySelectorAll(".alt-text-bookmarklet");
  forEach.call(altSpans, function(span) {
    span.parentNode.removeChild(span);
    });
  
  /*Next we do the actual image removal by replacing each image with a data URI of a transparent 1px gif.  The images get a border that is the text color*/
  forEach.call(images, removal); 
  function removal(img){
    img.setAttribute("src", "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
    img.style.border = "solid thin";
    
    /*Now we give the image some alt text if it has none and it's inside a link.*/    
    var imageAlt = img.getAttribute("alt");
    if ((imageAlt === "" || imageAlt === " ") && img.parentNode.nodeName.toLowerCase() === 'a') {
        imageAlt = "Link";
        } 

    /*Now we take the alt text and make it into a span that goes after the image.  It's in smallcaps and has a little padding.*/
    var  altText = document.createElement("span");
    altText.className = "alt-text-bookmarklet";
    altText.style.fontVariant = "small-caps";
    altText.style.padding = "0 0.25em";
    altText.innerHTML = imageAlt; 
    img.parentNode.insertBefore(altText,img.nextSibling);  
  }
}

/*Calls the main function after a delay of 5 seconds.*/
function delayedRemoval () {
  window.setTimeout(removeImages, 5000);
  }

/*Calls the delay function if the mouse is clicked anywhere in the document.*/
document.addEventListener("mousedown", delayedRemoval, false);

/*Calls the main function the first time.*/
removeImages();


How it works


This keeps the time delay after a click from the earlier version, but simplifies it by making it happen on any click anywhere. I chose mousedown as the event, since click events are often set by other scripts on the page that use capture, causing conflicts.

I kept the long 5 second delay simply to allow this to work on Livejournal comment thread expanders, which are very slow.

Images are not set to visibility: hidden or display: none, because that means a new image added via a page script is also invisible.

Instead they are replaced with a 1px transparent gif courtesy of CSS Tricks. This also allows title text on images or its wrapper link to show on hover, or other special hover effects to remain in place.

This can make the page busy, and if a page has spacer gifs on it, and the HTML or CSS does not set the color on that element to something invisible, the border shows. I decided to live with the visible cruft in order to see where an image was.

I kept the insertion of text on image links with no alt text--there is so much missing alt text in the wild, it's needed.

This has been tested in Firefox, briefly in Chromium and Opera, but I have no access to IE. It will not work in IE 8 or lower. It might work in IE 9 or 10.

It breaks some pages. News sites that put very long teaser blurbs for image links into the alt text look horrid. Some DW layouts set icons and some other part of the entry as side by side divs. Long alt text makes this a problem.

It preserves icon popups (images show in these) and title text on DW, as well as allowing the expand/colapse arrows to show and they change to the correct alt text.

If the delay is too short, just hit the bookmarklet again and remove all images.

Here's the bookmarklet to try:

Make a new bookmark and cut and paste the contents of the text box into the URL or Location field. Name you bookmarklet. (Sorry the drag this to the toolbar method doesn't seem to work on DW.)

carene_waterman: An image of the Carina Nebula (Default)
It all started with this Suggestion.

What the OP of the suggestion was doing was using the old Zapimages bookmarklet, made years ago by Jesse Ruderman, and still available at his site.

I've had that handy little bookmarklet sitting in my toolbar on Firefox for years. What the OP was experiencing was the text in the arrow icons for cuttags not updating. I started looking into why that happened and that led me on a trip through some JavaScript concepts that I hadn't had a practical exposure to. If you're not into code, this will be a boring trip, but I'm chronicling my journey mostly as an exercise in learning and reinforcement.

Original Bookmarklet


First I looked at the original bookmarklet code, which stripped of it's enclosing function and put through the beautifier looks like this:

The Code


function toArray(c) {
    var a, k;
    a = new Array;
    for (k = 0; k < c.length; ++k) a[k] = c[k];
    return a;
  }
  var images, img, altText;
  images = toArray(document.images);
  for (var i = 0; i < images.length; ++i) {
    img = images[i];
    altText = document.createTextNode(img.alt);
    img.parentNode.replaceChild(altText, img);
  }

What it does


Okay, what this does is two things. First, there's a helper function that takes one object of items and puts it into a new array. This is used on the images node list.

Second, the array of images is iterated over and the image is replaced with its alt text which has been made into text content with no enclosing html element.

This works really well as a blunt-force tool that strips the images out of the document and allows the alt text to become link text if the image is a link, or to sit there as description otherwise.

It does not respond to dynamically changing pages and it will conflict with other scripts that swap images for whatever reason, because the image is actually removed from the document, there is no image there anymore to swap.

My analysis


The first thing I wondered about was the last thing I figured out. Why is that helper function even there? It just moves the nodelist (which is an object, not an array) into an array.

The reason is because nodelists are dynamic. As the removal loop goes through, it is taking things out of the nodelist, but not the array copy. You can't just run the for loop on the nodelist, because the nodelist length keeps getting shorter and the image you just removed is lopped off the front, so the image you really want to work on is always at index 0. Oh, hey!

Revised code with no helper function


var img, altText;
var images = document.images;
var imagesLength = images.length;
  for (var i = 0; i < imagesLength; i += 1) {
    img = images[0];
    altText = document.createTextNode(img.alt);
    img.parentNode.replaceChild(altText, img);
  }


What it does


Now we just take the nodelist, note its original length, iterate over the list by that length and always work on the image at index 0.

This works fine, is shorter and (not detectable by a human) less of a performance drag. It could be made even more succinct by leaving out those temporary variables:

var images = document.images;
var imagesLength = images.length;
  for (var i = 0; i < imagesLength; i+=1){
    images[0].parentNode.replaceChild(document.createTextNode(images[0].alt), images[0]);
  }


does exactly the same thing, but is a little harder to read. There's a lot less to fuss around with taking out space and linebreaks though, and is likely a "better" coding style, since it doesn't bother with variables that aren't really used for anything. This is something you don't need to care about, I don't think, when you're making a bookmarklet. It is something you need to know to program at a bigger scale (where you can document what that complex expression does).

Zap the image a little more lightly


So, the source of the DW user's problem with the above code was that the image is gone and can't be swapped for the new one when you click on a cuttag expander. The simplest answer to this problem is to not remove the img tag itself, but to make it not display the image. When you do that the browser will just display the alt text for you making the whole process simpler.

var images = document.images;
for (var i=0; i < images.length; i+=1) 
    images.item(i).removeAttribute("src");


What it does


We just load up the nodelist and take the src attribute off each one. Easy. Peasy, even. On DW, when you click a cuttag expander, the new arrow loads as an image, and you can carry on reading. You can click the bookmarklet again to get rid of any new images that appear in the new page content.

My analysis (or why this fails)


I travelled down this path for quite a while. I added refinements to have it insert some default text when there was no alt text, resize the images so they weren't taking up huge amounts of space anymore, and I looked at ways of automatically detecting new content on the page and running the script again. Most of this works well, although the auto-remove of new content is a bit hacky, but it only works in Firefox (and likely the current Opera).

Bloody Chrome.

Okay, and to be fair to bloody Chrome, my code does violate web standards which say an img tag should have a valid src. The problem is that Chrome (and most other webkit browsers) and IE, reload the image if you strip its src attribute. Also if you set it to an empty string. In addition webkit browsers do a weird thing with images that don't load by displaying alt text only some of the time.

For more information of this if you're curious here's some links I collected:

Down the remove the src attribute path


For posterity, here's my works only in Firefox, souped up bookmarklet:
/*Make a .forEach that works on objects*/

var forEach = Array.prototype.forEach;

/*Get nodelists of these elements*/

var images = document.getElementsByTagName("img");
var links = document.getElementsByTagName("a");
var buttons = document.getElementsByTagName("button");

/*This function removes the src, width and height of each image and inserts default alt text when an image has none and is inside a link tag.*/

function removeImages()  {
  forEach.call(images, removal); 
  function removal(img){
    img.removeAttribute("src");
    img.removeAttribute("width");
    img.removeAttribute("height");
    var imageAlt = img.getAttribute("alt");
    if ((imageAlt === "" || imageAlt === " ")&& img.parentNode.nodeName.toLowerCase() === 'a') {
        img.setAttribute("alt", "Image");
        }    
  }
}

/*This runs the image removal function after a delay of 5 seconds*/

function delayedRemoval () {
  window.setTimeout(removeImages, 5000);
  }

/*This calls the image removal when you click on the bookmarklet*/

removeImages();
/*This puts an event listener on each button or link and then calls the delayed image removal function if any are clicked on*/

forEach.call(links, function(link){ 
  link.addEventListener("click", delayedRemoval);
});
forEach.call(buttons, function(button){ 
  button.addEventListener("click", delayedRemoval);
});

What it does


Okay, this is a big change from the short and pithy start of this concept, but it's not much more complex.

First, there's a new feature in here, the use of the .forEach method. Which is an array method that removes the need for all those for loops that look identical and repeating them violates DRY (don't repeat yourself). I already turfed the code where the nodelist gets made into an array, though and I didn't really want it back. MDN to the rescue. That page contains instructions for ways of using .forEach on objects. I picked the one where you .call the function defined in the very first line of this code.

Next up, we get nodelists of all the images, links and buttons.

The basic task of changing the attributes on each image is inside a function now. It also has a conditional statement that adds the text, "Image" if there is no alt text present and the image is inside a link. This gives you something to actually click on to use the link. (Very handy on Wikis where the use of alt text is very spotty and weirdly implemented.)

There is another function that calls the removal function with a time delay. This gets invoked when a button or link on the page is clicked. It's a (somewhat) hacky way of running the removal automatically whenever new content gets added to the page. This is usually done by a show/hide link or button. If a page designer uses that horrible technique of making a span act like a button, then this won't work, but it fails silently, and you can always just click the bookmarklet again.

My analysis


Gotta say, I like this code, not least for the way I got that for each thing to work first go, and I'll likely put it in use for myself, but the only on Firefox thing makes it of limited value.

Some improvements could also be made to the resizing of images, to allow for pages that size the image via the CSS or via a parent div. The goal there is to get the alt text to flow better and to not have text cut off. The abomination that is absolutely positioned icons is a problem however.

Also, I think it is a real hole in the Document Object Model that there's no way to trigger a script when the page dynamically changes by having hidden content un-hidden by a script (MutationObserver does not do this) since every other page has this effect on it. Obviously if I was writing a script for a specific website, I could build in the correct event listeners with class names, but a general purpose method would be nice.

So what then?


Someone else on the DW Suggestions post has an idea to use the bookmarklet to toggle images on and off. It doesn't quite work--has a couple of minor bugs, one of which I'm still trying to figure out--but I don't think it's the direction I want to go. If you click on a cuttag, you still don't see the arrow for collaps and the alt text still says expand and you have to click the bookmarklet twice to fix that.

So what do I want? Blue sky the idea first and then see if it's at all possible to do.

I want...


  • Images off, but not removed from the DOM so that dynamically added images will show up on the page at least as an image.

  • Limiting the text flow issues and overlap you get where the alt text is too long for the image container.

  • Works in at least FF, Webkit (Blink!) and current Opera.

  • Allows some way to respond to unhidden new content. Either automatically or a quick re-run of the removal script.

  • Making sure an image link with no alt text is still clickable.


And I might like...

  • Some custom style for image link alt text to avoid it not looking like a link when the native styling removes the regular link styling for the page

  • Some customs style for all the alt text to make it clear it is not regular content text.

  • Some visual designation that there was an image there--to help with no alt text images and image links.



So back to the drawing board to see if I can get the image gone enough to be invisible, but still there enough to be replaced. Part II comes when I've done that.
carene_waterman: An image of the Carina Nebula (Default)
I bloodied my nose pretty hard on the Functional Programming chapter in Eloquent JavaScript. I went back a few days later and got through the first half okay and hit a wall again.

While I wait for a time stretch long enough and quiet enough to give it another go, I thought I'd go back to Codecademy and get some instant gratification (and frustration).

I tested out the HTML and CSS tracks to see if they were worth recommending and I'd give a heavily qualified meh as a rating.

I found these lessons the buggiest of all, and any exercise that uses a height or font size property requires you to have no minimum font size set in your browser and to be viewing at the un-zoomed size. Um, yeah, no.

If that's not a deal breaker for you, be aware that they were written when the submit and results display were obviously functioning differently (and submit was called run). I found you had to click things in the right order, hit submit multiple times, and occasionally save the code and refresh the page to get the lesson to validate. You get a lot of false negative results claiming your code doesn't work. They are working on their UI so this is likely to improve.

But this does dynamically teach you HTML and then integrates CSS into what you know--and then adds in JS and jQuery in some off-track projects.

That part is great and pretty much impossible to find (free) anywhere else.

I didn't like the way they showed examples of bad semantics in early lessons or the way they introduced styling with inline html styles first. (Although for DW users wanting to know how to style things in a post, this isn't useless info.) They don't put any emphasis on best practices and never utter the words web standards.

Some of the lessons link to W3Schools for reference docs, which is appalling, and they're aimed at a very young audience.

Still, it's interactive, you instantly know what things do and you get to experiment.

I skipped over to the jQuery track, which assumes you don't know JS, but doesn't tell you that on the main page. I would have done it sooner, had I known, but also I think that's the wrong order.

Don't learn jQuery without at least a 101 understanding of JS. Don't be one of those people who can do all kinds of cool shit with jQuery but are baffled by the simplest native JS statements and don't know anything about how JS uses the DOM API. You won't be able to debug anything that goes deeper than how you wrote the jQuery.

That said, OMG is jQuery easy to learn. It is so intuitive to anyone familiar with HTML and css, it barely needs explaining beyond the syntax.

I also found the Codecademy jQuery track the best written I've done so far. It's not too twee and it's less about testing how carefully you read the riddle instructions and how well you can copy and paste (the JS track) and more about learning through repetition and building of skills.

It is buggy--I had to make sure my cursor was at the bottom of the code in the .js tab, and then I had to hit results, do the user action on the results tab and hit submit several times for almost every exercise.

If you like text based instruction, with example code, but no exercises, nothing beats Shay Howe's HTML and CSS guide (The beginners guide is available at the same site.) He teaches the two things side-by-side, as they really should be.
carene_waterman: An image of the Carina Nebula (Default)
I'm getting back into the JavaScript. I really like the Eloquent Javascript book and online learning system.

When I worked on it before, it really brought home how Codeacademy's methods tech you to mimic code without learning it in any conceptual way. This can be okay for very beginner stuff, but it gets to a point where you're not learning, just copying forms.

I hit a wall in Eloquent, and decided to redo one of the basic projects and then ran out of any free time. I'm just going to start over. I will report back!

Profile

carene_waterman: An image of the Carina Nebula (Default)
carene_waterman

July 2014

S M T W T F S
  12345
6789101112
13141516171819
2021 22 2324 2526
2728293031  

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Sep. 25th, 2017 06:54 pm
Powered by Dreamwidth Studios