Tips and Tricks About Computers, Web Development, Linux, the Internet and the Like
Programming
jQuery and jQuery UI in your Grease Monkey scripts.
Jul 16th
Not all websites are made equal. Unfortunately, some of those websites are ones we’re required to use. Be it our job, our interests or the undiscovered, web-browsing can be the orgeon trail of the internet. Cultivating the atmosphere to work for us and bringing the right tools along for the journey are essential for survival, if you’re a pioneer.
I’m tired of websites that don’t provide, can’t provide or will never provide. I’ve called in my boys, Stylish and Grease Monkey on the front lines. However, beyond the crazy happenstance that another user might share a solution to your issue on userstyles.org or userscripts.org you could be left in the cold, without the right tools. I won’t go into detail on Stylish in this post but you might not even need it after you see the script.
Since I’m already connected with Google every which way in my life, why not pretend like the Internet is a big party and websites are your friends. Introduce jQuery from Google’s CDN to other friends (websites) that don’t already know jQuery and jQuery UI and make it personal.
That was the driving force behind this Grease Monkey Script, which was derived from mashing together strd6′s script and Joan Piedra’s script with an update to the latest versions of jQuery and jQuery UI.
// ==UserScript==
// @name jQuery and jQuery UI
// @author Tyler "-z-" Mulligan
// @version 0.5
// @namespace http://www.doknowevil.net
// @description jQuery 1.6.2 and jQuery UI 1.8.4 loaded from google's CDN with redmond theme.
// @include http://www.website.com/*
//
// @resource jQuery https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js
// @resource jQueryUI https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js
//
// @resource jQueryUICSS http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/jquery-ui.css
//
// @resource ui-bg_inset-hard_100_fcfdfd_1x100.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png
// @resource ui-bg_gloss-wave_55_5c9ccc_500x100.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png
// @resource ui-bg_glass_85_dfeffc_1x400.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_glass_85_dfeffc_1x400.png
// @resource ui-bg_glass_75_d0e5f5_1x400.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png
// @resource ui-bg_inset-hard_100_f5f8f9_1x100.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png
// @resource ui-bg_flat_55_fbec88_40x100.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_flat_55_fbec88_40x100.png
// @resource ui-bg_glass_95_fef1ec_1x400.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_glass_95_fef1ec_1x400.png
// @resource ui-icons_469bdd_256x240.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-icons_469bdd_256x240.png
// @resource ui-icons_469bdd_256x240.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-icons_469bdd_256x240.png
// @resource ui-icons_d8e7f3_256x240.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-icons_d8e7f3_256x240.png
// @resource ui-icons_6da8d5_256x240.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-icons_6da8d5_256x240.png
// @resource ui-icons_217bc0_256x240.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-icons_217bc0_256x240.png
// @resource ui-icons_f9bd01_256x240.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-icons_f9bd01_256x240.png
// @resource ui-icons_2e83ff_256x240.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-icons_2e83ff_256x240.png
// @resource ui-icons_cd0a0a_256x240.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-icons_cd0a0a_256x240.png
// @resource ui-bg_flat_0_aaaaaa_40x100.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png
// @resource ui-bg_flat_0_aaaaaa_40x100.png http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png
// ==/UserScript==
var $;
// Inject jQuery into page... gross hack... for now...
(function() {
if (typeof unsafeWindow.jQuery == 'undefined') {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
var jQuery = GM_getResourceText('jQuery');
var jQueryUI = GM_getResourceText('jQueryUI');
script.innerHTML = jQuery + jQueryUI;
head.appendChild(script);
}
GM_wait();
})();
// Check if jQuery's loaded
function GM_wait() {
if (typeof unsafeWindow.jQuery == 'undefined') {
window.setTimeout(GM_wait, 100);
} else {
$ = unsafeWindow.jQuery.noConflict(true);
addUIStyles();
letsJQuery();
}
}
function addUIStyles() {
var head = document.getElementsByTagName('head')[0];
var resources = {
'ui-bg_inset-hard_100_fcfdfd_1x100.png': GM_getResourceURL('ui-bg_inset-hard_100_fcfdfd_1x100.png'),
'ui-bg_gloss-wave_55_5c9ccc_500x100.png': GM_getResourceURL('ui-bg_gloss-wave_55_5c9ccc_500x100.png'),
'ui-bg_glass_85_dfeffc_1x400.png': GM_getResourceURL('ui-bg_glass_85_dfeffc_1x400.png'),
'ui-bg_glass_75_d0e5f5_1x400.png': GM_getResourceURL('ui-bg_glass_75_d0e5f5_1x400.png'),
'ui-bg_inset-hard_100_f5f8f9_1x100.png': GM_getResourceURL('ui-bg_inset-hard_100_f5f8f9_1x100.png'),
'ui-bg_flat_55_fbec88_40x100.png': GM_getResourceURL('ui-bg_flat_55_fbec88_40x100.png'),
'ui-bg_glass_95_fef1ec_1x400.png': GM_getResourceURL('ui-bg_glass_95_fef1ec_1x400.png'),
'ui-icons_469bdd_256x240.png': GM_getResourceURL('ui-icons_469bdd_256x240.png'),
'ui-icons_469bdd_256x240.png': GM_getResourceURL('ui-icons_469bdd_256x240.png'),
'ui-icons_d8e7f3_256x240.png': GM_getResourceURL('ui-icons_d8e7f3_256x240.png'),
'ui-icons_6da8d5_256x240.png': GM_getResourceURL('ui-icons_6da8d5_256x240.png'),
'ui-icons_217bc0_256x240.png': GM_getResourceURL('ui-icons_217bc0_256x240.png'),
'ui-icons_f9bd01_256x240.png': GM_getResourceURL('ui-icons_f9bd01_256x240.png'),
'ui-icons_2e83ff_256x240.png': GM_getResourceURL('ui-icons_2e83ff_256x240.png'),
'ui-icons_cd0a0a_256x240.png': GM_getResourceURL('ui-icons_cd0a0a_256x240.png'),
'ui-bg_flat_0_aaaaaa_40x100.png': GM_getResourceURL('ui-bg_flat_0_aaaaaa_40x100.png'),
'ui-bg_flat_0_aaaaaa_40x100.png': GM_getResourceURL('ui-bg_flat_0_aaaaaa_40x100.png')
};
var style = document.createElement('style');
style.type = 'text/css';
var css = GM_getResourceText ('jQueryUICSS');
$.each(resources, function(resourceName, resourceUrl) {
console.log(resourceName + ': ' + resourceUrl);
css = css.replace( 'images/' + resourceName, resourceUrl);
});
style.innerHTML = css;
head.appendChild(style);
}
// All your GM code must be inside this function
function letsJQuery() {
//alert($); // check if the dollar (jquery) function works
//alert($().jquery); // check jQuery version
$("
<div id='example' class='flora' title='This is my title'>I'm in a dialog!</div>
").dialog({
buttons: {
"Ok": function() {
alert("Ok");
},
"Cancel": function() {
$(this).dialog("close");
}
}
});
}
I also wanted to use this script with Chromium browser, the open-source version of Google Chrome. I found the TamperMonkey Extension for Chrome. Stylish is also available for Chrome.
Generating CSS Based on Images, also SEO-Friendly
Feb 20th
I’ve typed out a lot of CSS that’s focused on using a background image to replace text. I’ve been using a cross-browser CSS trick that’s SEO-friendly, I’m not sure if it’s documented anywhere. I came upon the solution myself when I was searching for a clever way to do this years ago and it’s become the standard method for me. However, I dislike the tedious task of typing out the CSS and referencing the image pixels and at work, I don’t want my developers wasting that time either.
I figured it’d be best to come up with a simple solution, so I started coding something out in bash and it worked:
#!/bin/bash
# css.sh - generate common css and html
# Tyler Mulligan (z@interwebninja.com)
# Last Update: 02/16/2011
# MIT License
create_block_image() {
feh -lr ${1:-.} | awk '{ print $3" "$4" "$8 }' | sed '1d' | while read l; do N=$((N+1));
cbi_css $l
done
feh -lr ${1:-.} | awk '{ print $3" "$4" "$8 }' | sed '1d' | while read l; do N=$((N+1));
cbi_html_a $l
done
feh -lr ${1:-.} | awk '{ print $3" "$4" "$8 }' | sed '1d' | while read l; do N=$((N+1));
cbi_html_div $l
done
}
cbi_css() {
f=${3##*/}
echo "#${f%.*} {
display: block;
background: url('$3') no-repeat 0 0;
width: 0;
height: $2px;
padding-left: $1px;
overflow: hidden;
}"
}
cbi_html_a() {
f=${3##*/}
echo "<a href=\"#\" id=\"${f%.*}\" alt=\"$f\" title=\"$f\">${f%.*}</a>"
}
cbi_html_div() {
f=${3##*/}
echo "
<div id=\"${f%.*}\"></div>
"
}
s=$1; shift; case $s in
--image-block|--cbi|-i) create_block_image $@;;
*) help help;;
esacWhich ouputs something like:
z@zygon:~/scripts/css$ ./css.sh -i img/
#dumbtubes-fav {
display: block;
background: url('img/dumbtubes-fav.png') no-repeat 0 0;
width: 0;
height: 14px;
padding-left: 16px;
overflow: hidden;
}
#submit_new {
display: block;
background: url('img/submit_new.png') no-repeat 0 0;
width: 0;
height: 106px;
padding-left: 244px;
overflow: hidden;
}
#dumbtubes-logo {
display: block;
background: url('img/dumbtubes-logo.png') no-repeat 0 0;
width: 0;
height: 70px;
padding-left: 274px;
overflow: hidden;
}
<a href="#" id="dumbtubes-fav" alt="dumbtubes-fav.png" title="dumbtubes-fav.png">dumbtubes-fav</a>
<a href="#" id="submit_new" alt="submit_new.png" title="submit_new.png">submit_new</a>
<a href="#" id="dumbtubes-logo" alt="dumbtubes-logo.png" title="dumbtubes-logo.png">dumbtubes-logo</a>
<div id="dumbtubes-fav"></div>
<div id="submit_new"></div>
<div id="dumbtubes-logo"></div>
To explain the way this CSS works, it uses the padding-left as the actual width, setting the width 0 and then pushes whatever content you have out of view using overflow:hidden to hit it from view. This makes it easy for search engines, keeping your HTML clean and CSS simple.
The bash script relies of “feh” a lightweight image viewer for linux that outputs a list of images and their dimensions. It also generates some sample HTML to quickly drop in and modify.
This is not an elegant solution and I’m not happy with the fact that it relies on feh. I’ve been slowly getting into python and I was amazed at how fast I was able to recreate this script in Python.
#!/usr/bin/python
# css.py - generate common css and html
# Tyler Mulligan (z@interwebninja.com)
# Last Update: 02/17/2011
# MIT License
from PIL import Image
import sys
import os.path
CSS_FORMAT = """#%s {\n\
display: block;\n\
background: url('%s') no-repeat 0 0;\n\
height: %spx;\n\
width: 0;\n\
padding: %spx;\n\
overflow: hidden;\n\
}\n"""
HTML_A_FORMAT = """<a href="#" id="%s" alt="%s" title="%s">%s</a>\n"""
HTML_DIV_FORMAT = """
<div id="%s">%s</div>
\n"""
css=""
html_a=""
html_div=""
for tf in os.listdir(sys.argv[1]):
f = os.path.join(sys.argv[1],tf)
if os.path.isfile(f) == True :
img = Image.open(f)
fid = os.path.splitext(tf)[0]
(width, height) = img.size[0:2]
css += CSS_FORMAT % (fid,f,height,width)
html_a += HTML_A_FORMAT % (fid,f,fid,fid)
html_div += HTML_DIV_FORMAT % (fid,fid)
print css
print html_a
print html_div This is just the basic idea, I didn’t spend more than 15 minutes on this rewrite, it does what I need it to do so far. It was suggested by friends if I make it any bigger, to look into a templating engine, such as mako. I hope this script can be useful to someone :). PS, HTML_DIV_FORMAT should be on one line, not sure why my syntax highlighter is trying to put it on 3.
MySQL and Python – The Problems and [Some] Solutions
Oct 15th
Intro
I’m still relatively new to Python. I dabble once in a while. Usually when I catch myself writing something overly complicated in bash. I figured doing MySQL from a Python script would be easier and better implemented in Python. Well, I was in for a few surprises I’d like to share with others looking to integrate with MySQL in Python before you encounter the same problems I did.
Getting on with it
As a Python noob, it made sense to me to use the python-mysqldb module. It “works” but not well. Read through the [working] code below and see if you can find the problem and how I circumvented it.
#!/bin/python
# Tyler Mulligan (tyler@doknowevil.net)
# MySQL the WRONG way in Python
import MySQLdb
def connection_settings():
global DB_ADMIN, DB_ADMIN_PASSWORD, DB_NAME, DB_HOST, DB_USER, DB_PASSWORD
DB_ADMIN="root"
DB_ADMIN_PASSWORD="local"
DB_NAME="foobardb"
DB_HOST="localhost"
DB_USER="foobar"
DB_PASSWORD="local"
def run_admin_sql():
conn = MySQLdb.Connection(host=DB_HOST, user=DB_ADMIN, passwd=DB_ADMIN_PASSWORD)
cursor = conn.cursor()
#cursor.execute("DROP USER %s@%s", (DB_USER, DB_HOST))
#cursor.execute("DROP DATABASE %s" % DB_NAME)
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (DB_USER, DB_HOST, DB_PASSWORD))
cursor.execute("CREATE DATABASE %s" % DB_NAME)
cursor.execute("GRANT USAGE ON %s.* TO %s@%s IDENTIFIED BY '%s'" % (DB_NAME, DB_USER, DB_HOST, DB_PASSWORD))
cursor.execute("GRANT ALL PRIVILEGES ON *.* to %s@%s IDENTIFIED BY %s", (DB_USER, DB_HOST, DB_PASSWORD))
cursor.close()
conn.close()
conn = MySQLdb.Connection(db=DB_NAME, host=DB_HOST, user=DB_USER, passwd=DB_PASSWORD)
cursor = conn.cursor()
cat_table_sql = """CREATE TABLE cats
(
`id` int(3) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`age` int(2) NOT NULL,
`type` int(2) NOT NULL,
PRIMARY KEY (`id`)
)"""
cursor.execute(cat_table_sql)
cursor.execute("INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)", ("fluffy", 5, 1))
cursor.execute("INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)", ("meow meow", 6, 1))
cursor.execute("INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)", ("purrfect", 3, 1))
cursor.execute("SELECT * FROM cats")
results = cursor.fetchall()
cursor.close()
conn.close()
return results
connection_settings()
print run_admin_sql()The problem is parameterization. The python-mysqldb module sucks at it. It’s glaringly obvious when you look at the follow snippet and how I worked around it.
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (DB_USER, DB_HOST, DB_PASSWORD)) # GOOD
cursor.execute("CREATE DATABASE %s" % DB_NAME) # BAD NEWS BEARS
cursor.execute("CREATE DATABASE %s", (DB_NAME)) # but the module won't support this
The difference, is that the first implementation uses the module’s parameterization to escape the variables that are passed to it, making it safe. The second is using Python’s string formatter which means I’d have to do all the escaping and sanitization prior, which is obviously more dangerous and annoying.
The MySQLdb docs bury this little note, “Parameter placeholders can only be used to insert column values. They can not be used for other parts of SQL, such as table names, statements, etc.”
You can implement it this way but you’ll be wasting your time. There are better modules out there to make up for this one’s shortcomings. OurSQL and SQLAlchemy were the two libraries recommended to me in #python on irc.freenode.org. I will make another post when I develop the same example above using one of these libraries.
New Python eBook, Much Better, Down and Dirty
Jul 1st
Some friends recommended a book that’s a quicker pace with some better programming practices. Learn Python The Hard way (or quick way :-P). It reads more like a walk-through tutorial / quick reference and gives you easily repeatable programming practices that will get you a stronger feel for the language. It’s probably best used in combination with the Python Documentation. Also, a fair warning, Dive Into Python has war declared against it.


