Ruby – Phone Number to Word Combinations

I was recently looking for a phone-number-to-words Ruby solution but could not find one. I could not even find a good algorithm in any language. This told me that I needed to write one to share with others. The following code lets you convert a phone number to possible word(s) combinations using a dictionary.

Please feel free to comment with feedback or improvements.


require 'csv'

# This class provides methods for converting phone numbers 
# to possible word matches.
#
# How to use this file:
# => ruby phone_number_converter.rb -n NUMBER
# => ruby phone_number_converter.rb -f NUMBER_FILE
# => ruby phone_number_converter.rb -n NUMBER -d DICTIONARY_FILE
# => ruby phone_number_converter.rb -f NUMBER_FILE -d DICTIONARY_FILE
#
class PhoneNumberConverter
  attr_accessor :number, :number_file, :dictionary_file

  class << self
    # This method provides all the number-to-letters
    # options for conversions.
    #
    # @return [Hash]
    def keypad
      {
        '1' => %w{1},
        '2' => %w{A B C},
        '3' => %w{D E F},
        '4' => %w{G H I},
        '5' => %w{J K L},
        '6' => %w{M N O},
        '7' => %w{P Q R S},
        '8' => %w{T U V},
        '9' => %w{W X Y Z}
      }
    end

    # This method extracts all the options provided in 
    # the command and assigns them to class variables.
    def extract_options
      ARGV.each_with_index do |arg,index|
        following_arg = ARGV[index + 1].to_s
        case arg
        when '-n'
          @number = following_arg
        when '-f'
          @number_file = following_arg
        when '-d'
          @dictionary_file = following_arg
        end
      end
    end

    # This method parses a provided dictionary file and returns
    # an array of words or returns the default dictionary array.
    #
    # @return [Array]
    def dictionary
      if @dictionary_file
        CSV.read(@dictionary_file).flatten
      else
        %w{use ruby}
      end.map(&:downcase)
    end

    # This method generates a list of phone numbers from a specified
    # file or uses one number provided in the command.
    #
    # @return [Array]
    def numbers
      if @number_file
        CSV.read(@number_file).flatten
      else
        [@number]
      end.map{|n| clean_number(n) }
    end

    # This method cleans the provided phone number.
    #
    # @param [String] number
    #  The provided phone number.
    #
    # @return [String]
    def clean_number(number)
      number.gsub(/\D/, '').strip
    end

    # This method returns an array of digits from
    # the provided number.
    #
    # @param [String] number
    #  The provided phone number.
    #
    # @return [Array]
    def array_of_numbers(number)
      number.to_s.split(//)
    end

    # This method generates all the possible partitions
    # from the provided phone number.
    #
    # [1,2,3,4] => [[[1],[2,3,4]],[[1,2],[3,4]],[[1],[2,3,4]]] ...
    #
    # @param [Array] arr_nums
    #  An array of digits from the phone number.
    #
    # @return [Array]
    def partitions(arr_nums)
      (0...arr_nums.length)
      .flat_map{|i| (1...arr_nums.length).to_a.combination(i).to_a }
      .map{|cut| i = -1; arr_nums.slice_before{cut.include?(i += 1)}.to_a }
    end

    # This method generates all the possible word(s) matches
    # for the provided number and dictionary.
    #
    # @return [Array]
    def permutations
      extract_options

      numbers.collect do |phone_number|
        partitions = partitions(array_of_numbers(phone_number))
        partitions.map do |partition| 
          partition.map do |numbers|
            numbers.map do |number|
              keypad[number]
            end.inject(&:product).map do |perms| 
              numbers.one? ? perms : perms.join
            end.select do |word| 
              dictionary.include?(word.downcase) || word.length == 1
            end.collect do |word|
              dictionary.include?(word.downcase) ? word : keypad.select{|k,v| v.include?(word) }.first[0]
            end
          end.inject(&:product).collect do |word_option| 
            word_option.is_a?(Array) ? word_option.join('-') : word_option
          end
        end.reject(&:empty?).flatten.reject do |word_option| 
          word_option.length < array_of_numbers(phone_number).length || word_option =~ /[0-9]-[0-9]/
        end.uniq
      end
    end
  end
end

permutations = PhoneNumberConverter::permutations

permutations.each do |phone_number|
  puts phone_number
end

Thanks to sawa for helping out with a part of it.

Rails Routes – Dynamic Controller or Module

I was recently looking for a solution to call the correct controller depending on other data (the user that is being viewed). If the user is an admin, call one controller. If the user is a regular user, call another controller. You could redirect to the correct controllers but it’s not elegant. You could also have the controller (or module) directly in the URL but that does not look pretty.

Since there is no Rails way of doing it (and probably not the best thing to do), here is a possible solution that will probably work:


get 'users/:id', to: 'admin/users#show', constraints: lambda { |request| 
  id = request.fullpath.gsub('/users/','').to_i
  User.find_by_id(id).admin?
}

get 'users/:id', to: 'regular/users#show', constraints: lambda { |request| 
  id = request.fullpath.gsub('/users/','').to_i
  !User.find(id).admin?
}

Thanks to kardeiz for the help. Again, this is just a possible solution that can be revised and improved until someone comes up with a Rails way of doing this.

RSpec – Reusing examples

A good practice while writing RSpec tests is to reuse similar examples to make the tests DRYer and easier to read and understand.

Create a shared_examples_for block with all the reused examples and include the block with it_behaves_like where you want to use the similar examples.


describe ".trigger_action"
  let(:subject) { User.trigger_action }

  shared_examples_for "email_sent" do
    it "sends an email" do
      expect(ActionMailer::Base.deliveries).not_to be_empty
    end
  end

  context "when valid" do
    it "is true" do
      expect(subject).to be_true
    end

    it_behaves_like "email_sent"
  end

  context "when invalid" do
    it "is false" do
      expect(subject).to be_false
    end

    it_behaves_like "email_sent"
  end
end

ATEM Wireless Tally Light

Wireless Tally Light System – Compatible with Blackmagic ATEM

If you have used the ATEM production switchers long enough you probably noticed that it’s missing a wireless tally light option. That’s no longer the case.

KvitkoIT has engineered and released a wireless tally light system for ATEM switchers which can be ordered or done as a DIY. The kit comes with a transmitter (which connects to the same network as the switcher using a Cat5 cable) and multiple receivers (which attach to your cameras using a hot shoe mount). This is all wireless so no wires are required between the transmitter and receivers.

The kit is Arduino-based so it’s fully customizable.

Check out the ATEM Wireless Tally Light Kit at KvitkoIT.

Arduino Clap Switch

Made a clap switch using Arduino. First experiment with this microcontroller. Works pretty well.

The switch that I used for the lamp can be found in Wal Mart. I opened it up, took the switch out and soldered an electric wire to the same location where the switch was.

Here it is in Wal Mart:
http://www.walmart.com/ip/GE-15-Amp-Polarized-Tap-with-Switch-White/16677117

Here is what mine looks like (used to look like that, I cleaned it up a bit):
photo (5)

The code for the Arduino clap switch is below. Some values will probably need to be adjusted depending on the environment. For example, the sound sensor was getting higher values in my office than in my bedroom. Have fun!


/*
	Arduino Clap Switch
	Artem Kalinchuk
	
	The THRESHOLD, SOUND_SAMPLE_LENGTH, 
	CLAP_DURATION_WINDOW, and CLAPS_FOR_TRIGGER are
	all adjustable values and will probably need to be 
	adjusted depending on your environment.
*/

const int SOUND_SENSOR = A0; //pin for the Grove Sound Sensor
const int LAMP_RELAY = 2; //pin for the Grove Relay
const int THRESHOLD = 400; //the sound level that will be treated as a 'clap'
const int SOUND_SAMPLE_LENGTH = 200; //the amount of ms to wait before determining to turn off/on
const int CLAP_DURATION_WINDOW = 1500; //the amount of ms max to make the number of claps specified (ms)
const int CLAPS_FOR_TRIGGER = 2; //the number of claps for the relay to trigger

//kind of used the same way as 'delay' but does not pause code.
//I use this because I have multiple 'delays' running in my original code.
//this 'delay' will make sure the relay does not switch on and off to fast.
//The current time is set to 1000 ms min (in code below)
unsigned long lastLampRelayLoop = 0;
int soundSensorReading = 0;
int soundLength = 0;
int previousSoundLength = 0;
int soundSampleLength = SOUND_SAMPLE_LENGTH;
int clapDurationWindow = CLAP_DURATION_WINDOW;
int currentClaps = 0;
int relayState = LOW;

void setup() {
  pinMode(LAMP_RELAY, OUTPUT);
}

void loop() {
  soundSensorReading = analogRead(SOUND_SENSOR);

  if (soundSensorReading >= THRESHOLD) { 
    soundLength++;
  } else {
    if (soundLength > 1) {
      previousSoundLength = soundLength;
    }

    soundLength = 0;
  }

  if (soundSampleLength == SOUND_SAMPLE_LENGTH) {
    soundSampleLength = 0;

    if (previousSoundLength > 1) { 
      clapDurationWindow = 0;
      currentClaps++;

      if (currentClaps == CLAPS_FOR_TRIGGER) {
        relayState = !relayState;

        if (millis()-lastLampRelayLoop >= 1000) {
          digitalWrite(LAMP_RELAY, relayState);
          lastLampRelayLoop = millis();
        }
      }

      previousSoundLength = 0;
    }
  }

  if (clapDurationWindow >= CLAP_DURATION_WINDOW) {
    currentClaps = 0; 
  }

  if (clapDurationWindow <= CLAP_DURATION_WINDOW) {
    clapDurationWindow++;
  }

  if (soundSampleLength < SOUND_SAMPLE_LENGTH) {
    soundSampleLength++; 
  }

  delay(1);
}

Ruby on Rails on Google Chrome OS

Have you been wondering how you can get Ruby on Rails running on the Google Chromium OS? Here is a way you can do it but do it at your own risk.

  1. I’m assuming that you know how to switch to development mode and make the file system writable. If not, here is a good tutorial.
  2. Once you’re logged in, go to http://www.archlinux.org/packages/core/i686/xz/ and download that file. This is a decompressor. Open shell, go to /home/chronos/user/Downloads folder and type tar -xvf xz-5.0.1-1-i686.pkg.tar.gz
  3. You will get a new folder in /Downloads called /usr. Type cd usr and then ls. You will see a list of folders like /bin and /lib. Do cd bin and then mv * /usr/bin/ . This will move everything that’s in the /bin folder to /usr/bin. Do that for all the folders in the usr folder. If you get an error saying that a certain file is a directory, open that directory and move all the files to the correct location in the systems /usr directory, make sure it’s consistent. Once that’s done, you should be able to run xz -d filename to decompress a file.
  4. Go to http://www.archlinux.org/packages/extra/i686/ruby/ and download that file also. These are the actual ruby files. Go to the /Downloads directory and and type xz -d ruby-1.9.2_p180-1-i686.pkg.tar.xz to decompress this file. Once decompressed, type tar -xvf ruby-1.9.2_p180-1-i686.pkg.tar . You will get a usr/ directory again. Do the same thing as you did with the other usr/ directory. Copy all the directories and files into the correct locations (/home/chronos/user/Downloads/usr/bin/* to /usr/bin/*, etc.)
  5. Once all the ruby files have been copied to the correct locations, you should be able to run ruby -v. Now it gets easier. Type gem install rails and it will install rails and it’s dependencies. You should know what to do from there on : )

Good luck! If you have any questions, feel free to ask.

Google Voice API Wrapper (PHP)

Update: Since this was written in 2010, Google might have changed Voice in a way that would make the below code unusable. If someone has any fixes, please let me know.

Since Google Voice does not have an official API, I decided to create a wrapper with PHP. Many people have been asking about some Google Voice API documentation but there isn’t any. This wrapper only sends and receives SMS messages for now. I will be adding more functions to it later. If you don’t like the way it works, then you can just use this as an example.

Note: Don’t forget to set $_rnr_se in the code below.


<?php
// GoogleVoice(EMAIL, PASSWORD)
$gv = new GoogleVoice('example@gmail.com', 'password');
// Sends an SMS. send_sms(NUMBER, MESSAGE)
echo $gv->send_sms('15555555555', 'Example');
// Gets all the sms
// get_sms() - returns all the sms
// get_sms(true) - returns all the unread sms
echo $gv->get_sms();

/**
  * Google Voice API Wrapper
  * 
  * new GoogleVoice(EMAIL, PASSWORD)
  * send_sms(NUMBER, MESSAGE)
  * get_sms()
  * get_sms(true) - unread
  *
  * @author Artem Kalinchuk
**/

Class GoogleVoice {
	/**
	  * Modify this
	**/
	var $account_type = 'GOOGLE'; 	// The Google account type
	var $service = 'grandcentral'; 	// Service for Google Voice is grandcentral (it may change)
	var $source = '';				// The host of your site (for logging purposes) 
	// _rnr_se - This can be found in the source code of the inbox page of your Google Voice
	// Simply view the source and search for '_rnr_se'. Should be a string of about 30
	// characters (numbers, letters, and symbols)
	var $_rnr_se = ''; 
	
	/**
	  * Do not modify
	**/
	var $url = 'https://www.google.com/';	// Google HTTPS URL
	var $auth; 								// The AUTH key
	var $email;								// Users email address
	var $password;							// Users password
	
	function __construct ($email, $password) {
		if ($email)
			$this->email = $email;
		if ($password)
			$this->password = $password;
			
		// Authenticate if the Auth key is empty
		if ($this->auth == '') {
			$this->authenticate();
		}
	}
	
	/**
	  * authenticate
	  * Authenticates using the email and password.
	  * @return Auth Session Key
	**/
	
	function authenticate () {
		$form_data = array();
		$form_data['accountType'] = $this->account_type;
		$form_data['Email'] = $this->email;
		$form_data['Passwd'] = $this->password;
		$form_data['service'] = $this->service;
		$form_data['source'] = $this->source;
		
		$response = $this->transmit($form_data, 'accounts/ClientLogin');
		preg_match("/Auth\=(.*)/", $response, $matches);

		if (count($matches) == 0) {
			return $response;
		} else {
			$this->auth = str_replace("Auth=", "", $matches[0]);
			return $this->auth;
		}
	}
	
	/**
	  * transmit
	  * Transmits the passed in POST data
	  * @param $form_data An array of POST fields and values
	  * @param $path The path to call
	  * @return Response from the server
	**/
	
	function transmit ($form_data, $path, $USE_POST=true) {
		$url = $this->url.$path;
		$fields = array();
		
		foreach ($form_data as $field => $value)
			$fields[] = $field.'='.urlencode($value);
		
		// POST or GET?
		if ($USE_POST) {
			$ch = curl_init($url);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($ch, CURLOPT_POST, count($form_data));
			curl_setopt($ch, CURLOPT_POSTFIELDS, implode('&', $fields));
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
			curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/x-www-form-urlencoded", "Authorization: GoogleLogin auth=".$this->auth));
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		} else {
			$ch = curl_init($url.'?'.implode('&', $fields));
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		}
		
		$response = curl_exec($ch);
		
		return $response;
	}
	
	/**
	  * send_sms
	  * Sends an SMS message
	  * @param $phone_number The number to send the SMS message to
	  * @param $text The message
	  * @return Response from the server (success or fail)
	**/
	
	function send_sms ($phone_number, $text) {
		$form_data = array();
		$form_data['phoneNumber'] = $phone_number;
		$form_data['text'] = $text;
		$form_data['id'] = '';
		$form_data['_rnr_se'] = $this->_rnr_se;
		
		$response = $this->transmit($form_data, 'voice/sms/send/');
		
		return $response;
	}
	
	/**
	  * get_sms
	  * Gets the HTML of the SMS inbox
	  * @param $UNREAD boolean - Show unread or not
	  * @return The HTML from the SMS inbox page
	**/
	
	function get_sms($UNREAD=false) {
		$form_data = array();
		$form_data['auth'] = $this->auth;
		
		if ($UNREAD)
			$path = 'voice/inbox/recent/unread/';
		else
			$path = 'voice/inbox/recent/';
			
		$response = $this->transmit($form_data, $path, false);
		
		return $response;
	}
}
?>

Google Voice API – Ruby on Rails

I’ve been searching google for a ruby on rails Google Voice API but couldn’t find anything. I decided to make my own…since I know there are others that would like some help. This is just a simple way of sending an SMS message using Google Voice.

The first thing you have to do is login using your Google account.


require 'net/http'
require 'net/https'
require 'uri'

login_response = ''

url = URI.parse('https://www.google.com/accounts/ClientLogin')
login_req = Net::HTTP::Post.new(url.path)
login_req.form_data = {'accountType' => 'GOOGLE', 'Email' => 'YOUR_GOOGLE_ACCOUNT(full)', 'Passwd' => 'YOUR_PASSWORD', 'service' => 'grandcentral', 'source' => 'ANYTHING_YOU_WANT(eg. mysite.com)'}
login_con = Net::HTTP.new(url.host, url.port)
login_con.use_ssl = true
login_con.start {|http| login_response = http.request(login_req)}

Notice the last line. It sets login_response to the http response which contains the auth token that you will need to use when sending the SMS message.

Once you have successfully logged in, you can send the message.


url = URI.parse('https://www.google.com/voice/sms/send/')
req = Net::HTTP::Post.new(url.path, { 'Content-type' => 'application/x-www-form-urlencoded', 'Authorization' => 'GoogleLogin auth='+login_response.body.match("Auth\=(.*)")[0].gsub("Auth=", "") })
# We're sending the auth token back to google
req.form_data = {'id' => '', 'phoneNumber' => '555555555', 'text' => 'Sample message', '_rnr_se' => 'FROM_GOOGLE_VOICE_PAGE(view source)'}
con = Net::HTTP.new(url.host, url.port)
con.use_ssl = true
con.start {|http| response = http.request(req)}

The response will be {“ok”:true…} if successful. Notice ‘_rnr_se’. You can get this by going to your google voice inbox and viewing the source for that page. Search for ‘_rnr_se’ and use that value to send it back to google. As you can see, we’re sending “auth” with the google auth response that we got after logging into google.

I split the code into 2 parts but it should be put together into 1 part.

JavaScript :: Drag Objects on Page

I was working on a little JavaScript program and I wanted to let users be able to drag objects around on the screen and make the web page customizable but could not find any usable code.  I searched for hours until I found a pretty good script that does the job right.

Click here for an example. (Click on the header to move the object around on the page)

The Code

First you will need some divs to move around on the page.

1
2
3
4
5
6
<div id="boxB" cass="box" style="left:400px;top:150px;">
  <div class="bar" style="width:12em;"
       onmousedown="dragStart(event, 'boxB')">Drag Box B</div>
  <div class="content" style="width:12em;">This is Box B, drag it using the bar above.</div>
 
</div>

Some styles for the divs….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.box {
  background-color: #ffff00;
  border: 1px solid #000000;
  color: #000000;
  padding: 0px;
  position: absolute;
}
 
.bar {
  background-color: #008080;
  color: #ffffff;
  cursor: move;
  font-weight: bold;
  padding: 2px 1em 2px 1em;
}
 
.content {
  padding: 1em;
}

And the JavaScript… (leave the header as it is because this is not my work)
This first function determines the browser that the user is using and the version to make sure it’s compatible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//*****************************************************************************
// Do not remove this notice.
//
// Copyright 2001 by Mike Hall.
// See http://www.brainjar.com for terms of use.
//*****************************************************************************
 
// Determine browser and version.
 
function Browser() {
 
  var ua, s, i;
 
  this.isIE    = false;
  this.isNS    = false;
  this.version = null;
 
  ua = navigator.userAgent;
 
  s = "MSIE";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isIE = true;
    this.version = parseFloat(ua.substr(i + s.length));
    return;
  }
 
  s = "Netscape6/";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isNS = true;
    this.version = parseFloat(ua.substr(i + s.length));
    return;
  }
 
  // Treat any other "Gecko" browser as NS 6.1.
 
  s = "Gecko";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isNS = true;
    this.version = 6.1;
    return;
  }
}
 
var browser = new Browser();
 
// Global object to hold drag information.
 
var dragObj = new Object();
dragObj.zIndex = 0;

The DragStart function which is called when the user clicks on the header to move the object…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
function dragStart(event, id) {
 
  var el;
  var x, y;
 
  // If an element id was given, find it. Otherwise use the element being
  // clicked on.
 
  if (id)
    dragObj.elNode = document.getElementById(id);
  else {
    if (browser.isIE)
      dragObj.elNode = window.event.srcElement;
    if (browser.isNS)
      dragObj.elNode = event.target;
 
    // If this is a text node, use its parent element.
 
    if (dragObj.elNode.nodeType == 3)
      dragObj.elNode = dragObj.elNode.parentNode;
  }
 
  // Get cursor position with respect to the page.
 
  if (browser.isIE) {
    x = window.event.clientX + document.documentElement.scrollLeft
      + document.body.scrollLeft;
    y = window.event.clientY + document.documentElement.scrollTop
      + document.body.scrollTop;
  }
  if (browser.isNS) {
    x = event.clientX + window.scrollX;
    y = event.clientY + window.scrollY;
  }
 
  // Save starting positions of cursor and element.
 
  dragObj.cursorStartX = x;
  dragObj.cursorStartY = y;
  dragObj.elStartLeft  = parseInt(dragObj.elNode.style.left, 10);
  dragObj.elStartTop   = parseInt(dragObj.elNode.style.top,  10);
 
  if (isNaN(dragObj.elStartLeft)) dragObj.elStartLeft = 0;
  if (isNaN(dragObj.elStartTop))  dragObj.elStartTop  = 0;
 
  // Update element's z-index.
 
  dragObj.elNode.style.zIndex = ++dragObj.zIndex;
 
  // Capture mousemove and mouseup events on the page.
 
  if (browser.isIE) {
    document.attachEvent("onmousemove", dragGo);
    document.attachEvent("onmouseup",   dragStop);
    window.event.cancelBubble = true;
    window.event.returnValue = false;
  }
  if (browser.isNS) {
    document.addEventListener("mousemove", dragGo,   true);
    document.addEventListener("mouseup",   dragStop, true);
    event.preventDefault();
  }
}

DragGo…moves the object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function dragGo(event) {
 
  var x, y;
 
  // Get cursor position with respect to the page.
 
  if (browser.isIE) {
    x = window.event.clientX + document.documentElement.scrollLeft
      + document.body.scrollLeft;
    y = window.event.clientY + document.documentElement.scrollTop
      + document.body.scrollTop;
  }
  if (browser.isNS) {
    x = event.clientX + window.scrollX;
    y = event.clientY + window.scrollY;
  }
 
  // Move drag element by the same amount the cursor has moved.
 
  dragObj.elNode.style.left = (dragObj.elStartLeft + x - dragObj.cursorStartX) + "px";
  dragObj.elNode.style.top  = (dragObj.elStartTop  + y - dragObj.cursorStartY) + "px";
 
  if (browser.isIE) {
    window.event.cancelBubble = true;
    window.event.returnValue = false;
  }
  if (browser.isNS)
    event.preventDefault();
}

And DragStop which stops the object from moving

1
2
3
4
5
6
7
8
9
10
11
12
13
function dragStop(event) {
 
  // Stop capturing mousemove and mouseup events.
 
  if (browser.isIE) {
    document.detachEvent("onmousemove", dragGo);
    document.detachEvent("onmouseup",   dragStop);
  }
  if (browser.isNS) {
    document.removeEventListener("mousemove", dragGo,   true);
    document.removeEventListener("mouseup",   dragStop, true);
  }
}

And that’s it. Here is the combined version of all the code…feel free to use but don’t remove the header.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<style type="text/css">
 
.box {
  background-color: #ffff00;
  border: 1px solid #000000;
  color: #000000;
  padding: 0px;
  position: absolute;
}
 
.bar {
  background-color: #008080;
  color: #ffffff;
  cursor: move;
  font-weight: bold;
  padding: 2px 1em 2px 1em;
}
 
.content {
  padding: 1em;
}
 
</style>
 
<script type="text/javascript">
//<![CDATA[
 
//*****************************************************************************
// Do not remove this notice.
//
// Copyright 2001 by Mike Hall.
// See http://www.brainjar.com for terms of use.
//*****************************************************************************
 
// Determine browser and version.
 
function Browser() {
 
  var ua, s, i;
 
  this.isIE    = false;
  this.isNS    = false;
  this.version = null;
 
  ua = navigator.userAgent;
 
  s = "MSIE";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isIE = true;
    this.version = parseFloat(ua.substr(i + s.length));
    return;
  }
 
  s = "Netscape6/";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isNS = true;
    this.version = parseFloat(ua.substr(i + s.length));
    return;
  }
 
  // Treat any other "Gecko" browser as NS 6.1.
 
  s = "Gecko";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isNS = true;
    this.version = 6.1;
    return;
  }
}
 
var browser = new Browser();
 
// Global object to hold drag information.
 
var dragObj = new Object();
dragObj.zIndex = 0;
 
function dragStart(event, id) {
 
  var el;
  var x, y;
 
  // If an element id was given, find it. Otherwise use the element being
  // clicked on.
 
  if (id)
    dragObj.elNode = document.getElementById(id);
  else {
    if (browser.isIE)
      dragObj.elNode = window.event.srcElement;
    if (browser.isNS)
      dragObj.elNode = event.target;
 
    // If this is a text node, use its parent element.
 
    if (dragObj.elNode.nodeType == 3)
      dragObj.elNode = dragObj.elNode.parentNode;
  }
 
  // Get cursor position with respect to the page.
 
  if (browser.isIE) {
    x = window.event.clientX + document.documentElement.scrollLeft
      + document.body.scrollLeft;
    y = window.event.clientY + document.documentElement.scrollTop
      + document.body.scrollTop;
  }
  if (browser.isNS) {
    x = event.clientX + window.scrollX;
    y = event.clientY + window.scrollY;
  }
 
  // Save starting positions of cursor and element.
 
  dragObj.cursorStartX = x;
  dragObj.cursorStartY = y;
  dragObj.elStartLeft  = parseInt(dragObj.elNode.style.left, 10);
  dragObj.elStartTop   = parseInt(dragObj.elNode.style.top,  10);
 
  if (isNaN(dragObj.elStartLeft)) dragObj.elStartLeft = 0;
  if (isNaN(dragObj.elStartTop))  dragObj.elStartTop  = 0;
 
  // Update element's z-index.
 
  dragObj.elNode.style.zIndex = ++dragObj.zIndex;
 
  // Capture mousemove and mouseup events on the page.
 
  if (browser.isIE) {
    document.attachEvent("onmousemove", dragGo);
    document.attachEvent("onmouseup",   dragStop);
    window.event.cancelBubble = true;
    window.event.returnValue = false;
  }
  if (browser.isNS) {
    document.addEventListener("mousemove", dragGo,   true);
    document.addEventListener("mouseup",   dragStop, true);
    event.preventDefault();
  }
}
 
function dragGo(event) {
 
  var x, y;
 
  // Get cursor position with respect to the page.
 
  if (browser.isIE) {
    x = window.event.clientX + document.documentElement.scrollLeft
      + document.body.scrollLeft;
    y = window.event.clientY + document.documentElement.scrollTop
      + document.body.scrollTop;
  }
  if (browser.isNS) {
    x = event.clientX + window.scrollX;
    y = event.clientY + window.scrollY;
  }
 
  // Move drag element by the same amount the cursor has moved.
 
  dragObj.elNode.style.left = (dragObj.elStartLeft + x - dragObj.cursorStartX) + "px";
  dragObj.elNode.style.top  = (dragObj.elStartTop  + y - dragObj.cursorStartY) + "px";
 
  if (browser.isIE) {
    window.event.cancelBubble = true;
    window.event.returnValue = false;
  }
  if (browser.isNS)
    event.preventDefault();
}
 
function dragStop(event) {
 
  // Stop capturing mousemove and mouseup events.
 
  if (browser.isIE) {
    document.detachEvent("onmousemove", dragGo);
    document.detachEvent("onmouseup",   dragStop);
  }
  if (browser.isNS) {
    document.removeEventListener("mousemove", dragGo,   true);
    document.removeEventListener("mouseup",   dragStop, true);
  }
}
 
//]]>
</script>
 
<div id="boxB" class="box" style="left:400px;top:150px;">
  <div class="bar" style="width:12em;"
       onmousedown="dragStart(event, 'boxB')">Drag Box B</div>
  <div class="content" style="width:12em;">This is Box B, drag it using the bar above.</div>
 
</div>