Saturday, October 25, 2008

Wednesday, October 22, 2008

Google Chrome theme for Firefox



This is how my Firefox looks. Note that this isn't maximized or trimmed, there just isn't a title of menu bar. The menu is accessible via a drop down on the top right as well as window controls.

Here is a list of the addon's and tweaks I used to get this look:
  • Chromifox: This is the primary theme used.
  • Stylish: For minor CSS tweaks.
  • Autohide: This extension allows you to go into Zombie mode and hide the titlebar.
  • Compact Menu 2: This extension allows you to hide the main menu and add a toolbar icon instead.
  • FaviconizeTab: Lets you minimize tabs to just show their icon.
Extra settings:
  • When using Autohide you will need to open up about:config and set the setting extensions.autohide.zombieBorder to 0. This will remove the titlebar and border when you go into zombie mode. (Shift-F11)

Runnig Firefox Pre Releases

I always like the latest and greatest so here are instructions for running the latest and greatest Firefox builds while maintaining browser auto-updated and extensions.

First off you need to download the latest night build from http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/. Alternately you can get the 3.1 Beta witch is not updated as often but still has the newest features at http://releases.mozilla.org/pub/mozilla.org/firefox/releases/3.1b1/. The second option may be more stable but it will replace your Firefox install where as the nightly installs in a separate location.

Regardless of witch version you install when done you will have a few problems, namely most ad dons will report out of date and won't load. To fix this open up Firefox and navigate to about:config. Now search for the setting extensions.checkCompatibility or add it if needed. Also set it to false. This will simply make extensions load even if their versions don't match Firefox. Now some addon's still may not work but all of my major addon's work just fine.

Also while here we want to change a few other settings. Next find the setting general.useragent.extra.firefox and set it to Firefox/3.0. This just changes the Firefox version reported to web sites so the Install Extension links in https://addons.mozilla.org/en-US/firefox still work correctly.

Also we want to enable the new TraceMonkey Javascript Engine witch is much faster and one of the best features of 3.1. To do so again in about:config find the setting javascript.options.jit.content and set it to true.

You should now be all set, just restart Firefox and check out some sites. Also note that extensions and settings are shared between all versions of Firefox but noting we changed will break Firefox 3.0.

Ubiquity Command: Search Google Web History

Search your Google Web History.

web-history.js
makeSearchCommand({
name: "web-history",
homepage: "http://pynej.blogspot.com/",
author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},
url: "http://www.google.com/history/find?q={QUERY}",
icon: "http://www.google.com/favicon.ico",
description: "Searches Google Web History."
});

Monday, October 20, 2008

My Firefox Extentions

Here is how I see the world.

(Picture coming soon)

Ubiquity Command: Open in New Tab

Here is a Ubiquity script to open a link in a new tab.

open_in_new_tab.js
CmdUtils.CreateCommand({
name: "open_in_new_tab",
homepage: "http://pynej.blogspot.com/",
author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},
description: "Open the selected url in a new tab.",
takes: {"URL": noun_type_url},
preview: function( pblock, url ) {
pblock.innerHTML = "Will open: " + url.text;
},
execute: function( url ) {
Utils.openUrlInBrowser(url.text);
}
})

Ubiquity Command: Note in Reader

Update: Monday, August 18, 2009 9:54 am
Updated script to work with newer framework.

Update: Monday, October 20, 2008 2:52 pm
Updated the script to actually work. Also you can now enter your note on the command line itself.



Here is a Ubiquity script to note a page in Google Reader.
note-in-reader.js
CmdUtils.CreateCommand({
name: ["note-in-reader", "add-to-reader", "share-to-reader"],
homepage: "http://pynej.blogspot.com/",
author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},
description: "Note this page in Google Reader.",
arguments: [ {role: 'object', nountype: noun_arb_text, label: 'note'} ],
execute: function( note ) {
var document = Application.activeWindow.activeTab.document;
var gc = "var b=document.body;var GR________bookmarklet_domain='https://www.google.com';if(b&&!document.xmlVersion){void(z=document.createElement('script'));void(z.src='https://www.google.com/reader/ui/link-bookmarklet.js');void(b.appendChild(z));}else{}";

void(z=document.createElement('script'));
z.appendChild(document.createTextNode(gc));
document.body.appendChild(z);

if(note.text)
{
var timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
timer.initWithCallback(
function() {
document.getElementById("GR________link_bookmarklet_frame").contentDocument.getElementById("annotation").innerHTML= note.text;
},
1500,
Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}
}
})

Friday, October 10, 2008

Comfigure Gmai to stay online in Mail.

Here is another tip for Gmail users that want to work in Mail.app.

When you set up a Gmail account in Mail.app it will default to POP. Now that Gmail supports IMAP you can use that instead. The difference between the two is simply that POP downloads everything from the server and works locally where as IMAP access the server and works directly there.

The two major benefits to using IMAP are that any changes you make to your mailbox locally. First of moving a message from one folder to anther is reflected on the server and secondly only a list of message is retrieved instead of the entire mailbox.

The second benefit isn't quite true though as Mail.app by default downloads the entire mailbox and all the attachments for offline viewing. Now as I rarely use Mail.app and furthermore never use the offline mode, I disabled this feature and substantially improved the Gmail synchronization. Not only the headers are cached and the message and attachments are retrieved when I open a message.

To change this setting you will need to remove your Gmail account, to clear out the cache, and add it again, this time manually set it up and on the last screen uncheck "Take this account online". This will prevent syncing. Now go to the Advanced tab and change "Keep a copies of messages for offline viewing" to "Don't keep copies of any messages".

Problem Syncing Gmail with Mail 3.0 in OS X 10.5

I general use Gmail via the web but I do have it configure in Mail.app for IMAP access as well for sending photos and files more easily. I was trying to send some photos today and had a problem with mail hanging as it were. It wasn't exactly crashing, but upon launching it would just sit there trying to sync and send files but never succeed. This blocked access to the IMAP. I verified the configuration but there were no problems.

As it turns out the problem was that Mail.app was trying to redo some task that had failed at an earlier time and these tasks were no invalid. Probably send some photo's that no longer exist. To clean up this problem just execute this command in a the terminal.


rm ~/Library/Mail/IMAP-*/.OfflineCache/*

Tuesday, October 7, 2008

Flex DataGrid LookupColumn

Update- 10/27/2008 3:27 pm
Updated the LookupColumn control to search for items via a hash array. The old code would loop through the source for each item witch would make the computation do R*N checks not a has array is created so that only R check's are needed. This makes a big difference is there area are large number rows or lookup items.

I highly recommend updating to this code to make renders run faster.


Here is another useful Flex component. This is a DataGridColumn that can be used to do simple lookups from another source when the DataGrid is rendered. You can use it to full data from a second source without having to first merge the data of write custom label functions.

Here is a simple example:

private const myGroups:ArrayCollection = new ArrayCollection(
[{groupid: 1, name: "Admin"},
{groupid: 2, name: "User"},
{groupid: 3, name: "Guest"}]);

<mx:DataGrid dataProvider="{}">
<mx:columns>
<mx:DataGridColumn headerText="User Name" dataField="username"/>
<LookupColumn headerText="Group" dataField="groupid" labelField="name" source="{myGroups}"/>
</mx:columns>
</mx:DataGrid>

LookupColumn.as
package hines
{
import flash.events.Event;

import mx.collections.ArrayCollection;
import mx.controls.dataGridClasses.DataGridColumn;

[Exclude(name="dataTipField", kind="property")]

/**
* This class is a custom DataGridColumn taht will look up each item in another array and fill in a field from the found item in its place.
*
* Example:
* private const myGroups:ArrayCollection = new ArrayCollection(
* [{groupid: 1, name: "Admin"},
* {groupid: 2, name: "User"},
* {groupid: 3, name: "Guest"}]);
*
* <hines:LookupColumn headerText="Group" dataField="groupid" labelField="groupname" source="{myGroups}"/>
*
* This will cause and rows with a groupid of 1 to render as "Admin" and so on.
*
* This will also work with a xml source and the lookupField can be used if the field names differ.
*
* <hines:LookupColumn headerText="Users Group" dataField="usersgroupid" lookupField="groupid" labelField="groupname" source="{myGroups}"/>
*
* @author jpyne
*/
public class LookupColumn extends DataGridColumn
{
public function LookupColumn(columnName:String=null)
{
super(columnName);

// Set up the internal lableFunction.
labelFunction = LookupColumn.doLookup;
}


//----------------------------------
// labelField
//----------------------------------

private var _labelField:String;

/**
* Field to display in the destination record. If this is not set it will default to "label".
*/
[Inspectable(category="General", defaultValue="")]
[Bindable("labelFieldChanged")]
public function get labelField():String
{
return _labelField ? _labelField : "label";
}

/**
* @private
*/
public function set labelField(value:String):void
{
_labelField = value;

dispatchEvent(new Event("labelFieldChanged"));
}


//----------------------------------
// lookupField
//----------------------------------

private var _lookupField:String;

/**
* Field to search for in the source ArrayCollection. If unset then datafield will be used.
*/
[Inspectable(category="General", defaultValue="")]
[Bindable("lookupFieldChanged")]
public function get lookupField():String
{
return _lookupField ? _lookupField : dataField;
}

/**
* @private
*/
public function set lookupField(value:String):void
{
_lookupField = value;

if(source)
prepSource();

dispatchEvent(new Event("lookupFieldChanged"));
}


//----------------------------------
// source
//----------------------------------

private var _source:ArrayCollection;

/**
* Field to display in the destination record. If this is not set it will default to "label".
*/
[Inspectable(category="General", defaultValue="")]
[Bindable("sourceChanged")]
public function get source():ArrayCollection
{
return _source;
}

/**
* @private
*/
public function set source(value:ArrayCollection):void
{
_source = value;

if(lookupField)
prepSource();

dispatchEvent(new Event("sourceChanged"));
}


//----------------------------------
// hashSource
//----------------------------------

private var _hashSource:Object;

public function get hashSource():Object
{
return _hashSource;
}

private function prepSource():void
{
_hashSource = new Object();

for each (var item:Object in source)
{
_hashSource[item[lookupField]] = item;
}
}


/**
* Override the dataTipFiled and change it to the dataField as that will be hidden.
*/
override public function get dataTipField():String
{
return dataField;
}

/**
* Look for a item in a source and display its labelField. This is an internal method and will be called for each item in teh DataGrid.
*
* @param row
* @param control
* @return
*/
static private function doLookup(row:Object, control:LookupColumn):String
{
if(control.hashSource && control.hashSource[row[control.dataField]])
return control.hashSource[row[control.dataField]][control.labelField];

// If no match was found, just return the original value.
return row[control.dataField];
}
}
}

Monday, October 6, 2008

Auto Mount/Unmout Disk Images

Here are two scripts to automatically mount and unmout image files in Linux.

Console Script:
/usr/local/bin/isomnt
#!/bin/bash
if [ -e "$*" ]; then
if [ -d /media/"$*" ]; then
echo "Unmounting '$*'..."
sudo umount -d /media/"$*"
sudo rmdir /media/"$*"
else
echo "Mounting '$*'..."
sudo mkdir /media/"$*"
sudo mount -o loop "$*" /media/"$*"
fi
else
echo "No image found to mount."
fi

Nautilus Script:
~/.gnome2/nautilus-scripts/(Un)Mount ISO
#!/bin/bash
if [ -d /media/"$*" ]; then
gksudo ls /media
sudo umount -d /media/"$*"
sudo rmdir /media/"$*"
else
gksudo mkdir /media/"$*"
wd=${NAUTILUS_SCRIPT_CURRENT_URI#file://}
sudo mount -o loop "$wd/$*" /media/"$*"
fi

MediaWiki Blacklist Extension

This is an older extension I wrote for MediaWiki to allow the blacklisting of various pages.

To install the extention just ave the code below to blacklist.php in MediaWiki's extensions folder and add the following line in the LocalSettings.php file:
include_once("extensions/blacklist.php");

Example: To block some special pages for normal users, but not sysops, do this.
$wgWhitelist['sysop']['read']  = $wgBlacklist['*']['read'] = array("Special:Export", "Special:Listusers", "Special:Ipblocklist", "Special:Log", "Special:Allmessages");

Or with a RegEx:
$wgBlacklistOps["useRegex"] = true;
$wgWhitelist['sysop']['read'] = $wgBlacklist['*']['read'] = array("^Special:(Export|Listusers|Ipblocklist|Log|Allmessages)$");

blacklist.php
<?php
/*
Blacklist Mediawiki Extension
By Jeremy Pyne jeremy.pyne@gmail.com

This extension adds support for a $wgBlacklist array, layed out like $wgGroupPermissions, to support overrides.
For example I can set $wgBlacklist['*']['read'] to diable specific special pages or
make some pages of the site only visible for special groups.
This blacklisting is done from lowest to highest powered groups and is implisit. IE if you deny Main Page to User, it also denies it for all parent's of user.
To override a blacklist at a higher level ou have to add an entry to $$wgWhitelist['sysop']['read'] to re-enable the pages if you are a sysop.

Options:
$wgBlacklistOps["useRegex"] = true;
This setting dictates whether to tread the page lists as regular expressions or not. Though turning regular expressions off is much faster, you can not
mark page groups, partial page titles, or variations of title formating.

Example: To block some special pages for normal users, but not sysops do this.
$wgWhitelist['sysop']['read'] = $wgBlacklist['*']['read'] = array("Special:Export", "Special:Listusers", "Special:Ipblocklist", "Special:Log", "Special:Allmessages");
Or wth a RegEx
$wgBlacklistOps["useRegex"] = true;
$wgWhitelist['sysop']['read'] = $wgBlacklist['*']['read'] = array("^Special:(Export|Listusers|Ipblocklist|Log|Allmessages)$");

Note: This is not flawless method as page inclusions and such can get around this.
*/

if (!defined('MEDIAWIKI')) die();

$wgExtensionCredits['other'][] = array(
'name' => 'blacklist',
'description' => 'adds $wgBlacklist array to provide blacklist overrides',
'url' => 'http://www.mediawiki.org/wiki/Extension:Blacklist',
'author' => 'Jeremy Pyne',
'version' => '1.0'
);

$wgHooks['userCan'][] = 'checkBlacklist';

/**
* Is this page blacklisted
* @param &$title the concerned page
* @param &$wgUser the current mediawiki user
* @param $action the action performed
* @param &$result (out) true or false, or null if we don't care about the parameters
*/
function checkBlacklist(&$title, &$wgUser, $action, &$result) {
global $wgBlacklist;
global $wgWhitelist;
global $wgBlacklistOps;
$hideMe = false;

$groupPower = array(
0 => "*",
1 => "user",
2 => "autoconfirmed",
3 => "emailconfirmed",
4 => "bot",
5 => "sysop",
6 => "bureaucrat");
$myGroups = array_intersect($groupPower, $wgUser->getEffectiveGroups());

foreach($myGroups as $myGroup) {
if(array_key_exists($myGroup, $wgBlacklist) && array_key_exists($action, $wgBlacklist[$myGroup]) && is_array($wgBlacklist[$myGroup][$action]))
{
if($wgBlacklistOps["useRegex"]) {
foreach($wgBlacklist[$myGroup][$action] as $myBlacklist)
if(preg_match("/$myBlacklist/", $title->getPrefixedText()))
{
$hideMe = true;
break;
}
} else {
$myBlacklist = array_flip($wgBlacklist[$myGroup][$action]);
if(array_key_exists($title->getPrefixedText(), $myBlacklist))
$hideMe = true;
}
}

if(array_key_exists($myGroup, $wgWhitelist) && array_key_exists($action, $wgWhitelist[$myGroup]) && is_array($wgWhitelist[$myGroup][$action]))
{
if($wgBlacklistOps["useRegex"]) {
foreach($wgWhitelist[$myGroup][$action] as $myWhitelist)
if(preg_match("/$myWhitelist/", $title->getPrefixedText()))
{
$hideMe = false;
break;
}
} else {
$myWhitelist = array_flip($wgWhitelist[$myGroup][$action]);
if(array_key_exists($title->getPrefixedText(), $myWhitelist))
$hideMe = false;
}
}
}

if($hideMe)
$result = false;

return !$hideMe;
}

?>

Switch Statment for Smarty

There is a complete and working switch plugin. It works exactly as a php switch and you can even use variables and modifiers for the conditions. Feel free to use it and let me know if you have and problems.

11/23/2010
I moved the code to github for matinance. https://github.com/pynej/Smarty-Switch-Statement/archives/Smarty-2.0

03/08/2008 - Updated to version 2
This update changes the break attribute to work how you would expect. That is, setting the break attribute will cause smarty to automatically render a {break} tag before it starts the NEXT case or default section. I strongly recommend updating to this version of the switch plugin, as the older version was very counter intuitive in this aspect.

02/09/2010 - Posted Smarty 3 Version
A separate version of this plug-in is available for Smarty 3 here.

(Also, this does work with nested switches, witch is why you see it accessing the _switchData variable. Be sure to not alter this variable in smarty code, as it will make the switches behave incorrectly.)

Both of these blocks will produce identical switch logic.
{case 1 break}
Code 1
{case 2}
Code 2
{default break}
Code 3

{case 1}
Code 1
{break}
{case 2}
Code 2
{default}
Code 3
{break}


03/08/2008 - Updated to version 2.1
Added {/case} tag, this is identical to {break}.

MediaWiki Tidy URLs

This article describes how to configure nice URLs on MediaWiki. The wiki page on the topic is rather massive and hard to understand so here are the instructions for Apache2.
Before:
http://my.domain.com/wiki/index.php?title=Main_Page&action=view

After:
http://my.domain.com/wiki/Main_Page/view


Add the following likes at the end of LocalSettings.php. Update $wgScriptPath to reflect you install.
LocalSettings.php
$wgScriptPath = "/wiki";

$wgArticlePath = "$wgScriptPath/$1";

$actions = array('view', 'edit', 'watch', 'unwatch', 'delete','revert', 'rollback', 'protect',
'unprotect','info','markpatrolled','validate','render','deletetrackback','print',
'dublincore','creativecommons','credits','submit','viewsource','history','purge');

foreach ($actions as $a)
$wgActionPaths[$a] = "$wgScriptPath/$1/$a";


Enable the loading of the mod_rewrite.c module.
LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so


Add the following to your httpd.conf or virtual site. Again, replace /wiki as needed.
RewriteCond %{REQUEST_URI} /(view|edit|watch|unwatch|delete|revert|rollback|protect|unprotect|info|markpatrolled|validate|render|deletetrackback|print$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/wiki/(.*)/([a-z]*)$ /wiki/index.php?title=$1&action=$2 [L,QSA]

RewriteCond %{REQUEST_URI} !/wiki/images
RewriteCond %{REQUEST_URI} !/wiki/skins
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/wiki/(.*)$ /wiki/index.php?title=$1 [PT,L,QSA]

Flex DateUtil Class

Upadte- 10/8/2008 1:26 PM
Added DateUtil.create() and DateUtil.clone() static methods. These can be used to work with a existing date. The Clone will cause updates to the original source where as the clone will not.

DateUtil.clone(StartDate.selectedDate).date;

Upadte- 10/10/2008 9:54 AM
Fixed a problem with the getters corrupting the date.


When working with dates and times in flex I found myself wanting to make simple changes like adding a day or trimming off the time witch turned out to be quite a few lines of code. Here is a example of setting a Date Field to yesterday and trimming off the time.
[Bindable]
private var prevDate:Date;

prevDate.hours = prevDate.minutes = prevDate.seconds = prevDate.milliseconds = 0;
prevDate.date--;

// Update the DateField.
dtDate.selectedDate = prevDate;
dtDate.dispatchEvent(new CalendarLayoutChangeEvent(CalendarLayoutChangeEvent.CHANGE));

<mx:datefield id="dtDate" change="" selecteddate="{prevDate}">

As you can see this is a rather large chunk of code to just change the date so I decided to create a DateUtil class. Here is the same operation using the DateUtil class.
<hines:DateField id="dtDate" change="" selectedDate="{DateUtil.now.subtract(DateUtil.DATE, 1).date}"/>

Here are some more examples of the DateUtil class.
var time:Date = DateUtil.now.time;
var monday:Date = DateUtil.now.assign(DateUtil.DAY, 1).date;
var then:Date = DateUtil.now.add(DateUtil.HOURS, 2).datetime;
var day:Number = DateUtil.now.fetch(DateUtil.Day);

Here is the custom class. Feel free to use it just credit me. If you have any problems let me know.
DateUtil.as
package
{
/**
* DateUtil class for simple date manipulation.
*
* You can do simple time alterations with this calss. Here are some samples.
*
* Subtract a day:
* DateUtil.now.subtract(DateUtil.DATE, 1).date;
*
* Add a month:
* DateUtil.now.add(DateUtil.MONTH, 1).date;
*
* Go to Monday:
* DateUtil.now.assign(DateUtil.DAY, 1).date
*
* @author Jeremy Pyne jeremy.pyne@gmail.com
*/
public class DateUtil
{
/**
* Date storage object.
*/
private var _date:Date;

public static const MILLISECONDS:String = "milliseconds";
public static const SECONDS:String = "seconds";
public static const MINUTES:String = "minutes";
public static const HOURS:String = "hours";
public static const DAY:String = "day";
public static const DATE:String = "date";
public static const MONTH:String = "month";
public static const YEAR:String = "fullYear";

/**
* Create a new DateUtil class.
*
* @param date
*/
public function DateUtil(date:Date)
{
_date = date;
}

/**
* Add some value to a variable.
*
* @param variable
* @param value
* @return
*/
public function add(variable:String, value:Number):DateUtil
{
if(variable == DateUtil.DAY)
variable = DateUtil.DATE;

_date[variable] += value;
return this;
}

/**
* Subtract some value to a variable.
*
* @param variable
* @param value
* @return
*/
public function subtract(variable:String, value:Number):DateUtil
{
if(variable == DateUtil.DAY)
variable = DateUtil.DATE;

_date[variable] -= value;
return this;
}

/**
* Set some value to a variable.
*
* @param variable
* @param value
* @return
*/
public function assign(variable:String, value:Number):DateUtil
{
if(variable == DateUtil.DAY)
{
variable = DateUtil.DATE;
value = _date.date + value - _date.day;
}

_date[variable] = value;
return this;
}

/**
* Get a variable.
*
* @param variable
* @return
*/
public function fetch(variable:String):Number
{
return _date[variable];
}

/**
* Get only the current date.
*
* @return
*/
public function get date():Date
{
var date:Date = new Date(_date.time);
date.hours = date.minutes = date.seconds = date.milliseconds = 0;
return date;
}

/**
* Get only the current time.
*
* @return
*/
public function get time():Date
{
var date:Date = new Date(_date.time);
date.fullYear = 1969;
date.month = date.date = 0;
return date;
}

/**
* Get the full date and time.
*
* @return
*/
public function get datetime():Date
{
var date:Date = new Date(_date.time);
return date;
}

/**
* Create a new DateUtil for the current data and time.
*
* @return
*/
public static function get now():DateUtil
{
return new DateUtil(new Date);
}

/**
* Create a new DateUtil for the passed in date. Updates will not effect the original date.
*
* @return
*/
public static function clone(date:Date):DateUtil
{
return new DateUtil(new Date(date.time));
}

/**
* Create a new DateUtil for the passed in date. Updates will alter the original date.
*
* @return
*/
public static function create(date:Date):DateUtil
{
return new DateUtil(date);
}
}
}