Wednesday, November 22, 2006

The Drain That Wouldn't Drain

Since being married (2000) we have lived in 4 homes. Each has had its quirks. But each seemed to have issues with the bathtub drain to some extent. Some tubs were worse than others. I think my wife took care of some while I was at work (she mentioned pliers). They all seemed to drain...um...less well...after awhile. I don't remember this happening when I was a kid. But I was pretty oblivious to everything then.

Our "new"(1973) house has the same problem. We've only lived here for 6 months. We replaced the drain and some pipes below the tub when we moved in. But now it already takes half the day to drain the tub. It fills with slimy hairy soap water when you shower and leaves and disgusting mess when it finally drains.

So, I'm off to see the Google to find out how to get this tub to drain again. In the past we've tried chemicals (we even had a plumber apply his super powerful chemicals). We tried coat hangers. We tried some sorry excuse for a snake that we picked up at the dollar store. All these things helped, a bit, but not really.

So, Google had lots of interesting information, most of which we had tried. But one Blog (which I can't find again) mentioned a nifty tool called "Zip-It". It's a long slender flexible 2-foot plastic stick with 1/4" barbs on it. They claim that all you have to do is stick it down the drain and pull it out again. It pulls out all the hair and attached gunk clearing the drain. The Blog mentioned that they were able to buy some at Lowes.

So, I checked out the website. Impressive isn't it. But it's only a few bucks and we haven't tried it yet.

I wish I would have pulled out the digital camera so you all could see the dirty glory. The Zip-It (about $3 at Lowes) pulled out so much nasty that I thought I'd be sick. The box says that you can only use it once or to throw it away after use or some such. But after one use I could tell there was a lot more crap down there (Our drain was twice as bad and gross as the one in the video on the Zip-It websit). So I cleaned the thing off and pushed it down 4 or 5 more times until there wasn't anything left to pull up. I did have to get some scissors to cut the hair off that wrapped around our drain (battling for survival it seemed). But the drain drains! It's like new!! Hurrah! We conquered!

I fully recommend the Zip-It for hair related clogs. But now my feet get cold when I shower. I kinda miss that warm backed-up water. Que sera.

Here's an image from the Zip-It website that about matches what we pulled up on our first "zip"

Friday, November 10, 2006

Textarea Cursor Position in Internet Explorer

Background...
I'm building a content management type website and wanted to include a nice WYSIWYG editor. Unfortunately all the editors I tested are either too unpredictable and erratic, cost a fortune, or are not customizable enough for my needs (out of the box anyways).

In the end I just didn't want to deal with the awful (x)html that they produce. I also didn't want to deal with my end users, whom have never heard of HTML and won't want to "View Source" to fix the HTML when the WYSIWYG fails.

So, I decided to build my own. After a smidgen of research and testing I decided not to build my own. Now I'm going for a Wiki style code editor with a toolbar. With this I can generate clean consistent simple markup. It's not WYSIWYG but at least it will be consistent and predictable.

I quickly found that I'd need to be able to retrieve and adjust the current text selection in the textarea control. So, after not finding any satisfactory solutions via Google I've come up with my own.

Textarea Cursor Position in IE

First off I'd like to give credit to the sites that provided good tips, suggestions, and solutions.
  • parentNode - The best written and closest solution I found.
  • Mishoo - A good idea but the comments were more helpful
  • Scott Van Vliet - Also a very interesting approach. The comments were also helpful
  • Thanks to WikkaWiki which has a good solution for their text editor.
  • MSDN Library
Summary of the Implementation

My solution works like this:
  1. Make 2 TextRanges.
    1. The first TextRange selects from the beginning of the textarea to the beginning of the selected text.
    2. The second TextRange is a duplicate of the selection.
  2. Restore missing new line characters in both TextRanges
    1. The TextRange.text property right trims all the \r\n characters from the selection. We must restore the new lines to calculate the correct cursor position.
  3. Calculate the start position which is the untrimmed length of the 1st TextRange.text property.
  4. Calculate the end position which is the untrimmed length of the 2nd TextRange.text property plus the start position.
  5. Calculate the complete untrimmed selection using textarea.value.substring(startPoint, endPoint).
Further Explanation and Repetition

Internet Explorer makes this whole process very difficult. The biggest issues are:
  • It treats \r\n as one character when working with the TextRanges
  • It trims \r\n from the end of the TextRange.text so you don't know the real selection length.
My solution discovers the correct index of the start and end positions of the selection which can't be done by just using the TextRange.text.length. Once you have the positions you can use textarea.value.substring(start,end) to get the actual selected text rather than the trimmed TextRange.text property.

All the string methods (substring, indexOf, length, etc.) treat \r\n as 2 characters, however, the TextRange methods (moveStart, moveEnd, etc.) treat \r\n as 1 character. So just moving the TextRange until we reach the beginning and counting how many times we moved will be off by exactly the number of new lines passed along the way.

A Word About Changing the Selection

The TextRange.text \r\n trim issue also causes problems when attempting to change the selection's position. You need to count the newlines between the beginning and the target index and then subtract count from the target index. Then you can use this adjusted index with the TextRange.moveStart and TextRange.moveEnd methods to get the correct selection.

The Code

In my code I use 3 TextRanges (instead of 2 as described above) so that I can re-assemble the entire string and compare it to the original textarea.value. I do this to make sure I performed the untrimming correctly.

You will also notice that I only use a single do..while loop to untrim all the TextRanges. I could have done this with 3 separate loops. Even better, I probably should move the untrim bit to a separate function. I purposely made these design decisions but it could be done differently.

Also note that this bit of code was only tested in IE 6.0. Please don't complain because it doesn't work in Opera, Firefox, Safari or Lynx.


var textarea = document.getElementById("myTextArea");
textarea.focus();
var selection_range = document.selection.createRange().duplicate();

if (selection_range.parentElement() == textarea) {    // Check that the selection is actually in our textarea
  // Create three ranges, one containing all the text before the selection,
  // one containing all the text in the selection (this already exists), and one containing all
  // the text after the selection.
  var before_range = document.body.createTextRange();
  before_range.moveToElementText(textarea);                    // Selects all the text
  before_range.setEndPoint("EndToStart", selection_range);     // Moves the end where we need it

  var after_range = document.body.createTextRange();
  after_range.moveToElementText(textarea);                     // Selects all the text
  after_range.setEndPoint("StartToEnd", selection_range);      // Moves the start where we need it

  var before_finished = false, selection_finished = false, after_finished = false;
  var before_text, untrimmed_before_text, selection_text, untrimmed_selection_text, after_text, untrimmed_after_text;

  // Load the text values we need to compare
  before_text = untrimmed_before_text = before_range.text;
  selection_text = untrimmed_selection_text = selection_range.text;
  after_text = untrimmed_after_text = after_range.text;

  // Check each range for trimmed newlines by shrinking the range by 1 character and seeing
  // if the text property has changed.  If it has not changed then we know that IE has trimmed
  // a \r\n from the end.
  do {
    if (!before_finished) {
      if (before_range.compareEndPoints("StartToEnd", before_range) == 0) {
        before_finished = true;
      } else {
        before_range.moveEnd("character", -1)
        if (before_range.text == before_text) {
          untrimmed_before_text += "\r\n";
        } else {
          before_finished = true;
        }
      }
    }
    if (!selection_finished) {
      if (selection_range.compareEndPoints("StartToEnd", selection_range) == 0) {
        selection_finished = true;
      } else {
        selection_range.moveEnd("character", -1)
        if (selection_range.text == selection_text) {
          untrimmed_selection_text += "\r\n";
        } else {
          selection_finished = true;
        }
      }
    }
    if (!after_finished) {
      if (after_range.compareEndPoints("StartToEnd", after_range) == 0) {
        after_finished = true;
      } else {
        after_range.moveEnd("character", -1)
        if (after_range.text == after_text) {
          untrimmed_after_text += "\r\n";
        } else {
          after_finished = true;
        }
      }
    }

  } while ((!before_finished || !selection_finished || !after_finished));

  // Untrimmed success test to make sure our results match what is actually in the textarea
  // This can be removed once you're confident it's working correctly
  var untrimmed_text = untrimmed_before_text + untrimmed_selection_text + untrimmed_after_text;
  var untrimmed_successful = false;
  if (textarea.value == untrimmed_text) {
    untrimmed_successful = true;
  }
  // ** END Untrimmed success test

  var startPoint = untrimmed_before_text.length;
  var endPoint = startPoint + untrimmed_selection_text.length;
  var selected_text = untrimmed_selection_text;

  alert("Start Index: " + startPoint + "\nEnd Index: " + endPoint + "\nSelected Text\n'" + selected_text + "'");
}

Tuesday, November 07, 2006

Disabling Gnome-Screensaver screensavers

I recently installed Ubuntu 6.10 (Edgy Eft) on an extra PC. Before that I tried to install Suse Linux Enterprise Edition (SLED) but it couldn't handle the video properly. I am very impressed with Ubuntu's ability to handle hardware. Even when it can't handle the hardware it seems to do something intelligent about it like...not crashing.

I found that some of the many Gnome screensavers ran poorly on this old PC and one (GLBlur) caused a hard lockup. The Screensaver Preference dialog is almost useless in terms of individual screen saver configuration. Seeing as I wanted to use the Random screen saver function but didn't want hard lockups randomly I figured I'd have to dig around in the terminal. Whoopdy-doo.

Disabling Gnome screen savers
I tried man pages and Google for info on the subject but didn't find anything helpful. The Gnome-Screensaver website didn't talk about this either.

Open a terminal.

Find the place where your screen savers are located. I used the locate command to find 'glblur' (or some other screensaver in your list). In the results I found /usr/share/applications/screensavers/glblur.desktop. This looked promising (yes, I know this isn't the "screensaver" but just a config file, thank you Linux expert. Where were you when my PC was hard locking? Huh? Huh?)

So, in the past, I've found that removing files often helps track down what I'm looking for because if something else needs it then it will probably complain (and as I'm not removing the kernel or anything it shouldn't blow up my installation, but even if it does that's how I learn my lesson).

Rename glblur.desktop to glblur.desktop.disabled. You'll have to use sudo. Repeat for any other screensavers you'd like to see disappear.

Now when you open the very useful Screensaver preferences GUI those 'disabled' screen savers will no longer be able to save your screen.

Tab Mix Plus & Menu Editor

My newest favorite Firefox extensions are Tab Mix Plus & Menu Editor.

Tab Mix Plus
I've tried Tab Mix Plus (TMP) in the past and didn't really care for it. It had some default settings that I didn't like, the configuration was confusing (using terms I didn't understand out of context) and didn't do the one thing that I wanted, which was make double-click and middle-click on the tab/tab-bar open and close tabs.

But a recent release fixes all these issues. I still think it is a bit overkill and it could use little config GUI tweaking but it is definitely an improvement. It adds my double/middle click requirement (basically copying the Tab Clicking Options extension), lets me fix some of the tab changes in FF2 and adds some more great new features.

In addition to pulling in the Tab Clicking Options extension TMP seems to have added the Session Manager extension. I think this is great. Now instead of three extensions I only need one. Of course, FF2 added some Session management features but TMP's features are more complete.

The final great thing about TMP is that checks for compatibility issues with other extensions. After installing TMP (and restarting FF) I was prompted with a list of extensions that were incompatible (Tab Clicking Options was on my list) and then given a set of options on how to proceed. They've gone above and beyond where most extensions would have stopped. They don't just say "EEK incompatible" and quit. They say "EEK incompatible" and give me some buttons that will take care of the problem.

Great job and many kudos to Gary Reyes, oneman, and the other TMP contributors.

Menu Editor
The other big annoyance that I've always had with extension systems (in any application) is that the extensions aren't held to any standard on menu items. I seem to end up with menu items all over the place, out of place, out of order, in context menus and on the menu bar. And to go one further 90% of the time uninstalling the extension leaves, the now old and broken, menu items.

So, in steps Menu Editor. It's not exactly intuitive but it gets the job done! My FireFox tools menu is clean at last. The 'Options...' item is finally returned to the bottom of the Tools menu. I have regrouped all my 'Extension X Options...' menu items together and removed all the other stuff I never use.

It even lets me fiddle with the context menu, getting ride of all the fluff there as well.

Menu Editor's behavior is not always predictable. Extensions still have power over their menus and can make menu items show and hide even if you change their state with Menu Editor. But it's better than nothing and some fiddling around will eventually get your menus where you want them to be.