Skip to content
November 3, 2010

Linear Cryptanalysis of SPN

No idea if this works correctly. My highest bias ended up not being what I calculated, so who knows. I’m beat and it’s late

 

import random, math, operator, pprint

substitutions = [[0, 8], [1, 4], [2, 2], [3, 1], [4, 12], [5, 6], [6, 3], [7, 13], [8, 10], [9, 5], [10, 14], [11, 7], [12, 15], [13, 11], [14, 9], [15, 0]]

keys = ["0010100010101001", "0111010001111011", "0011110110100011", "1100100101101110", "0100110110101101"]

## {{{ http://code.activestate.com/recipes/113799/ (r1)
#
# bitfield manipulation
#
class bf(object):
    def __init__(self,value=0):
        self._d = value

    def __getitem__(self, index):
        return (self._d >> index) & 1

    def __setitem__(self,index,value):
        value    = (value&1L)<<index -="" -1="" :="" def="" mask="(1L)<> start) & mask

    def __setslice__(self, start, end, value):
        mask = 2L**(end - start) -1
        value = (value & mask) << start
        mask = mask <> start) & mask

    def __int__(self):
        return self._d
## end of http://code.activestate.com/recipes/113799/ }}}

def sbox(inp):
	out = ""
	for i in range(0, 4):
		s = ""
		for j in range(0, 4):
			s = s + str(inp[i * 4 + j])
		x = int(s, 2)
		o = bin(substitutions[x][1])[2:]
		while len(o) != 4:
			o = "0" + o
		out = out + o
	ret = [None] * 16

	for i in range(16):
		ret[i] = int(out[i])

	return ret

# Reverses a singular sbox instead of an entire string
def reverse_sbox(inp):
	s = ""
	for i in range(4):
		s = s + str(inp[i])
	x = int(s, 2)

	for i in range(16):
		if substitutions[i][1] == x:
			ret = [None] * 4
			temp = bf(substitutions[i][0])
			ret[0] = temp[3]
			ret[1] = temp[2]
			ret[2] = temp[1]
			ret[3] = temp[0]
			return ret

def permutation(inp):
	out = [None] * 16
	out[0] = inp[0]
	out[1] = inp[4]
	out[2] = inp[8]
	out[3] = inp[12]
	out[4] = inp[1]
	out[5] = inp[5]
	out[6] = inp[9]
	out[7] = inp[13]
	out[8] = inp[2]
	out[9] = inp[6]
	out[10] = inp[10]
	out[11] = inp[14]
	out[12] = inp[3]
	out[13] = inp[7]
	out[14] = inp[11]
	out[15] = inp[15]

	return out

def keymix(cipher, key_i):
	for j in range(16):
		cipher[j] = (int(cipher[j]) + int(keys[key_i][j])) % 2
	return cipher

def encrypt(plaintext):
	print "Encrypting plaintext: " + str(plaintext)
	print

	cipher = [None] * 16

	for i in range(16):
		cipher[i] = int(plaintext[i])

	for i in range(3):
		print "Round " + str(i + 1)
		print "Cipher: " + str(cipher)
		#Key mixing
		cipher = keymix(cipher, i)
		print "Keymix: " + str(cipher)

		#S-Box
		cipher = sbox(cipher)
		print "S-Box: " + str(cipher)

		#Permutation
		cipher = permutation(cipher)
		print "Permutation: " + str(cipher)
		print

	#Last round
	print "Round 4"
	print "Cipher: " + str(cipher)
	cipher = keymix(cipher, 3)
	print "Keymix: " + str(cipher)

	cipher = sbox(cipher)
	print "S-Box: " + str(cipher)

	print
	print "Final Keymix..."
	cipher = keymix(cipher, 4)
	print "Keymix: " + str(cipher)

	return cipher

def plaintext_generator():
	plaintext = [None] * 16
	for x in range(16):
		plaintext[x] = random.randint(0, 1)
	return plaintext


def generator(amount):
	f = open("pairs.txt", "w")
	for x in range(amount):
		plaintext = plaintext_generator()
		cipher = encrypt(plaintext)
		for y in range(16):
			f.write(str(plaintext[y]))
		f.write(" : ")
		for y in range(16):
			f.write(str(cipher[y]))
		f.write("\n")
	f.close()

def cryptanalyze(amount):
	pairs = []
	key_counter = {}
	for x in range(256):
		key_counter[x] = 0

	print "Reading data file..."
	f = open("pairs.txt", "r")
	for line in f:
		p = line.split(" : ")[0]
		c = line.split(" : ")[1].replace("\n", "")
		pairs.append([p, c])
	f.close()

	print "Extracting key bits..."
	for i in range(amount):
		for j in range(256):
			plain = pairs[i][0]
			cipher = pairs[i][1]
			field = bf(j)

			#XOR with "key"
			c1 = [None] * 4		#Key result 1
			c2 = [None] * 4		#Key result 2

			#bf class reverses bit order
			c1[0] = (int(cipher[0]) + field[3]) % 2
			c1[1] = (int(cipher[1]) + field[2]) % 2
			c1[2] = (int(cipher[2]) + field[1]) % 2
			c1[3] = (int(cipher[3]) + field[0]) % 2
			c2[0] = (int(cipher[8]) + field[7]) % 2
			c2[1] = (int(cipher[9]) + field[6]) % 2
			c2[2] = (int(cipher[10]) + field[5]) % 2
			c2[3] = (int(cipher[11]) + field[4]) % 2

			c1 = reverse_sbox(c1)
			c2 = reverse_sbox(c2)

			if (c1[0] + c2[1] + int(plain[15])) % 2 == 0:
				key_counter[j] = key_counter[j] + 1

	print "Calculating biases..."
	for i in range(256):
		key_counter[i] = math.fabs(key_counter[i] - 1024.0) / 2048.0

	pp = pprint.PrettyPrinter()
	sorted_keys = sorted(key_counter.items(), key = operator.itemgetter(1))
	field = bf(sorted_keys[255][0])		#Highest bias

	print "Bias: " + str(sorted_keys[255][1]) + " (key value: " + str(sorted_keys[255][0]) + ")"
	print "Keybits: [" + str(field[7]) + ", " + str(field[6]) + ", " + str(field[5]) + ", " + str(field[4]) + "] ... [" + str(field[3]) + ", " + str(field[2]) + ", " + str(field[1]) + ", " + str(field[0]) + "]"


# Generate 8 * (1/16) ^ -2 plaintext/ciphertexts (2048 plaintext/ciphertext pairs)

print "Commands:"
print "generate"
print "analyze"
print

while True:
	cmd = raw_input("Enter command: ")
	if cmd == "generate":
		generator(2048)
	elif cmd == "analyze":
		cryptanalyze(2048)
	elif cmd == "exit" or cmd == "quit":
		break

 

October 30, 2010

iTunes Song Rating with Logitech G15 G-Keys and Python

I just put together a quick script for setting song ratings in iTunes using a Logitech G15 keyboard and the programmable “G-keys” For this to work, you’ll need Python (I’m using 2.7) and the pywin32 package (http://sourceforge.net/projects/pywin32/)

import win32com.client
import sys

itunes = win32com.client.gencache.EnsureDispatch("iTunes.Application")

itunes.CurrentTrack.Rating = int(sys.argv[1]) * 20

Put this in a script somewhere easy to remember. Then, open up the Logitech Key Profiler, and for each G-key, set it up as such (example is the first G-key):

G1 -> Assign Shortcut

Name: Whatever you want. I used Rate 1 
Shortcut: pythonw itunes_rate.py 1
Working Directory: ##Location of your script if you don’t want to enter the entire path to the script##

Just do that and you should be good to go. Another thing to note is that I have the Python directory in my environment path. The version pythonw lets Python execute without bringing up a command prompt window, which gets annoying and might interrupt you if you’re playing a game or something.

October 21, 2010

iTunes Playcount Updater Using Last.fm

This script takes your Last.fm username, finds your playcounts for tracks in your iTunes library, and updates those playcounts. Useful if you base playlists off of how much you listen to a particular song and you need to keep your playcounts in sync from various sources.

#
#	iTunes Playcount Updater
#
#	This program is free software: you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation, either version 3 of the License, or
#	(at your option) any later version.
#
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with this program.  If not, see .
#
#
#	INSTRUCTIONS:
#	----------------------------------------------------------------------------
#	This script downloads your Last.FM play history and updates the playcount
#	for all tracks it can find in your library. There are two modes, one which uses
#	currently selected tracks, and the other which searches your entire Last.fm
#	library
#
#	Requirements:
#		pywin32
#		BeautifulSoup

#Strange or International characters will be either replaced or removed. Map them here
strangeCharacterMap = ({
	u"\u042f" : "r" #Korn's crazy R
})

import sys
import win32com.client
import urllib
from BeautifulSoup import BeautifulSoup
import unicodedata
import time

lastFMUrl = "http://ws.audioscrobbler.com/2.0/?method="

apiKey = "YOUR_API_KEY"
itunes = None

username = ""

def getTracks(page):
	url = lastFMUrl + "library.gettracks&api_key=" + apiKey + "&user=" + username + "&page=" + str(page)
	data = urllib.urlopen(url).read()
	soup = BeautifulSoup(data)

	totalpages = int(soup.find("tracks")["totalpages"])

	tracks_soup = soup.findAll("track")

	tracks = {}

	for t in tracks_soup:
		name = t.find("name").next.replace("&", "&").replace(""", "\"")
		artist = t.find("artist").find("name").next.replace("&", "&").replace(""", "\"")
		plays = int(t.find("playcount").next)

		tracks[artist + " " + name] = [artist, name, plays]

	return (tracks, totalpages)

def check_users_library():
	libraryPlaylist = itunes.LibraryPlaylist

	page = 0

	tracks, pages = getTracks(1)

	while page < pages:
		page = page + 1
		tracks, x = getTracks(page)

		print "Page: " + str(page) + " / " + str(pages)

		for track in tracks:
			search = libraryPlaylist.Search(track, 1)
			if search == None:
				continue
			else:
				for x in search:
					if x.Artist.lower() == tracks[track][0].lower() and x.Name.lower() == tracks[track][1].lower():
						x.PlayedCount = tracks[track][2]

def check_selected_tracks():
	selectedTracks = itunes.SelectedTracks

	for t in selectedTracks:
		last_access = time.clock()
		url = ""
		try:
			url = lastFMUrl + "track.getInfo&api_key=" + apiKey + "&username=" + username + "&artist=" + \
				  urllib.quote(unicodedata.normalize("NFKD", t.Artist).encode("ascii", "ignore")) + \
				  "&track=" + urllib.quote(unicodedata.normalize("NFKD", t.Name).encode("ascii", "ignore")) + "&autocorrect=1"
		except:
			print "Problem creating URL for " + t.Artist + " - " + t.Name
			continue

		data = urllib.urlopen(url).read()
		soup = BeautifulSoup(data)

		playcount = soup.find("userplaycount")

		if playcount == None:
			print "No plays for " + t.Artist + " - " + t.Name
		else:
			t.PlayedCount = int(playcount.next)
			print t.Artist + " - " + t.Name + " played " + str(t.PlayedCount) + " times"
		while time.clock() - last_access < 1.0:
			pass

def main():
	global username
	global itunes

	username = raw_input("Last.FM Username: ")

	itunes = win32com.client.gencache.EnsureDispatch("iTunes.Application")

	print
	print "Use mode:"
	mode = raw_input("1 = Check selected tracks\n2 = Check entire Last.fm library\n")

	if mode == "1":
		check_selected_tracks()
	elif mode == "2":
		check_users_library()
	else:
		pass

if __name__ == "__main__":
	main()
	print "Completed"

October 20, 2010

Linear Approximation for S-Boxes

Based off of tutorial by Howard M. Heys

import pprint

"""
input  0 1 2 3 4 5 6 7 8 9 A B C D E F
output 8 4 2 1 C 6 3 D A 5 E 7 F B 9 0
"""

inp = ["0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"]
out = ["1000", "0100", "0010", "0001", "1100", "0110", "0011", "1101", "1010", "0101", "1110", "0111", "1111", "1011", "1001", "0000"]

# S-Box from Heys tutorial
#out = ["1110", "0100", "1101", "0001", "0010", "1111", "1011", "1000", "0011", "1010", "0110", "1100", "0101", "1001", "0000", "0111"]

def check_bits(inbits, outbits):
    total = 0

    for x in range(len(inp)):
        r = 0
        for a in inbits:
            r ^= int(inp[x][a])
        for b in outbits:
            r ^= int(out[x][b])
        if r == 0:
            total += 1
    return total

def linear_approximation():
    result = []
    for i in range(len(inp)):
        row = []
        for j in range(len(inp)):
            inbits = []
            outbits = []
            for x in range(len(inp[i])):
                if inp[i][x] == "1":
                   inbits.append(x)
            for x in range(len(inp[j])):
                if inp[j][x] == "1":
                   outbits.append(x)

            row.append(check_bits(inbits, outbits) - 8)
        result.append(row)
    return result


if __name__ == '__main__':
    pp = pprint.PrettyPrinter()
    pp.pprint(linear_approximation())

October 5, 2010

Miller-Rabin Error Testing

This program brute force tests the Miller-Rabin algorithm to determine error probabilities. For the assignment, we ran it from 101,000 to 102,000. The program runs in about 25 minutes.

import sys, time

#Runs the algorithm for some values n and a. m and k are already calculated
def miller_rabin(n, a, m, k):
	#pow(x, y, z) = x^y mod z
	b = pow(a, m, n)
	#b is congruent to 1 (mod n)
	if b == 1:
		return (True, n, a, m, k, b) #Prime

	for i in xrange(0, k):
		#b is congruent to -1 (mod n)
		if b == n - 1:
			return (True, n, a, m, k, b) #Prime
		else:
			b = (b * b) % n

	return (False, n, a, m, k, b) #Composite

#Performs the overall loop and saves the data to a file
def miller_time():
	start = time.clock()
	f = open("stats.txt", "a")
	for n in xrange(101000, 102001):
		print n
		primes = 0
		composites = 0

		m = n - 1
		k = 0

		#Calculate k and m
		while m %  2 == 0:
			m &gt;&gt;= 1
			k += 1

		for a in xrange(1, n):
			ret = miller_rabin(n, a, m, k)
			if ret[0]:
				primes += 1
			else:
				composites += 1
		f.write("n=" + str(n) + ", Primes=" + str(primes) + ", Composites=" + str(composites) + ", Error=" + str(1.0 * primes / (primes + composites)))
		f.write("\n")
	f.close()
	end = time.clock()
	t = end - start
	print str(t) + " seconds"

#Sorts and then displays errors for the dataset
def stats():
	f = open("stats.txt", "r")
	data = []
	for line in f:
		n = int(line.split(",")[0].split("=")[1])
		p = int(line.split(",")[1].split("=")[1])
		c = int(line.split(",")[2].split("=")[1])
		e = float(line.split(",")[3].split("=")[1])

		data.append((n, p, c, e))

	data.sort(key=lambda x: x[3])

	for e in data:
		print e

#Command input
while True:
	cmd = raw_input("Enter command: ")
	if cmd == "it's miller time":
		miller_time()
	elif cmd == "stats":
		stats()
	elif cmd == "exit" or cmd == "quit":
		break

September 22, 2010

Vigenère Cipher

This python program can be used to do some analysis on a Vigenère cipher and then try to decrypt it with a given key

Usage:

#:> python vigenere.py
Enter command: ioc 4
0.0663865546218
0.0719887955182
0.072268907563
0.06918767507
AVERAGE: 0.0699579831933
Enter command: split 4
hgofttubjastbsfsnzaauftfvaabvvacavygluegzflfeyfbonyjvgflavnzghpgyhhzsvfnpauftxnfaaata
dvowchcioqpvicrwqdokpowcgocrbbrdrrwvcwocoohipwofcgzbgogmbgrccgctcqgowbfbazhhamrrourqh
smwemsalflvxrpmrlmqmsrvjsqjseklcmyxeywalwjsksxwcyqxmrkmesmvrreyyjlxvpwsxsmgmywsspamsv
fkaefkqgmgaqycvvsgslqvdzoskelssansqllvfaadssoalslsglgghjjlqggufdkugqdseagysfjzoogafmq
Enter command: key noes
uponthisbasisiamgoingtoshowyouhowabunchofbrightyoungfolksdidfindachampionamanwit
hboysandgirlsofhisownamanofsodominatingandhappyindividualitythatyouthisdrawntohi
masisaflytoasugarbowlitisastoryaboutasmalltownitisnotagossipyyarnnorisitadrymono
tonousaccountfullofsuchcustomaryfillinsasromanticmoonlightcastingmurkyshadowsdow
nalongwindingcountryroad

'''
	vigenere.py
	Andrew Burgess (andrew@deceptacle.com)
	Corey Greenhawk

	Can be used to do some analysis on a ciphertext encoded with a Vigenere cipher and attempt to decrypt

	The function "cheat" is used because the ciphertext was said to be from "Gadsby" by Ernest Vincent Wright
	Due to this information, it can be found that the plaintext would not contain the letter 'e'
	"cheat" takes advantage of this by removing potential key letters that would result in 'e' as the
	decrypted plaintext

	"cheat_brute" does some further trimming by removing decrypted plaintexts that contain way too many 'x's
	or 'z's (guessed amount, but seemed reasonable). This leaves a file with a much smaller possible set
	of potential plaintexts which can be quickly scanned to find the key
'''

'''
	Guessed keylength of 4 based on results from Index of Coincidence calculations
'''

#Block length of at most 6

cipher = "hdsfgvmkoowafweetcmfthskucaqbilgjofmaqlgspvatvxqbiryscpcfrmvswrvnqlszdmgaoqsakmlupsqforvtwvdfcjzvgsoaoqsacjkbrsevbelvbksarlscdcaarmnvrysywxqgvellcyluwwveoafgclazowafojdlhssfiksepsoywxafowlbfcsocylngqsyzxgjbmlvgrggokgfgmhlmejabsjvgmlnrvqzcrggcrghgeupcyfgtydycjkhqluhgxgzovqswpdvbwsffsenbxapasgazmyuhgsfhmftayjxmwznrsofrsoaopgauaaarmftqsmahvqecev"
#cipher = "lxfopvefrnhr" 	#Decryptes to "attackatdawn" Key is "lemonlemonle"

base = {} #Used for the base alphabet
for x in range(0, 26):
	base[chr(97 + x)] = 0

def split_string(length, s):
	substrings = []
	p = 0 #Passes
	for x in range(1, length + 1):
		substrings.append("")
		i = 0 + p
		while i  4:
			return

	if "z" in cnt:			#drop out way too many z's in the text
		if cnt["z"] &gt; 8:
			return
	f.write("KEY: " + key)
	f.write("\n")
	f.write(decrypt(key))
	f.write("\n\n")

while True:
	'''	Commands:
		ioc 		Calculates index of coincidence for the keylength
		key 			Attempts to decrypt using the given key
		split 	Splits the ciphertext into the substrings used in IOC
		cheat 	Basically brute forces an answer using the provided keylength
	'''
	command = raw_input("Enter command: ")

	if (command.split(" ")[0] == "ioc"):
		ioc(int(command.split(" ")[1]))
	elif (command.split(" ")[0] == "key"):
		print decrypt(command.split(" ")[1])
	elif (command.split(" ")[0] == "split"):
		s = split_string(int(command.split(" ")[1]), cipher)
		for x in s:
			print x
	elif (command.split(" ")[0] == "cheat"):
		cheat(int(command.split(" ")[1]))
	elif (command == "exit"):
		break;

September 13, 2010

Affine Cipher Decryptor

Solves a cipher text encrypted with the Affine cipher using two guessed cipher/plaintext pairs Usage:

#:> python affine.py
Trying with two characters...

First character (CIPHER=plain): I=a
Second character (CIPHER=plain): H=f
Guess is: a = 5, b = 8
affinecipher

'''
	Affine Cipher Decryptor
	Andrew Burgess (andrew@deceptacle.com)

	Using two guessed cipher/plaintext character pairs, guesses a key and shows the decrypted text
'''

cipher = "IHHWVCSWFRCP"		#Cipher text to crack (becomes affinecipher when solved)

'''
	Extended Euclidean Algorithm implementation.

	First number returned is the multiplicative inverse for u if v is the base
	Third number determines whether u and v are actually coprime. Should be 1 if they are
'''
def eea(u, v):
	u1 = 1
	u2 = 0
	u3 = u
	v1 = 0
	v2 = 1
	v3 = v
	while v3 != 0:
		q = u3 / v3
		t1 = u1 - q * v1
		t2 = u2 - q * v2
		t3 = u3 - q * v3
		u1 = v1
		u2 = v2
		u3 = v3
		v1 = t1
		v2 = t2
		v3 = t3
	return u1, u2, u3

'''
	Decrypts a character based on the alpha/beta values of the algorithm
'''
def decrypt(key, shift, char):
	v = eea(key, 26)

	return chr(((v[0]) * ((ord(char) - 65) - shift) % 26) + 97)

'''
	Guesses a key/shift pair based on a CIPHER=plaintext combo

	Input Example:
		First character: Y=e
		Second character: N=j

	Returns a pair of integers. The first is the key, and the second is the shift
'''
def guess_key(key1, key2):
	p = ord(key1.split("=")[1]) - 97
	r = ord(key1.split("=")[0]) - 65
	q = ord(key2.split("=")[1]) - 97
	s = ord(key2.split("=")[0]) - 65

	d = (p - q) % 26
	dinv = eea(d, 26)[0]

	a = (dinv * (r - s)) % 26
	b = (dinv * (p * s - q * r)) % 26

	return a, b

'''
	Loop to allow multiple guesses without needing to restart the program
'''
while True:
	print "Trying with two characters..."
	print
	key1 = raw_input("First character (CIPHER=plain): ")
	key2 = raw_input("Second character (CIPHER=plain): ")

	a, b = guess_key(key1, key2)

	print "Guess is: a = " + str(a) + ", b = " + str(b)

	message = ""
	for c in cipher:
		message = message + decrypt(a, b, c)

	print message
	raw_input()

August 25, 2010

jQuery Masked Input

Found this plugin for jQuery for masking input boxes to restrict input to a specific format. http://digitalbush.com/projects/masked-input-plugin/ Definitely pleased with this plugin. Very easy to use, and the format for specifying masks is easy as well.

$("#CellPhone").mask("(999) 999-9999");

That’s how easy it is to set up a mask. I do have one problem with it though. It’s basically a one to one mapping for characters (you can specify optional sections on the end) but it doesn’t allow something more regex based. For instance: at least one but up to three letters, followed by 6 numbers (a135923 and ab473922). I will probably try to extend it if I ever run across this situation though. Give it a look if you need to restrict input on textboxes.

August 3, 2010

Creating a Vertically Oriented Table

For the project that I’m working on at my company, one of the pages required developing a table that was vertically oriented (first cell in each row is a header, each data item takes a column). It also had to be editable in place, which means that using a traditional table would be extremely difficult and hackish. I settled on using unordered lists and some jQuery to give the appearance of a table, and it seems to have worked out splendidly.

CSS Styles

.table-container {
	border: 1px solid #AAAAAA;
}
.vertical-list-container {
	overflow-x:scroll;
	width: 600px;
	border-left: 1px solid #AAAAAA;
}
.vertical-list {
	float: left;
	width: 230px;
	padding-bottom:10px;
}
.vertical-list .label {
	padding-right: 14px;
	text-align:left !important;
	font-weight: bold;
}
.vertical-list li {
	vertical-align: middle;
	text-align: center;
	margin-top: 5px;
	padding: 5px 10px;
	min-height: 15px;
	border-bottom: 1px dotted #E0E0E0;
	border-right: 1px dotted #E0E0E0;
}

jQuery magic to make it more table-y. This code basically extends the inner section (id = “scroller”) to the maximum width of the table columns (based on how many there are). It then stretches the container to fit within the whole parent container that the scroll bar will go all the way across. Finally, it loops through each column, and then uses a custom selector to find any cell heights that are above the minimum amount, and then updates all of the heights for those rows

jQuery

$.extend($.expr[':'], {
	height: function (a, i, m) {
		//Check to make sure it's :height(&gt;##) or :height(&lt;##)
		if (!m[3] || !(/^()\d+$/).test(m[3])) { return false; }
		return m[3].substr(0, 1) === "&gt;" ?
						$(a).height() &gt; m[3].substr(1) : $(a).height() 15)").each(function (index) {
			var z = $(this).index();
			var height = $(this).height();
			$(".vertical-list").each(function (y) {
				$(this).find("li:eq(" + z + ")").height(height);
			});
		});
	});
});

Then finally, here’s the structure for the HTML code that displays the data

HTML

<div class="table-container">
	<ul class="vertical-list float-left">
		<li>Row Header</li>
		<li>Row Header</li>
	</ul>
	<div class="vertical-list-container">
		<div id="scroller">
			<ul class="vertical-list">
				<li>Item 1, Field 1</li>
				<li>Item 1, Field 2</li>
			</ul>
			<ul class="vertical-list">
				<li>Item 2, Field 1</li>
				<li>Item 2, Field 2</li>
			</ul>
		</div>
	</div>
</div>

August 2, 2010

BP Spills Coffee

Jay shared this, and I thought it was amazing. Check it out

Follow

Get every new post delivered to your Inbox.