diff --git a/css/dashboard.css b/css/dashboard.css
new file mode 100644
index 0000000..d551090
--- /dev/null
+++ b/css/dashboard.css
@@ -0,0 +1,207 @@
+/**
+* @file
+*    CSS for the jQuery.dashboard() plugin.
+*
+* Released under the GNU General Public License.  See LICENSE.txt.
+*/
+
+#crm-container .column {
+    float: left;
+    margin: 0;
+    /* padding-bottom and min-height make sure that there is always a sizable drop zone. */
+    min-height: 400px;
+    height: 400px;
+    height: auto !important;
+    padding: 0 0 250px;
+    list-style-type: none;
+}
+
+#crm-container .column-0 {
+    width: 40%;
+}
+
+#crm-container .column-1 {
+    width: 60%;
+}
+
+/* The placeholder that indicates where a dragged widget will land if dropped now. */
+#crm-container .placeholder {
+    margin: 5px;
+    border: 3px dashed pink;
+}
+
+/* Spacing between widgets. */
+#crm-container li.widget, #crm-container li.empty-placeholder { margin: 6px 3px; }
+#crm-container li.widget { 
+    padding: 0px;
+}
+
+/* Spacing inside widgets. */
+#crm-container .widget-wrapper { 
+    padding: 0px;
+    overflow-x:auto;
+    margin-right: .25em    
+}
+
+/* wodget header / title */
+#crm-container .widget-header {
+	background:#CDE8FE none repeat scroll 0 0;
+	color:#000;
+	cursor:move;
+	display:inline;
+	font-size:1.2em;
+	margin:0;
+	}
+
+/* widget content / body*/
+#crm-container .widget-content {
+    background-color: #ffffff;
+    padding:0.5em;
+}
+
+/* Standards-browsers do this anyway because all inner block elements are floated.  IE doesn't because it's crap. */
+#crm-container .widget-controls { 
+	background-color:#CDE8FE;
+	display:block;
+	padding:5px 0px;
+	}
+
+#crm-container .widget-icon, #crm-container .full-screen-close-icon img {
+    display: block;
+    float: right;
+    margin-left: 2px;
+    margin-top: 2px;
+    cursor: pointer;
+}
+
+#full-screen-header {
+    display: block;
+    padding: .2em .4em;
+    background: #F0F0E8;
+    /* Although this is an <a> link, it doesn't have an href="" attribute. */
+    cursor: pointer;
+}
+
+/* Make the throbber in-yer-face. */
+#crm-container .throbber {
+    text-align: right;    
+    background:url("images/throbber.gif") no-repeat scroll 0 0 transparent;
+    height:20px;
+    width:20px;
+}
+
+#crm-container p.loadtext {
+    margin:1.6em 0 0 26px;
+}
+
+#crm-container .minimize-icon {
+    background-position:-63px -17px;
+    background-image:url('../jquery-ui-1.8.16/css/smoothness/images/ui-icons_222222_256x240.png');
+    height:16px;
+    width:16px;
+    padding-right:2px;
+    float: left;
+}
+
+#crm-container .maximize-icon {
+    background-position:-31px -18px;
+    background-image:url('../jquery-ui-1.8.16/css/smoothness/images/ui-icons_222222_256x240.png');
+    height:16px;
+    width:16px;
+    padding-right:2px;
+    float: left;
+}
+
+#crm-container .close-icon {
+    background-position:-81px -129px;
+    background-image:url('../jquery-ui-1.8.16/css/smoothness/images/ui-icons_222222_256x240.png');
+    height:16px;
+    width:16px;
+    padding-right:2px;
+}
+
+#crm-container .fullscreen-icon {
+    background-position:-159px -113px;
+    background-image:url('../jquery-ui-1.8.16/css/smoothness/images/ui-icons_222222_256x240.png');
+    height:16px;
+    width:16px;
+}
+
+/* CSS for Dashlets */
+
+#crm-container #available-dashlets { 
+    width: 98%; 
+    border: 2px dashed pink; 
+    background-color: #999999; 
+    min-height:40px;
+}
+
+#crm-container .dash-column { 
+    border: 2px solid #696969;
+    min-height: 100px; 
+    background-color: #EEEEEE
+}
+
+#crm-container .dashlets-header {
+    font-weight: bold;
+} 
+
+#crm-container #dashlets-header-col-0 { 
+    float: left; 
+    width: 40%; 
+}
+
+#crm-container #dashlets-header-col-1 { 
+    margin-left: 42%;
+    width: 56%; 
+}
+
+#crm-container #existing-dashlets-col-0 { 
+    float: left; 
+    width: 40%; 
+}
+
+#crm-container #existing-dashlets-col-1 { 
+    margin-left: 42%;
+    width: 56%; 
+}
+
+#crm-container .portlet { 
+    margin: .5em;
+    width: 75%;
+    display: inline-block; 
+}
+
+#crm-container .portlet-header { 
+    margin: 0.3em; 
+    padding: 0.5em; 
+    cursor: pointer;
+}
+
+#crm-container #available-dashlets .portlet {
+    width: auto;
+} 
+
+#crm-container .portlet-header .ui-icon { 
+    float: right; 
+}
+
+#crm-container .portlet-content { 
+    padding: 0.4em; 
+}
+
+#crm-container .ui-sortable-placeholder { 
+    border: 1px dotted black; 
+    visibility: visible !important; 
+    height: 50px !important; 
+}
+
+#crm-container .ui-sortable-placeholder * { 
+    visibility: hidden; 
+}
+
+#crm-container .delete-dashlet {
+    display: block;
+    float: right;
+    cursor: pointer;
+}
diff --git a/css/images/222222_11x11_icon_resize_se.gif b/css/images/222222_11x11_icon_resize_se.gif
new file mode 100644
index 0000000000000000000000000000000000000000..251dc1628e0fe7820d7ae0d494a2e4708d6cfada
GIT binary patch
literal 61
zcmZ?wbhEHb<YwSzXkcJaQBq<2_gC>J3nK#qBZCeD5P)PDm_&N|8&+4pWhyB+b-dux
Np18-A3j!G!tO2bd5heft

literal 0
HcmV?d00001

diff --git a/css/images/454545_11x11_icon_close.gif b/css/images/454545_11x11_icon_close.gif
new file mode 100644
index 0000000000000000000000000000000000000000..390a75978faa9fc8a3e9e137f2c86daea109fedb
GIT binary patch
literal 62
zcmZ?wbhEHb<YwSzXkcJ)adTn(_gC>J3nK#qBZCeD5P)PDm_&Q}*%##OW-8fi*8O3w
Obn!onNi$Sf8LR=hE)X;T

literal 0
HcmV?d00001

diff --git a/css/images/888888_11x11_icon_close.gif b/css/images/888888_11x11_icon_close.gif
new file mode 100644
index 0000000000000000000000000000000000000000..326d015330520dab34a3bf6911b2dcb14b2798e4
GIT binary patch
literal 62
zcmZ?wbhEHb<YwSzXkcLI>FQzp_gC>J3nK#qBZCeD5P)PDm_&Q}*%##OW-8fi*8O3w
Obn!onNi$Sf8LR=|?hyz8

literal 0
HcmV?d00001

diff --git a/css/images/arrow.png b/css/images/arrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c3166ffd1a547cf9e484493f7b1a6bb0a1960c2
GIT binary patch
literal 183
zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>3?#4ne^UZdEa{HEjtmUzPnffIy#(?lOI#yL
zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+JH<5@CkAK|NsBYnKM6p_~5cQ^#xFn
zu_VYZn8D%MjWi%f-P6S}q~camLP84z8=C?XvztLT=Q$=rM;$c*2Vv(x_Oyd%nK=v?
YIC2>D6MnFI1GO-Cy85}Sb4q9e0Nq41fB*mh

literal 0
HcmV?d00001

diff --git a/css/images/bg.gif b/css/images/bg.gif
new file mode 100644
index 0000000000000000000000000000000000000000..9ab78a2ec788d6dfbbcd6212a4d3b1d9917d55e7
GIT binary patch
literal 854
zcmZ?wbhEHbWMZ&jXlGz}_Uzg7=g(ifc=7V(%U7>ny?*`r&6_uG-@bkK?%n(M?>~I_
z@c;jRhEXsY0>d-}6o0Y+efFP02jo9co?zfGW)Rn5a1d~4Xl7*5h_P7kkby}+S!_nc
jfrg{q>_&192R0@*^>d1J$arpAa&odp@G2Wg0S0RTW*Ik7

literal 0
HcmV?d00001

diff --git a/css/images/btn-sprite.gif b/css/images/btn-sprite.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e827ba35728adaadfea417b20b4727a859ae770a
GIT binary patch
literal 955
zcmZ?wbhEHbWM;5u_|Cw<U{k{IpTXh3Ls<8br_&jJY%=I*Yg@Z^ZO^6~`!2uyd!zHh
zg$sA?+_`t}-nsklFFyQm<?+YsPd?pv`sw!b&-Y$_x&P|x{}1OMJb3Wv(WA$YA3uHi
z^!fAWkDtGO@#4kHmoHzxe*Nano40S@zI*rX;p?xD-+p`g?)$U%-(P(A@$cW?|NsB~
z`}hCf|NsAC6p#sY(I^-V0lI~N;!hSv1_nk39gvNnJi)**gMpdF!XV%%qqr2`0)aw<
zBaC7Se0zQ<Tx>qVCuHv7^FyiGrC;B`V~@f{N6$_kz9kU`8(JNhS(qhw794D46x85p
z*}&P<+Q_fZQ^9dd@kQGLsiw35DG}!j+>X*Nyr&diG>Nc9NpNf^eZbhMAD1Day(p(+
sv%;mih}R2q+qX+U>bp8&;ksC!3jG(k2e#`s@8@P}=gm6c;J{!F06~?tlmGw#

literal 0
HcmV?d00001

diff --git a/css/images/dataTable/back_disabled.gif b/css/images/dataTable/back_disabled.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e4bdae7c7fe7330cb210c213a829d8a7dacd4386
GIT binary patch
literal 64
zcmZ?wbhEHb<Yr)JSjfmQbLPzd|Noyka|TE%{$ybkVPIy^VE_V<JOh*99L8l_ld4;q
Q7oTL;JvVt?t_XuQ0N%zG$p8QV

literal 0
HcmV?d00001

diff --git a/css/images/dataTable/back_enabled.gif b/css/images/dataTable/back_enabled.gif
new file mode 100644
index 0000000000000000000000000000000000000000..540d1b59e8bced7dc15fd059ea3bc69b54cb3eb5
GIT binary patch
literal 62
zcmZ?wbhEHb<Yr)JSjfmQbLPzd|Noyka|TE%{$ybkVPIy^VE_V<JOdN|9L8l`JFnl%
O@Hm%IShkvh!5RR-wH5gQ

literal 0
HcmV?d00001

diff --git a/css/images/dataTable/forwad_disabled.gif b/css/images/dataTable/forwad_disabled.gif
new file mode 100644
index 0000000000000000000000000000000000000000..13a60597d9519d4b168b4fbf20c45175912238c7
GIT binary patch
literal 64
zcmZ?wbhEHb<Yr)JSjfmQbLPzd|Noyka|TE%{$ybkVPIy^VE_V<JOh*99R7lAg;TRC
Q3U_c`KRfGMi3o!=0OVs9m;e9(

literal 0
HcmV?d00001

diff --git a/css/images/dataTable/forwad_enabled.gif b/css/images/dataTable/forwad_enabled.gif
new file mode 100644
index 0000000000000000000000000000000000000000..4014fd2af42065c6019c49eed6ab486cf2e2f674
GIT binary patch
literal 62
zcmZ?wbhEHb<Yr)JSjfmQbLPzd|Noyka|TE%{$ybkVPIy^VE_V<JOdN|9R7mrm3L%h
O^bT63)-7gWum%9G0~9y_

literal 0
HcmV?d00001

diff --git a/css/images/dataTable/sort_asc.gif b/css/images/dataTable/sort_asc.gif
new file mode 100644
index 0000000000000000000000000000000000000000..eb4b729142fcabe09831c3e597e0bd774106f340
GIT binary patch
literal 63
zcmZ?wbhEHbWM|-JSjfmQbLPzd|Noyka|TE%{$ybkVPIy^VE_V<JOh)!oJ7th_5N$k
P3vNx@bZ^gRCI)K&)m;}a

literal 0
HcmV?d00001

diff --git a/css/images/dataTable/sort_both.gif b/css/images/dataTable/sort_both.gif
new file mode 100644
index 0000000000000000000000000000000000000000..7437f1d54c2302370b70f34e839a7ab2924e08e9
GIT binary patch
literal 67
zcmZ?wbhEHbWM|-Jn8?5|bLPzd|NoykbB2L|LGdRGs|W)lgAS0*0Fq~566s0gY*O#P
V*1X`>v~yd9*-mOCwuv%W0|2HM6kh-U

literal 0
HcmV?d00001

diff --git a/css/images/dataTable/sort_desc.gif b/css/images/dataTable/sort_desc.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2d7acbb7026e062cf658fccbf2c197c28056485f
GIT binary patch
literal 63
zcmZ?wbhEHbWM|-JSjfmQbLPzd|Noyka|TE%{$ybkVPIy^VE_V<JOh)!oc@)kx%lVL
P5aw}Md+^s6CI)K&&FB^L

literal 0
HcmV?d00001

diff --git a/css/images/ddn.png b/css/images/ddn.png
new file mode 100644
index 0000000000000000000000000000000000000000..2178f11e327167c25acfb35264b03dd87b9486b4
GIT binary patch
literal 111
zcmeAS@N?(olHy`uVBq!ia0vp^96-#%#0(_QEuNMPq&Ne7LR^6~5JW~sygPna1;}D7
z3GxeOaCmkj4agDkba4!+xRpFZ>evFQg9m0FIB<o5fqw#1h&RV~bC5nyS3j3^P6<r_
DHsu`r

literal 0
HcmV?d00001

diff --git a/css/images/dn.png b/css/images/dn.png
new file mode 100644
index 0000000000000000000000000000000000000000..a19083be7668600f73e056a625262c6f2bd2ff70
GIT binary patch
literal 220
zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3-ozC9e7dDajJoh?3y^w370~qErUQl>DSr
z1<%~X^wgl##FWaylc_d9MFs&rA+EKxwMUK|$<58(vSmwOU!RMM%lh@}Po6y4-`~G;
z=gyv<o@vvjZP>73!GZ->u3Y*5|9@k!)E}T;fs!D<U?3M6Fg#F>l?2MEdAc};RNP8R
zNPUp>F!gcbgY-wwQl39ffAIWy!js4B7yq1b;1Nh*5J+RxDZkxN0@T6a>FVdQ&MBb@
E04CC4Q~&?~

literal 0
HcmV?d00001

diff --git a/css/images/fhbg.gif b/css/images/fhbg.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8d459a304e0b224f8c28d6b7b585da7019d28cce
GIT binary patch
literal 836
zcmZ?wbhEHbWMYtDXlG!!aN)x1H}BrOegF2|hj;HkzW?y)!^h7bKYjW6^C!b77!85p
z9s-I#S%6;r&!7YHC@4=ba40eea>#gWNI1yM!7mYUVnf4WCKe8!85Rx=4Ga>@3=9GS
G4Auam1ttan

literal 0
HcmV?d00001

diff --git a/css/images/first.gif b/css/images/first.gif
new file mode 100644
index 0000000000000000000000000000000000000000..7623e7387a1db305267e63826400a23bf4298dee
GIT binary patch
literal 217
zcmV;~04D!ONk%w1VGsZi0K^RdBuJ1gQ=T(frax%6EN0j^Zrne0-7InLL~p=CZPZG2
z$VhtMPJ7}1|Nm2f)=`7uR)pzai{N04<Y<)TZ<g$BndWDl`*E7*exL4wpy!RN@QAMe
zmbCAozVM>G|Ek3AA^8LW000dDEC2ui01yBW000G6U?YxXX`bhlT_#&TUsXnO>2wnr
zdAZF=qr}DoEGaY*QNm*3Bufa)qah$1EShO)Kv{?Y){D0!pcqEr$C)fCkN^pHDF7jq
Tz$O60UgE#uD1m~5Bq0Dh8K_|4

literal 0
HcmV?d00001

diff --git a/css/images/hl.png b/css/images/hl.png
new file mode 100644
index 0000000000000000000000000000000000000000..6248e45dfe83355a94b9ccac57f557a760b003c3
GIT binary patch
literal 189
zcmeAS@N?(olHy`uVBq!ia0vp^j6f{F!3-oHI6iF!Qj#UE5hcO-X(i=}MX3yqDfvmM
z3ZA)%>8U}fi7AzZCsS>JiVOmLLR{~C`t$zB-|O%HTz&uN+1J0XzWsgi?eClKe?R>A
zd+q(7hoAr4`taw;=f8J8{`vUh@3-H7^W;8E0qRxuba4!+xW#gim61V#fkENTwg367
n=dZH*2BzLVveCu)7ZVQyM+8Hq(5Z@YpmGLJS3j3^P6<r_8--Qa

literal 0
HcmV?d00001

diff --git a/css/images/last.gif b/css/images/last.gif
new file mode 100644
index 0000000000000000000000000000000000000000..61483e998b141b8b137506dabf4338b045afa13d
GIT binary patch
literal 220
zcmV<203-iLNk%w1VGsZi0K^RdBuJ1gQ=T(frax%6EN0j^Zrne0-7InLL~p=CZPZG2
z$VhtMPJ7}1|Nm2f)=`7uR)pzai{N04<Y<)TZ<g$BndWDl`*E7*exL4wpy!RN@QAMe
zmbCAozVM>G|Ek3AA^8LW000dDEC2ui01yBW000G9U?YxXX`bh0UfE<;QCWSDIeGCo
zb&e@{o5V=M>6IcJi$&OoXcVT0#&RGC8V?M@w766Oq6KAOGCT-ik%4k86%uJtLDD9n
WO9V*3MBp>xC?arxfrW+*Apkq35Mp)!

literal 0
HcmV?d00001

diff --git a/css/images/line.gif b/css/images/line.gif
new file mode 100644
index 0000000000000000000000000000000000000000..c76a16e95997a487ee9cd1675ecdd99bd2f37c17
GIT binary patch
literal 817
zcmZ?wbhEHbWMbfDXlGzpvts7||Nj|A!DtAK$PiHc$pZBEe+C_p??HKjfrF2Mkww6x
PV8OvA4t_Qc4hCxg>zoX)

literal 0
HcmV?d00001

diff --git a/css/images/load.gif b/css/images/load.gif
new file mode 100644
index 0000000000000000000000000000000000000000..68f01d04813c1f622810864a3aaa04b4a17ac8f9
GIT binary patch
literal 1737
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
O1il%9bk82&KlvLjD^%40

literal 0
HcmV?d00001

diff --git a/css/images/load.png b/css/images/load.png
new file mode 100644
index 0000000000000000000000000000000000000000..d65defbcb54ab2e3ffb4d12201ca3f6a56182df4
GIT binary patch
literal 827
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!WQl7;NpOBzNqJ&XDuZK6ep0G}
zXKrG8YEWuoN@d~6R2v2c#)bf&5Z9?^djJ0WyX4C3S?4G9pKSM^>M`L|hyMi6q(#xs
z-aczRQa52w_nh-nW}ctWcdRXBs^88l+t%M$HGALmzLTxp$C`7tq;wu@3Y#8KHm7*Y
zxlNNc_Xp4Oc4)H<ob8#nAuVcV$o@NfLgx5ntVo!!t*7^R+x$z@8xNIFJk>S-;<R0l
zwuCSCYdv0_yd<{!cx&qNINxcm-G`gAr)Nw%+h^BozUa_==X%?+&G|FV^)I|Mqi%oc
zl+!)gYmzryS+iizyd9Uf`gOY}&y8z2UcL0jy!E$NMlT2noaDV||AO4PnNiCE4&C3E
zGc&XKNM-lomKEohq^ykf>2=?GbJv^;Q@V~c-}`W9+r=%rukV<2sxxC+%D#s?`%iYn
ztqz%Rva@qdd-LJSoVBUD?``*O^m_OG?e1&aYxWi|I63>z-#;5quXpLTop!cAZ*_Xp
zp{mulmYlnDcKZ1Vjr*#WU!T{0q#<Kf;*?zz%&U!z%k&nUp53^#&b`~={g1a(Hcje0
z+%WIrRI54@uWr}+gO&gP|6j6up)WAP8cKrvf`QzD1Pm6e-swQM9QAZ@45_%4w4$9+
z+Rfl7gZPOjZJZ%4F5OigueA#`oLQQRlMgeku3Q_z^<$|6Q>WV@pJ!)ya+G39{~0PY
zR(NyEiQ3MbCG_o<(FU>kT5SqHl@zocnUYq_SJK`s&=ETG)k=X0DaQr01e9hTTfr&x
zsWee{d;5;VCj~v06nt=<wX>OZ)7Gntwxq1(jW}}Tt^?D#dp~_vIn3U?qhybTBU95!
zDI3XGyDAIUwOI=6pK?lcnY8%qw%bLAY>vNW_^#~IaFIoiK_G$Qo@|7?`$KD9pobVd
MUHx3vIVCg!0CQN?vH$=8

literal 0
HcmV?d00001

diff --git a/css/images/magnifier.png b/css/images/magnifier.png
new file mode 100644
index 0000000000000000000000000000000000000000..cf3d97f75e9cde9c143980d89272fe61fc2d64ee
GIT binary patch
literal 615
zcmV-t0+{`YP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz`AI}UR5;6}
zlD}({VGzf^?~f!#tf`u$5Q9JHQ1uTGDng(|aLClHAP#~!RCnFF2#yY>gNuvOO$0ks
zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+
z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW
zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B
zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8
z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN
z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@!
zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR
zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP
zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0
B3<CfF

literal 0
HcmV?d00001

diff --git a/css/images/myshadow.png b/css/images/myshadow.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb6c71b572bd10695ff784d25a11112e09ea758f
GIT binary patch
literal 5741
zcmeHJc{r49+rLEieGMrhTlTH&%viI9(F`hEj3sNzuCWtYQr1RPwy|W%Xe?z5WeXw3
zl0AesOO)*${o(i=&-;DH@qEYg{?{M(egCfOyyu+X?>d+JjJaZ{O+(2+2>_tc)zL5k
zfS4PA69{rr!cNBBP!Ga`!b8XWHULyihZhmZcybB=N+TyI^vV@ScW?LGj_w}Zx=<*$
zhnKs9(=B@dd`HlxNSNsYtMb<P21G9!^+?a%goT{j1QLs4PU08lp`gDU&66|CV%*4}
zsY$}wo*hjbACHP-F&3e|Px+Z_hUZy)*z@T7A9f}Kp1TazZOwJ<R}HDm7q6o$d&nBd
zsZ(^Mjb%~Pg^)80U&7j2S|?}3)IuovJODj;<xQU3d%Q$oJ6K8S!np>rH-N~upNbqb
z7-Tj`oQ^u6TZE)vBSM7}HFzgo(j!MP12zA6NFGqrB0{0l_)S4J39#<4vz-Q4M1i%y
zy}5pXLT{%B5CO|aJS;@nNr0QlK3W4fDuUvktM@elLIN;5=(Wg$&!Rv=7w(_~%Bn#_
zH$7DaAg2Zr#?cW%fFuA|xAXG)g79>}thEML`hKC5eqNYxQt1^+I6isJ5KD4VPck@M
z?2KTyF53lWIhzwU8ERrpe(CH|Au@Dd);j^nPGTXbUElNVrY-I6mW#*IT8S>cB|SK2
zYdgI^+f(GB2Ee3uaPPi|cqIo)krd^+|Lpt<iQ^5b>^obr4i$82RUms|1itQZcs80j
ziMZk6*_oNoms=o~HXq<Y`^YA%2Ke5spTSDITbp0rO!0+CT@TSF+5XbhxuKtXy5}K9
zxb5g%g7$7T)xj=T|G5@jTO2}|dD@uKGhQPjZe1$(Tnyx)(7W?}2RElCM0SW(CS*aG
zvkFl_l4!woJK`UD0`v&)hN&$8)@t1wKM9f(qa4E~dwdR7Rrj>B&jXZ$?n6%iZfNj{
z!9G-~wvhv%ksTsjrp~$1cuJ&!^mOCN(MGCWYq@X;UrQ5&9zuB&b=u2Xs5lfN7}-_E
zC2TGJNsUXU!Pqe}*@ITB$?P?)vIpHS8}h71!MYF%67_a+CL5lSXd>JD2;O*dw)BW?
zo<<!K##lb?_tDJ8=k$_fbtFyh!??|KxYm@tPRK`H)2c~QYzI=IyODL;l8-_vjFmr8
zS3z+F7dgVUo>6_g=`ER&z?z-;(d!k5a-3Yw{Kq0kx{(AGXq!aC*HdKDEw?1ZTALx|
zmqaL%?K;ZO6;l_ra+F;yCa#UywCgGNAz=*BY@m=M@quvLoKe?7=pzh2>lARG=96I<
zp}0sv9B$ksa4xw(?-hRy!_Ssewo;N&2ptjLLB<3oar%I0Xv;-D#u%uS050)_ZutvX
zA7h_ZpPLEqf=FSej1C8H^81mSE)`_*k3_gHv`5UKXPRfEXE<kg)+{N}wrZX)woRu5
zO+GkZW}l^>m6;{APm@wNd!Au3R*(Z@kWH|Fl;mJdUK&RgFv;A>knaj()z!-_dG@%U
z!;#H#%jxA;?;3`&$7c*vKf?Pu7PP;nekI=Up{9vs32Tc+3bJROB0Gg-_c`@4O+Syl
zKbE}>CUH@6DQ!NTF5TS>A&eCcWJ&r!`S|i<{>Plc;;=$Y9%dXvWwvhC0ZS;nVH#Ab
zWhQMpT}bgMrN|6}zOrP>X^JdVEQrD&3*2F<FM`eYVo1%K^_z{LFO0Lxt)}Z*xvA{f
zD}6<HTxg0S?}W&<@RxTD2i_H~P21zDja9}J=Ey^(FH6HKP1YYdsFF>md2T=PHl&yr
z%uihpzCX^qDl{d;nJ1%<leE9)f=NkDK@{l}85e0U)ytJG=cDuLHEYdxLTJ_7E(=@}
z&_*V7pG41_4x5hUQ|7Z8C@XB1ieX%GIdf_?eBQd{ucT$Yxq-^kch4X9tDpaMI!JsE
zw&(gQe^Z+pL0U=8M~bKJKBe?d*3(^yOS}#L-57ekSq++N^VlYJGCT>_tMq(8>wy+s
zoKx$yX})RKR@c_&{G|Lm{CWn91}hmR2Dm(-JVkv){kE>ou4i3?>9U#1l0#DSne&-L
znG@C47QPlD*aGZpd)<l|i#+ocY@J(#g}iw#!p!1&rB(^L<osZ5No_tuKF%=NuqbN~
z*<AJ=`yN|Z&0aS9nG|1&b;SnRAZ^)@)$dB1SCZ?KgKB~v>?xDcL=DpxaanK$-+txA
ztZ-FfJfr)`ifYiijg$p(B%+7D=d<)QmZ?Li#R+Yi*{_XcL*j0Stw+bk=!JHOrK*T!
za<-1O*Uwz`-t@da>EE}B&aBLAS}wr9T#;LGT8SEc+9~r?D9c>>_Q&RS=ojPJURA26
zk0o3tii3@yMj^<md0ufl@g#W3P+4`Z4`$0|l}+V`ir3R=m1(7m<5J`1R-G3cs@tnO
zjGEoEtRg0$_jnI(9{3;hf_&6@A|!E4h<b?SiJfi#K%TepstS1f9~HyP>T%gPbr$u6
z(2~&E#u6UK_HT(LJX`Z=-AUa}v=LW=zYcyRvzfDrZ4Rauh=_`SN2<nqL?bwQB~xX6
zdPkwRCp3O(q-cz1A|;LFUy5Fq6;{EBIV+7^tdXfvZ54Ac?;Jp&5xTeZPRol4m&&=_
zdNZ-RydE`$o}!u#U`rEkzwY6e)7`WYk<VHv#wenYqNX2hc<Ir<)aiu_SmVyGG5MU2
zr}S&$YC@h9@0II~{tUe)e8zB1N7%ss;kTqZSwFsa(zznDxNw2O#5#Q%{jp4iEY^&>
z+^$l}3iXm<50er-As8NmWGr$KIp=V<qu4&8V*j06Y_<5A$7JtaDie!Onu}6mA$eb6
z7J@^<WEDtzxWn77m=E$PMOhcRmOCpy;1e%CS$%r2%-F;@`4F2MsRtM3Ld?|{JIhxn
zdHUfgr&&&3h`dT#tn~UmR@imGsAw6US{hu>?{DA|a-;D>Xk!(T%Mz6t<!nUI(&*6g
zbt8nh`9zh?uShhf3QNrWQf53`D$Bjkf}_5(oKfx{-lmT}dR17|g1gz{(C4r)H!hlG
zNt=R6olB*4qZuA~MKf{r)$-;KO}uz&mmt123=73Qf2;Jm&OZNrcbB3n>;-HC_6sJr
z(l=IRGqmm2yTTbz_e18}0N%20Yft6<8@w5jnZba=fc=i{PUV(rX$)pzJ7vf9dWoT{
z=W-iyAMqX;FD-Tm_i2EuPZ?&CFnD1}o3`!jn|<`Ek0^~CjoE`Ity8=n8h$eiTND#p
zXJ`~g?#2ZaPm=YYy~~B<5_g7VtYv6rnr7@++FI5ab*l6ZE{^-}YU9t+J&{o|_q)6f
zd+#)|5Za%NT<eKLKSl=yi~St6#y`Ub%-eNOIMcbW*3f%T_Guo(&MVd_c3)e2?Y&lk
zpJjr-hG)D~uyS4WUR2so?l)?yeNvlIio525Tk)^23UJt3;LN(%J+@Kr_nq}%a3D)w
zd8~fj-+k-d*5(B3Sg@DCUUx_Bu#dn>Nu5!h)rQ)#`Y?1XYCzR;fAsU}=so8KpX~MC
zQSB|#h_Ighq|F36=*fiigpNp>$fc}HD)RDEs$P2wyZO+bn?3wlZ2QmlJ4)GD#e=s8
zmWH}}(|U7K*HV+;x(4R%EjBE%zr?pTwVrwN-d9@k#>Um1<wl=TAFkEs6Ldy1f!y~5
z4;+7%Z<>D0-$`aqPCg(ZPFvr#lNQAj=1mf$x`8?X<?(?##>s>=x1ElO0RaBz0YHTT
z@biH1TmayX1OSWH04SsZz~=tY>YWy0K8@DZP>1=Be8YDcGOM3b>&16k!{Vsy$yN`%
zC|l1`ORH7K3Nf-OlsU;|CByi~3OlFpW3bIvB6@`Xc{$lam^c8@2>{6H{|7*6Tk;Z?
zgAxY)c^0i03lhRxWhs@5IW=cp?w+Iq0MU`ZXt#+~DB&022B5)04D)orEXoBzcY@^5
zXunxC0<U!zP$TfJdII+uxHC<e;{JOTSTGKE$2<T;1oz$wO?IXRhd=Q(JG|2qe4!34
ze+7pOew&T~Zh}j2xN{sF>iNBM3>;H<q*)C`aB*n*U%(-+-=<^WkRUkRIR^d-?ni2l
zaX(V?w?rJHd?ex+<^PU|e}MZ@NdAt9KLPkpiU0pgo<9Kq$J6Op=Q-*{zYq7QXpg|3
z3IIn9=da*T3;Iu=t)t{Q0{=i3{hjuC^f5S|_=nLt%J;v5KNawg4G@0<@PCeSyF+e8
zL3gaW1}gT3x}7qx1Lm8!ngX$+_3v&jDTFap`uq3#UuMf%;UaAiTlP%1qO$|3#^S4(
z2Yx32(Eg?!7+B<nsdWz4MeUZBww}EP&p)gV-`1e0<b+IGGLG6*l6GhmQ=;FlN^9HA
zEP_Xd_2|S}hO;})St<5EmsXrIIC3N(e7)qiCZa~D3A>c?IRTfS!~h%|5Q8@ljjRS=
Sj9ev5hCo--P@@!L9r|x3M^xqj

literal 0
HcmV?d00001

diff --git a/css/images/next.gif b/css/images/next.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2cd62ccbfbdb217bf29b803f2bec734d34437f53
GIT binary patch
literal 185
zcmV;q07m~uNk%w1VGsZi0K^dhBuJ1bO_nWFo-<jdIAE_oXty0^^)F=4FJ;p(V#q#o
z+azl8EOG8Ma_B^Fz)E$<PkYe+|Nm2f)>DMtRfXPIhu=?%_D_lZSBKzVjNxaT`)Hi`
zg|7dIuK%LF|E0hGA^8LW000pHEC2ui01yBW000FxU?h%YX`ZNvrKXgzB=o&-^k`pI
nI~Vs+u);;t@l03*hDD=*4Hk_<O<+jWA_2rT0@!-LwGaS17qe3C

literal 0
HcmV?d00001

diff --git a/css/images/pbar-ani.gif b/css/images/pbar-ani.gif
new file mode 100644
index 0000000000000000000000000000000000000000..0dfd45b885a2dd69a4c16febc5e886300cdb08e0
GIT binary patch
literal 304064
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
N;M(#v!DbQH{6AEl<e~ro

literal 0
HcmV?d00001

diff --git a/css/images/prev.gif b/css/images/prev.gif
new file mode 100644
index 0000000000000000000000000000000000000000..b440a29ad5b654d8f24509317ff3e6603d967064
GIT binary patch
literal 186
zcmV;r07d^tNk%w1VGsZi0K^{vUyR|UzyDQ*-cy9#EOG7~X7w**&xo%7EmNLPd(b3m
z@;G3xqP_o5iuQ%B|0GC|FJ;p(V#q{qz&>-^N_EIHS*BQr-&26rSBKy~Xt!va{WNmu
zXq@>+g!?E>mjD0%A^8LW0018VEC2ui01yBW000Fy;3tk`X`X0_r6q8&BM>C;X9XGs
o3xU%}tO4V|>11FNPJ^(h88(Va?Gq^=rXPq0`+0D`U{eSHJ0!qRy#N3J

literal 0
HcmV?d00001

diff --git a/css/images/throbber.gif b/css/images/throbber.gif
new file mode 100644
index 0000000000000000000000000000000000000000..7befe12521020d0eedbe5a518dc9829c9256162c
GIT binary patch
literal 1821
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
Gl=g27%x_fy

literal 0
HcmV?d00001

diff --git a/css/images/up.png b/css/images/up.png
new file mode 100644
index 0000000000000000000000000000000000000000..000c065635f189ba79e3566e1e9dbbdeec126dca
GIT binary patch
literal 224
zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3-ozC9e7dDajJoh?3y^w370~qErUQl>DSr
z1<%~X^wgl##FWaylc_d9MaBU>A+A@hT&b<Cy?F8BmMvSRPoM7K;Nb4=e)Z~A7Z;cH
z>(}4Bd2{~!`8hc`m6erlZf<AJocaI%KdVa{P&b2MNswPKkc$EsRM*Z-0!pfRx;TbZ
z+)5G<5J+H7Vq;|FbCVM|d+PMbGr9*396xmA{DGr<0tpOax{Q{y%M^S-=6SmMxvX<a
GXaWGW?paU(

literal 0
HcmV?d00001

diff --git a/css/images/uup.png b/css/images/uup.png
new file mode 100644
index 0000000000000000000000000000000000000000..660db638f243ac884e0b4e71a5170a183011b690
GIT binary patch
literal 130
zcmeAS@N?(olHy`uVBq!ia0vp^96-#%!3HEZpRM}<q$EpRBT9nv(@M${i&7aJQ}UBi
z6+Ckj(^G>|6H_V+Po~-c6{&c-IEGZ*N=k5GnmAGLzvB!>=?1R`!#1T9PafABDicK+
bStJ;q-j}gmEMvVDsGh;o)z4*}Q$iB}fe$3!

literal 0
HcmV?d00001

diff --git a/css/images/wbg.gif b/css/images/wbg.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8046089a9de7680673de4bc9dbf45ffedf72aa52
GIT binary patch
literal 868
zcmZ?wbhEHbWMt4`Y-eD&e*OB58#iv<x^?^Z?K^kw+`D)0!Gi~n9zA;e`0>-HPoF=3
z{^G@pmoHzwe*OB*n>TOYzJ2%Z-G>h!{{R2aFbYOPVCaW{;!hT!C;l_&fV=|A6AT=V
z46Gg<3=bQd*?7b>JQ^4dGBUGr2}md?Ff?>a7-!u%v2pS7eg)?)8P7|LTPAA+uZlUj
WY3b?d2Fa)9cy3<i!^p_UU=09lSWrU%

literal 0
HcmV?d00001

diff --git a/css/jquery.autocomplete.css b/css/jquery.autocomplete.css
new file mode 100644
index 0000000..e0fe2c2
--- /dev/null
+++ b/css/jquery.autocomplete.css
@@ -0,0 +1,48 @@
+.ac_results {
+	padding: 0px;
+	border: 1px solid black;
+	background-color: white;
+	overflow: hidden;
+	z-index: 99999;
+}
+
+.ac_results ul {
+	width: 100%;
+	list-style-position: outside;
+	list-style: none;
+	padding: 0;
+	margin: 0;
+}
+
+.ac_results li {
+	margin: 0px;
+	padding: 2px 5px;
+	cursor: default;
+	display: block;
+	/* 
+	if width will be 100% horizontal scrollbar will apear 
+	when scroll mode will be used
+	*/
+	/*width: 100%;*/
+	font: menu;
+	font-size: 12px;
+	/* 
+	it is very important, if line-height not setted or setted 
+	in relative units scroll will be broken in firefox
+	*/
+	line-height: 16px;
+	overflow: hidden;
+}
+
+.ac_loading {
+	background: white url('../../../i/loading.gif') right center no-repeat;
+}
+
+.ac_odd {
+	background-color: #eee;
+}
+
+.ac_over {
+	background-color: #0A246A;
+	color: white;
+}
diff --git a/css/menu.css b/css/menu.css
new file mode 100644
index 0000000..1fc4a0b
--- /dev/null
+++ b/css/menu.css
@@ -0,0 +1,340 @@
+html>body div.outerbox
+
+{
+
+	padding: 0 5px 5px 0;
+
+	z-index:999999;
+
+	font-size: 13px;
+
+	margin-top:2px;
+
+}
+
+html>body div.outerbox div.shadowbox1
+
+{
+
+	position: absolute;
+
+	right: 0;
+
+	bottom: 5px;
+
+	width: 5px;
+
+	height: 100%;
+
+	background: url(images/myshadow.png) no-repeat right top;
+
+}
+
+html>body div.outerbox div.shadowbox2
+
+{
+
+	position: absolute;
+
+	bottom: 0;
+
+	right: 5px;
+
+	height: 5px;
+
+	width: 100%;
+
+	background: url(images/myshadow.png) left bottom;
+
+}
+
+html>body div.outerbox div.shadowbox3
+
+{
+
+	position: absolute;
+
+	bottom: 0;
+
+	right: 0;
+
+	height: 5px;
+
+	width: 5px;
+
+	background: url(images/myshadow.png) no-repeat right bottom;
+
+}
+
+html>body .innerbox
+
+{
+
+    margin: 0;
+
+	z-index:999999;
+
+	margin-left:10px;
+
+}
+
+
+
+#root-menu-div ul {
+
+	border: 1px solid #000;
+
+}
+
+#root-menu-div li{
+
+	white-space:nowrap;
+
+	background-image: none;
+
+	text-align: left;
+
+}
+
+* html #root-menu-div li{
+
+	height: 1.5em; /* fixing ie6 problem */
+
+}
+
+#civicrm-menu,
+
+#root-menu-div ul {
+
+	background-color: #fff;
+
+	list-style: none;
+
+	margin: 0;
+
+	padding: 0;
+
+	background:#5D5D5D repeat-x;
+
+	color: white;
+
+	cursor: default;
+
+    font-size: 12px;
+
+    font-family: Tahoma, Verdana, Arial, Sans;
+
+}
+
+
+
+#civicrm-menu {
+
+    position:absolute;
+
+    top:0px;
+
+    left:0px;
+
+    background:#1B1B1B repeat-x;
+
+    width:100%;
+
+    height:23px;
+
+    z-index:999999;
+
+    overflow: hidden;
+
+}
+
+
+
+li.menu-separator.active{
+
+	background-color: transparent;
+
+}
+
+
+
+.menu-ul li.active {
+
+	background-color: #aaa;
+
+}
+
+
+
+#civicrm-menu .activetarget{
+
+	background-color: #aaa;
+
+}
+
+
+
+#civicrm-menu li a, #root-menu-div li a {
+
+	color:white;
+
+	cursor:pointer;
+
+	display:block;
+
+	font-weight:normal;
+
+	text-decoration:none;
+
+	}
+
+
+
+* html div.menu-item {
+
+	display: inline; /* fixes problem in ie6 */
+
+}
+
+
+
+li.menumain {
+
+	float: left;
+
+	padding: 3px 10px;
+
+	background-image: none;
+
+	border-right: 1px solid #5D5D5D;
+
+	margin:0px;	
+
+}
+
+
+
+#root-menu-div .menu-ul li {
+
+    margin: 0;
+
+    padding: 0 20px 0 2px;
+
+}
+
+
+
+div.menu-item {
+
+	padding: 1px 10px 1px 4px;
+
+	height: auto;
+
+}
+
+img.menu-item-arrow{
+
+	position: absolute;
+
+	right: 4px;
+
+	top: 8px;
+
+}
+
+li.menu-separator{
+
+	border-bottom: 1px solid #000;
+
+	font-size: 0; /* for ie */
+
+	height: 0;
+
+	line-height: 0; /* for ie */
+
+	margin: 2px 0;
+
+}
+
+li.red {
+
+	color: red;
+
+}
+
+li.blue {
+
+	color: blue;
+
+}
+
+
+
+/* syntaxhighlight stuff */
+
+.dp-highlighter
+
+{
+
+	font-family: "Consolas", "Courier New", Courier, mono, serif;
+
+	font-size: 12px;
+
+	background-color: #E7E5DC;
+
+	width: 99%;
+
+	overflow: auto;
+
+	margin: 18px 0 18px 0 !important;
+
+	padding-top: 1px; /* adds a little border on top when controls are hidden */
+
+}
+
+
+
+/* clear styles */
+
+.dp-highlighter ol,
+
+.dp-highlighter ol li,
+
+.dp-highlighter ol li span 
+
+{
+
+	margin: 0;
+
+	padding: 0;
+
+	border: none;
+
+}
+
+
+
+.dp-highlighter a,
+
+.dp-highlighter a:hover
+
+{
+
+	background: none;
+
+	border: none;
+
+	padding: 0;
+
+	margin: 0;
+
+}
+
+
+
+.dp-highlighter .bar
+
+{
+
+	padding-left: 45px;
+
+}
+
+
+
+.dp-highlighter.collapsed .bar,
+
+.dp-highlighter.nogutter .bar
+
+{
+
+	padding-left: 0px;
+
+}
+
+
+
+.dp-highlighter ol
+
+{
+
+	list-style: decimal; /* for ie */
+
+	background-color: #fff;
+
+	margin: 0px 0px 1px 45px !important; /* 1px bottom margin seems to fix occasional Firefox scrolling */
+
+	padding: 0px;
+
+	color: #5C5C5C;
+
+}
+
+
+
+.dp-highlighter.nogutter ol,
+
+.dp-highlighter.nogutter ol li
+
+{
+
+	list-style: none !important;
+
+	margin-left: 0px !important;
+
+}
+
+
+
+.dp-highlighter ol li,
+
+.dp-highlighter .columns div
+
+{
+
+	list-style: decimal-leading-zero; /* better look for others, override cascade from OL */
+
+	list-style-position: outside !important;
+
+	border-left: 3px solid #6CE26C;
+
+	background-color: #F8F8F8;
+
+	color: #5C5C5C;
+
+	padding: 0 3px 0 10px !important;
+
+	margin: 0 !important;
+
+	line-height: 14px;
+
+}
+
+
+
+.dp-highlighter.nogutter ol li,
+
+.dp-highlighter.nogutter .columns div
+
+{
+
+	border: 0;
+
+}
+
+
+
+.dp-highlighter .columns
+
+{
+
+	background-color: #F8F8F8;
+
+	color: gray;
+
+	overflow: hidden;
+
+	width: 100%;
+
+}
+
+
+
+.dp-highlighter .columns div
+
+{
+
+	padding-bottom: 5px;
+
+}
+
+
+
+.dp-highlighter ol li.alt
+
+{
+
+	background-color: #FFF;
+
+	color: inherit;
+
+}
+
+
+
+.dp-highlighter ol li span
+
+{
+
+	color: black;
+
+	background-color: inherit;
+
+}
+
+
+
+/* Adjust some properties when collapsed */
+
+
+
+.dp-highlighter.collapsed ol
+
+{
+
+	margin: 0px;
+
+}
+
+
+
+.dp-highlighter.collapsed ol li
+
+{
+
+	display: none;
+
+}
+
+
+
+/* Additional modifications when in print-view */
+
+
+
+.dp-highlighter.printing
+
+{
+
+	border: none;
+
+}
+
+
+
+.dp-highlighter.printing .tools
+
+{
+
+	display: none !important;
+
+}
+
+
+
+.dp-highlighter.printing li
+
+{
+
+	display: list-item !important;
+
+}
+
+
+
+/* Styles for the tools */
+
+
+
+.dp-highlighter .tools
+
+{
+
+	padding: 3px 8px 3px 10px;
+
+	font: 9px Verdana, Geneva, Arial, Helvetica, sans-serif;
+
+	color: silver;
+
+	background-color: #f8f8f8;
+
+	padding-bottom: 10px;
+
+	border-left: 3px solid #6CE26C;
+
+}
+
+
+
+.dp-highlighter.nogutter .tools
+
+{
+
+	border-left: 0;
+
+}
+
+
+
+.dp-highlighter.collapsed .tools
+
+{
+
+	border-bottom: 0;
+
+}
+
+
+
+.dp-highlighter .tools a
+
+{
+
+	font-size: 9px;
+
+	color: #a0a0a0;
+
+	background-color: inherit;
+
+	text-decoration: none;
+
+	margin-right: 10px;
+
+}
+
+
+
+.dp-highlighter .tools a:hover
+
+{
+
+	color: red;
+
+	background-color: inherit;
+
+	text-decoration: underline;
+
+}
+
+
+
+/* About dialog styles */
+
+
+
+.dp-about { background-color: #fff; color: #333; margin: 0px; padding: 0px; }
+
+.dp-about table { width: 100%; height: 100%; font-size: 11px; font-family: Tahoma, Verdana, Arial, sans-serif !important; }
+
+.dp-about td { padding: 10px; vertical-align: top; }
+
+.dp-about .copy { border-bottom: 1px solid #ACA899; height: 95%; }
+
+.dp-about .title { color: red; background-color: inherit; font-weight: bold; }
+
+.dp-about .para { margin: 0 0 4px 0; }
+
+.dp-about .footer { background-color: #ECEADB; color: #333; border-top: 1px solid #fff; text-align: right; }
+
+.dp-about .close { font-size: 11px; font-family: Tahoma, Verdana, Arial, sans-serif !important; background-color: #ECEADB; color: #333; width: 60px; height: 22px; }
+
+
+
+/* Language specific styles */
+
+
+
+.dp-highlighter .comment, .dp-highlighter .comments { color: #008200; background-color: inherit; }
+
+.dp-highlighter .string { color: blue; background-color: inherit; }
+
+.dp-highlighter .keyword { color: #069; font-weight: bold; background-color: inherit; }
+
+.dp-highlighter .preprocessor { color: gray; background-color: inherit; }
+
+
+
+/** Added by civicrm */
+
+
+
+#civicrm-menu li#crm-qsearch {
+
+    padding: 0 3px 2px 0;
+
+    height: 20px;
+
+    margin:0px 5px 0 5px;
+
+	}
+
+
+
+li#crm-qsearch input {
+
+    background-color:#ffffff;
+
+    border: 1px solid black;
+
+	}
+
+
+
+li#menu-logout {
+
+    float: right;
+
+    border-left: 1px solid #5D5D5D;
+
+	}

diff --git a/css/token-input-facebook.css b/css/token-input-facebook.css
new file mode 100644
index 0000000..4175450
--- /dev/null
+++ b/css/token-input-facebook.css
@@ -0,0 +1,118 @@
+/* Example tokeninput style #2: Facebook style */
+ul.token-input-list-facebook {
+    overflow: hidden; 
+    height: auto !important; 
+    height: 1%;
+    width: 400px;
+    border: 1px solid #999999;
+    cursor: text;
+    font-size: 12px;
+    font-family: Verdana;
+    min-height: 1px;
+    z-index: 1004;
+    margin: 0;
+    padding: 0;
+    background-color: #fff;
+    list-style-type: none;
+    list-style-image: none;
+    clear: left;
+}
+
+ul.token-input-list-facebook li input {
+    border: 0 !important;
+    width: 100px;
+    padding: 3px 8px;
+    background-color: white;
+    margin: 2px 0;
+}
+
+li.token-input-token-facebook {
+    background: none;
+    overflow: hidden; 
+    height: auto !important; 
+    height: 15px;
+    margin: 3px;
+    padding: 1px 3px;
+    background-color: #3E3E3E;
+    color: #F5F6F1;
+    cursor: default;
+    border: 1px solid #3E3E3E;
+    font-size: 11px;
+    float: left;
+    white-space: nowrap;
+}
+
+li.token-input-token-facebook p {
+    display: inline;
+    padding: 0;
+    margin: 0;
+}
+
+li.token-input-token-facebook span {
+    color: #2786C2;
+    margin-left: 5px;
+    cursor: pointer;
+}
+
+li.token-input-selected-token-facebook {
+    background-color: #999999;
+    border: 1px solid #ebece4;
+    color: #fff;
+}
+
+li.token-input-input-token-facebook {
+    float: left;
+    margin: 0;
+    padding: 0;
+}
+
+div.token-input-dropdown-facebook {
+    position: absolute;
+    width: 200px;
+    background-color: #fff;
+    overflow: hidden;
+    border-left: 1px solid #3e3e3e;
+    border-right: 1px solid #3e3e3e;
+    border-bottom: 1px solid #3e3e3e;
+    cursor: default;
+    font-size: 11px;
+    font-family: Verdana;
+    z-index: 1003;
+}
+
+div.token-input-dropdown-facebook p {
+    margin: 0;
+    padding: 5px;
+    color: #3e3e3e;
+}
+
+div.token-input-dropdown-facebook ul {
+    background: none;
+    margin: 0;
+    padding: 0;
+}
+
+div.token-input-dropdown-facebook ul li {
+    background-color: #fff;
+    padding: 3px;
+    margin: 0;
+    background:none;
+}
+
+div.token-input-dropdown-facebook ul li.token-input-dropdown-item-facebook {
+    background-color: #fff;
+}
+
+div.token-input-dropdown-facebook ul li.token-input-dropdown-item2-facebook {
+    background-color: #fff;
+}
+
+div.token-input-dropdown-facebook ul li em {
+    font-weight: bold;
+    font-style: none;
+}
+
+div.token-input-dropdown-facebook ul li.token-input-selected-dropdown-item-facebook {
+    background-color: #3E3E3E;
+    color: #fff;
+}

diff --git a/plugins/DataTables/Readme.txt b/plugins/DataTables/Readme.txt
new file mode 100644
index 0000000..b56ab6e
--- /dev/null
+++ b/plugins/DataTables/Readme.txt
@@ -0,0 +1,11 @@
+This DataTables plugin (v1.8.x) for jQuery was developed out of the desire to allow highly configurable access to HTML tables with advanced access features.
+
+For detailed installation, usage and API instructions, please refer to the DataTables web-pages: http://www.datatables.net
+
+Questions, feature requests and bug reports (etc) can all be asked on the DataTables forums: http://www.datatables.net/forums/
+
+The DataTables source can be found in the media/js/ directory of this archive.
+
+DataTables is released with dual licensing, using the GPL v2 (license-gpl2.txt) and an BSD style license (license-bsd.txt). Please see the corresponding license file for details of these licenses. You are free to use, modify and distribute this software, but all copyright information must remain.
+
+If you discover any bugs in DataTables, have any suggestions for improvements or even if you just like using it, please free to get in touch with me: www.datatables.net/contact
+

diff --git a/plugins/DataTables/license-bsd.txt b/plugins/DataTables/license-bsd.txt
new file mode 100644
index 0000000..cdb85aa
--- /dev/null
+++ b/plugins/DataTables/license-bsd.txt
@@ -0,0 +1,10 @@
+Copyright (c) 2008-2010, Allan Jardine
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the name of Allan Jardine nor SpryMedia UK may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+

diff --git a/plugins/DataTables/license-gpl2.txt b/plugins/DataTables/license-gpl2.txt
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/plugins/DataTables/license-gpl2.txt
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 2 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, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/DataTables/media/css/demo_table_jui.css b/plugins/DataTables/media/css/demo_table_jui.css
new file mode 100644
index 0000000..4a968f8
--- /dev/null
+++ b/plugins/DataTables/media/css/demo_table_jui.css
@@ -0,0 +1,538 @@
+/*
+ *  File:         demo_table_jui.css
+ *  CVS:          $Id$
+ *  Description:  CSS descriptions for DataTables demo pages
+ *  Author:       Allan Jardine
+ *  Created:      Tue May 12 06:47:22 BST 2009
+ *  Modified:     $Date$ by $Author$
+ *  Language:     CSS
+ *  Project:      DataTables
+ *
+ *  Copyright 2009 Allan Jardine. All Rights Reserved.
+ *
+ * ***************************************************************************
+ * DESCRIPTION
+ *
+ * The styles given here are suitable for the demos that are used with the standard DataTables
+ * distribution (see www.datatables.net). You will most likely wish to modify these styles to
+ * meet the layout requirements of your site.
+ *
+ * Common issues:
+ *   'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
+ *     no conflict between the two pagination types. If you want to use full_numbers pagination
+ *     ensure that you either have "example_alt_pagination" as a body class name, or better yet,
+ *     modify that selector.
+ *   Note that the path used for Images is relative. All images are by default located in
+ *     ../images/ - relative to this CSS file.
+ */
+
+
+/*
+ * jQuery UI specific styling
+ */
+
+.paging_two_button .ui-button {
+	float: left;
+	cursor: pointer;
+	* cursor: hand;
+}
+
+.paging_full_numbers .ui-button {
+	padding: 2px 6px;
+	margin: 0;
+	cursor: pointer;
+	* cursor: hand;
+}
+
+.ui-buttonset .ui-button {
+	margin-right: -0.1em !important;
+}
+
+.paging_full_numbers {
+	width: 350px !important;
+}
+
+.ui-toolbar {
+	padding: 5px;
+}
+
+.dataTables_paginate {
+	width: auto;
+}
+
+.dataTables_info {
+	padding-top: 3px;
+}
+
+table.display thead th {
+	padding: 3px 0px 3px 10px;
+	cursor: pointer;
+	* cursor: hand;
+}
+
+
+/*
+ * Sort arrow icon positioning
+ */
+table.display thead th div.DataTables_sort_wrapper {
+	position: relative;
+	padding-right: 20px;
+	padding-right: 20px;
+}
+
+table.display thead th div.DataTables_sort_wrapper span {
+	position: absolute;
+	top: 50%;
+	margin-top: -8px;
+	right: 0;
+}
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Everything below this line is the same as demo_table.css. This file is
+ * required for 'cleanliness' of the markup
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables features
+ */
+
+.dataTables_wrapper {
+	position: relative;
+	clear: both;
+}
+
+.dataTables_processing {
+	position: absolute;
+	top: 0px;
+	left: 50%;
+	width: 250px;
+	margin-left: -125px;
+	border: 1px solid #ddd;
+	text-align: center;
+	color: #999;
+	font-size: 11px;
+	padding: 2px 0;
+}
+
+.dataTables_length {
+	width: 40%;
+	float: left;
+}
+
+.dataTables_filter {
+	width: 50%;
+	float: right;
+	text-align: right;
+}
+
+.dataTables_info {
+	width: 50%;
+	float: left;
+}
+
+.dataTables_paginate {
+	float: right;
+	text-align: right;
+}
+
+/* Pagination nested */
+.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next {
+	height: 19px;
+	width: 19px;
+	margin-left: 3px;
+	float: left;
+}
+
+.paginate_disabled_previous {
+	background-image: url('../images/back_disabled.jpg');
+}
+
+.paginate_enabled_previous {
+	background-image: url('../images/back_enabled.jpg');
+}
+
+.paginate_disabled_next {
+	background-image: url('../images/forward_disabled.jpg');
+}
+
+.paginate_enabled_next {
+	background-image: url('../images/forward_enabled.jpg');
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables display
+ */
+table.display {
+	margin: 0 auto;
+	width: 100%;
+	clear: both;
+	border-collapse: collapse;
+}
+
+table.display tfoot th {
+	padding: 3px 0px 3px 10px;
+	font-weight: bold;
+	font-weight: normal;
+}
+
+table.display tr.heading2 td {
+	border-bottom: 1px solid #aaa;
+}
+
+table.display td {
+	padding: 3px 10px;
+}
+
+table.display td.center {
+	text-align: center;
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables sorting
+ */
+
+.sorting,
+th.sorting,
+th a.sorting,
+.sorting_asc,
+th.sorting_asc,
+th a.sorting_asc
+.sorting_desc,
+th.sorting_desc,
+th a.sorting_desc {
+     padding-left:16px !important;
+     background-repeat : no-repeat;
+}
+
+.sorting_asc,
+th.sorting_asc,
+th a.sorting_asc {
+      background-image: url('../images/sort_asc.png');
+      background-position:-2px 8px;
+}
+
+.sorting_desc,
+th.sorting_desc,
+th a.sorting_desc {
+      background-image: url('../images/sort_desc.png');
+      background-position:-2px 0px;
+}
+
+.sorting,
+th.sorting,
+th a.sorting {
+      background-image: url('../images/sort_both.png');
+      background-position:-2px 3px;
+}
+
+#crm-container a.sorting {
+      background-position:-2px -2px;
+}
+
+#crm-container a.sorting_asc {
+      background-position:-2px 4px;
+}
+
+#crm-container a.sorting_desc {
+      background-position:-2px -4px;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.display tr.odd.gradeA {
+	background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+	background-color: #eeffee;
+}
+
+
+
+
+table.display tr.odd.gradeA {
+	background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.odd.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.even.gradeC {
+	background-color: #eeeeff;
+}
+
+table.display tr.odd.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.even.gradeX {
+	background-color: #ffeeee;
+}
+
+table.display tr.odd.gradeU {
+	background-color: #ddd;
+}
+
+table.display tr.even.gradeU {
+	background-color: #eee;
+}
+
+
+tr.odd {
+	background-color: #E2E4FF;
+}
+
+tr.even {
+	background-color: white;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Misc
+ */
+.dataTables_scroll {
+	clear: both;
+}
+
+.top, .bottom {
+	padding: 15px;
+	background-color: #F5F5F5;
+	border: 1px solid #CCCCCC;
+}
+
+.top .dataTables_info {
+	float: none;
+}
+
+.clear {
+	clear: both;
+}
+
+.dataTables_empty {
+	text-align: center;
+}
+
+tfoot input {
+	margin: 0.5em 0;
+	width: 100%;
+	color: #444;
+}
+
+tfoot input.search_init {
+	color: #999;
+}
+
+td.group {
+	background-color: #d1cfd0;
+	border-bottom: 2px solid #A19B9E;
+	border-top: 2px solid #A19B9E;
+}
+
+td.details {
+	background-color: #d1cfd0;
+	border: 2px solid #A19B9E;
+}
+
+
+.example_alt_pagination div.dataTables_info {
+	width: 40%;
+}
+
+.paging_full_numbers span.paginate_button,
+ 	.paging_full_numbers span.paginate_active {
+	border: 1px solid #aaa;
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	padding: 2px 5px;
+	margin: 0 3px;
+	cursor: pointer;
+	*cursor: hand;
+}
+
+.paging_full_numbers span.paginate_button {
+	background-color: #ddd;
+}
+
+.paging_full_numbers span.paginate_button:hover {
+	background-color: #ccc;
+}
+
+.paging_full_numbers span.paginate_active {
+	background-color: #99B3FF;
+}
+
+table.display tr.even.row_selected td {
+	background-color: #B0BED9;
+}
+
+table.display tr.odd.row_selected td {
+	background-color: #9FAFD1;
+}
+
+
+/*
+ * Sorting classes for columns
+ */
+/* For the standard odd/even */
+tr.odd td.sorting_1 {
+	background-color: #D3D6FF;
+}
+
+tr.odd td.sorting_2 {
+	background-color: #DADCFF;
+}
+
+tr.odd td.sorting_3 {
+	background-color: #E0E2FF;
+}
+
+tr.even td.sorting_1 {
+	background-color: #EAEBFF;
+}
+
+tr.even td.sorting_2 {
+	background-color: #F2F3FF;
+}
+
+tr.even td.sorting_3 {
+	background-color: #F9F9FF;
+}
+
+
+/* For the Conditional-CSS grading rows */
+/*
+ 	Colour calculations (based off the main row colours)
+  Level 1:
+		dd > c4
+		ee > d5
+	Level 2:
+	  dd > d1
+	  ee > e2
+ */
+tr.odd.gradeA td.sorting_1 {
+	background-color: #c4ffc4;
+}
+
+tr.odd.gradeA td.sorting_2 {
+	background-color: #d1ffd1;
+}
+
+tr.odd.gradeA td.sorting_3 {
+	background-color: #d1ffd1;
+}
+
+tr.even.gradeA td.sorting_1 {
+	background-color: #d5ffd5;
+}
+
+tr.even.gradeA td.sorting_2 {
+	background-color: #e2ffe2;
+}
+
+tr.even.gradeA td.sorting_3 {
+	background-color: #e2ffe2;
+}
+
+tr.odd.gradeC td.sorting_1 {
+	background-color: #c4c4ff;
+}
+
+tr.odd.gradeC td.sorting_2 {
+	background-color: #d1d1ff;
+}
+
+tr.odd.gradeC td.sorting_3 {
+	background-color: #d1d1ff;
+}
+
+tr.even.gradeC td.sorting_1 {
+	background-color: #d5d5ff;
+}
+
+tr.even.gradeC td.sorting_2 {
+	background-color: #e2e2ff;
+}
+
+tr.even.gradeC td.sorting_3 {
+	background-color: #e2e2ff;
+}
+
+tr.odd.gradeX td.sorting_1 {
+	background-color: #ffc4c4;
+}
+
+tr.odd.gradeX td.sorting_2 {
+	background-color: #ffd1d1;
+}
+
+tr.odd.gradeX td.sorting_3 {
+	background-color: #ffd1d1;
+}
+
+tr.even.gradeX td.sorting_1 {
+	background-color: #ffd5d5;
+}
+
+tr.even.gradeX td.sorting_2 {
+	background-color: #ffe2e2;
+}
+
+tr.even.gradeX td.sorting_3 {
+	background-color: #ffe2e2;
+}
+
+tr.odd.gradeU td.sorting_1 {
+	background-color: #c4c4c4;
+}
+
+tr.odd.gradeU td.sorting_2 {
+	background-color: #d1d1d1;
+}
+
+tr.odd.gradeU td.sorting_3 {
+	background-color: #d1d1d1;
+}
+
+tr.even.gradeU td.sorting_1 {
+	background-color: #d5d5d5;
+}
+
+tr.even.gradeU td.sorting_2 {
+	background-color: #e2e2e2;
+}
+
+tr.even.gradeU td.sorting_3 {
+	background-color: #e2e2e2;
+}
+
+
+/*
+ * Row highlighting example
+ */
+.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
+	background-color: #E6FF99;
+}
+

diff --git a/plugins/DataTables/media/images/Sorting icons.psd b/plugins/DataTables/media/images/Sorting icons.psd
new file mode 100644
index 0000000000000000000000000000000000000000..53b2e06850767cb57c52b316f0b845b1a8e0ca0e
GIT binary patch
literal 27490
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
Y16@Zf7?Q8!JB-NJ(KYBs*Ff|C0Zg4MIsgCw

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/back_disabled.jpg b/plugins/DataTables/media/images/back_disabled.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..1e73a546e3609636f9cf4c543c2a4fe4050866c3
GIT binary patch
literal 612
zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5
z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tyUn
z+02YUSHb}sD?0}l&^EXLguy7lz`(-H!phDIlVN0HVHIR%V-ON%S2Pq6HFFG{Xryc+
zmRQ($@S#&sld*HEn>dFcBLmQ_$YwDLDjGT#PW*q1fd^<YlOVGogFVCRyQwLwf|@66
ze!$4fpt!)cj7zNLKSN?_SjIEf$Z0nVqZh4{&j?Wu4PU>^;oX+iLI)z|@;wX;U8Xr>
zT8Qgxn;R!Cottl}#(8Ri${a3d=J*$R5qBRbK9bRJe*4BYwq9(uIM4mLS9%TSY?*y6
zE==owYH)vf>dq6(Uzum+IyVJqc3N&dRPD57vW(=l(=(DZ-K_<Unt1pe8#qknGCi3V
zkh;vl;B4~gY1f{Kz2M5sJrOKuSm0ZhaMrL}#<_9H6t<5or9~0qKc;W}wfIQk>!LSx
zwPk@>bN@4B-OI9gCf>Wc_;CDAS%c@BJZ}k<t>BlbDz~@y-*&Y^_U+@};q`xNUthN}
On^Cj9W}Eu@|2F})$f79#

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/back_enabled.jpg b/plugins/DataTables/media/images/back_enabled.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..a6d764c79c7a2047b6bf65bf9d96fcafd5161e38
GIT binary patch
literal 807
zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5
z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63{a%*-grz$D1XEXer(2*U&h
z1}0{pBN2d|gO!bok%5r~RhW^Pm5qg+Lx6#Sm5Gs^od+h%$i&RTD#*spplB#0EW!~u
z(MZ`u)XXulu<_tSr=TWdXR%Z_aiE1jZ7eM8Fl&IpCkWK1$Y$s$#2&a&N#sM~M5iWU
z<^Q)Bc$gW1&SMs2uxGfw`}E06etb^r9k|jMc@Dlk(IlSD-Wwx*G{%4ThHhR%HnTsw
zbu3z!=BtbC=-In5f5nt;je{+vrKZdWckk$U`c^FEYC6}Jn=<Phg&RGxWowtu+j4vL
zuDrGP=1IPrb~kp*>lG)17QB>6z3khm7PHhg<-pEGSO0F>VA}N6`w53!8pr$>MbUW|
zME<s?dezw;U%dR;`Kd)lCauvOXRNpVXgK`4pX=Sp-@CZj9x15&y*j<b<JPA<K_*#K
z<@>sFn=(%w+^5v0yp7Lmb+~3PpI4(#G~bQ$y_;t)oo+U5`bnSHZcn){CH~m7Ys2rz
z2!R@xgGrCtcPz?eOPOzKr2P26p~(mTGjR1-c<x%~cJ0>3?psA>uUGsyt|R2Ts3~!B
zN71DGSrW%W*`w8#2{O$LX)%%8*<4Ycyr;tN(Hm<XHM0xJ?{xF0EskFCh5u=&<iulr
zYNyl=#<wqXD&+4u8oF^$)m7<lY@VgXk9azsKIKz?vp4^)yx{evPw)J-Dth+P<;w$q
z`@Q9}`(-Xi+_IiGtB&jWay`d}j~$NpR@hm5{cf*szhAZWsp!3Z`)}>8|M=_HtvR>P
P+Rcr(b5Xx$|NkZcT0jT?

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/favicon.ico b/plugins/DataTables/media/images/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..6eeaa2a0d393190ce748107222d9a026f992e4a7
GIT binary patch
literal 894
zcmZQzU<5(|0R|u`!H~hsz#zuJz@P!dKp_SNAO?xUfG{@$0|>*we<X0{?!DZ?LR&jK
zJ4*`(D+_yj`}mZUV<%4{DS<No{rlI{+GcBJTomZJrYz@lN9~2?%3V3hT|wRfwpN+B
zxv$^6LDrRDRAizmzo(`AZg1n=>Y}F;x;`B~@Mg`jC-vo5%k$%{OoKy1zkdA+(K2o3
zEDKGQQ~gcX+N!Tt<ln6-e!X((x9eB`|NHmj>Ej1|9e2tL(rheJva*5dKY#gZsIRxM
zB;!h7<JJ1on-%#_W>5Wi?9l5?>z>S-`t`=OAFp5C?(007k>qM(bnVuyja#;c*qL9N
z(Q%=@`f7d2?T&^wySIHjy#MKh?rZrO7i!Bt-@f(!@WDrwB`uD&)%Eqcd3o(gVV7ri
zp6ji@(BF7(+uE0x&)(R!?s#YIg_7JW1=-KHZv1@s(DS<L4gNk(j*fnQelrS_FV5;Z
zF}eA|vYC(19e;50(BU}~*0+>xD9<^am-%+f#!uU}z9=j>5*%b{WE2`20#tutPWS1#
zz4s39d~xOMwaqJzOl{rMU%#oo=xj&xyY1WF&71o?Hs-LOkA<OOT|+}_QrMM+6RvMw
z{^IiKhevmxo!@_KTI>D^4SRbV?yXz(e#efNwKXpT1J^s+yE;1`KXoe5&f?1A$<NLo
z|MBttt(|L+O=>tYso}un=1c3AzCL>B&D=RpqN3i1hL&lnclP!D`u*G0#bsJv!oAH)
z-rc|T`^Wdkrw?7(xb)_p%`Y#WdAn`Pvz+W#0Rbo6-Ha3zp1gPgjDkZ)k6Gz!@9D0(
zG^hL4?)Be4zWep#`{$>RUhUoaq@wgyfd9*oU{6tz+SXQhl3284sj;T&{L=I*bGxt4
z@4vfX%A<*$FN^a&MMa!<bG7B?%PuH@D~2#OZ`<zU;hyB_*c#=(G&yozSimf6i&AxU
ucVnZ@2@}v1L%4tb{$0CaV_ZU_m8GSrv9VuZ;G~(e-hB9gtQeLL$fW`Ph4SP8

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/forward_disabled.jpg b/plugins/DataTables/media/images/forward_disabled.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..28a9dc53fa232919299ac980dc9b61167c1868bd
GIT binary patch
literal 635
zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5
z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tySE
z1JEp>E8&2Nm4%I+g9~UMTnNHo6kuRrVP;|B;)Y2uGBL9Vva&G<iLfgg3UfFHPBdy#
zHW3vwODt@B=oDn^oa!bn$jHFNjA9g{prWDU#Q(P#c!1(eg3N*p_6)E5E7rbHvEH7V
z8#+gHc2rxYci=nCmS=BPhUq>lzGLeh=CM*_u2I6%$1L~6;-1yGa|?*Fva<AVW}f$w
zM|jiv%AS4aZbfpP)?S?%8?6+<6*?uQG@xF)s3@1Q<(tn>vCW+<XI6GdMB6yB9$3U&
z#>Av>{Flt>+*?d?iZ`9)^O?_H|D3t8;Og6lD<#WHQvW(mo1ETsD>A@mr8@sqg(?1R
z%d@qrmWLWmIn1#pQuo}{brVW*C8t%EDXbOt_DabwXgGW3;wDX1;~7hWrYL+ZX=qre
z&-y^2q0zm?-FJb#(AWJTHOJ-3`S`D|JEhzC{KvY_h0kZinXP-?w!g_L@p#JQO^!z$
mYCgVwS7#SryXs@?>&L&t^Z(SozHVt~@Z#>58}s)6zX<@qORf(9

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/forward_enabled.jpg b/plugins/DataTables/media/images/forward_enabled.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..598c075f13ab0e4bbe05a4eeea7cf777ba73cf03
GIT binary patch
literal 852
zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5
z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tywO
z0~0eN(3Nn&%Fe>Z!NthH$ciG&$iU3P%Em6hz`z1zu=B#yGcqx=2(q#<2(fb*3M-3<
zDh5t8G7&R#Oe}1C=oHjs?40T*F37+LbR`oj+(bq}CT0c}Rz*W0Hg?Cv#)(EB0yioN
zi~PUEzyov)lOVGogFVA5dn3uE%Z<*rn&ugY^FIG9<<=87L*?mOy^~u`Rq(Eg+UDw^
z@^Uh}zJ$t~P2YpRT#sm2p`oEt8zJ-EBRfY;>b?DxZo!24Z)Q(O&}!Mbt}27`dKa(x
zr)AHkZg5MjKa&4$syaupXWNXgp32dQs<o<XGr#5u9@=#E+?+!}FI~Pa)LWslJ}B36
z`Lb3YheKCCm%VOu;VpX6(0MkYSk}6t@aOKz+;_*D4{7|~^5)^QORgNty2^@M51##X
zueaWLX7+9e=RH;{oZr5)jkOooU8VU}TQps%p>_rj-?|M=Q9pN^hZg^mV7=UWs>}D5
z$40(ME7Q1T4h8p}ZJ8O!G-Xk84;P~#ci!&o`A5I3kG@gLm*VlJbn%kvSqIh?3Y?HQ
zAX0NL>XIkpf=MN83*!%}9-aN5)p7^py2Am>F9gW&pFSp#V0dxX#ZPW;UoD(gaI@8E
z(rNQz1DBIiM13!LtZ|85lHB@$wW~n0fhRz-$?R&m{@z;uI}N|35As;}zdXB<<FOu#
z{FTp-PcAia6ML~>I#+N1k=r*G-&mI78vJ_Ezs!yIV%J}l7d*c9>7Bn;xyLT84V&`$
z@9WMTAJ6DZyR)~=FS6P2e3MDv7lHB>{C&1H^7EJ9PIf=EdUwUYtGE9%$X9(m*ZWLf
M_rC2^{;K~s0TRp{r2qf`

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/sort_asc.png b/plugins/DataTables/media/images/sort_asc.png
new file mode 100644
index 0000000000000000000000000000000000000000..a56d0e21902ce620cd57f87fa58a6d3e804135cf
GIT binary patch
literal 263
zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL
zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>l2Ka=y{`vF&<?H{u_y6mi^6%Qs
z|I63>J9*~+_wWBdfBFCW_y37A{>@tOZ^^2EJ(K_a|Nno&o$%d2jr=7+e!)O45@5J;
zXU0OHl&hzUV@SoVE9Xw~GAQscA1s)^?0*rXqM}DeLv7?|BU8z@Q<ntt9<njnJ|j`Y
znD@ZlWoHa-oW7$XVs$3z>COGupVzP9Hek5&L+Gnl%BD>~0~tJB{an^LB{Ts5b0m7=

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/sort_asc_disabled.png b/plugins/DataTables/media/images/sort_asc_disabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..b7e621ef1c68da839ce077cec6e201a55c1ba168
GIT binary patch
literal 252
zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL
zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIy<HzUE
zpI^Ot_51hlzkmNec<|ufy?c)zKfZJ4&j0`aCog>d9;k)4B*-rq$b|z2+ZRf0KnW*L
z7srr_TUXBQ7Ge<Kak=PnB>V6G?@}f$7k6Jhw{qGm$-QDzD`qrDaZ3s$b2XF;fBBPU
nqjNDy&Nw~puHFA+-v&m8`?>-yUFVHK{$ucT^>bP0l+XkKhw^g=

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/sort_both.png b/plugins/DataTables/media/images/sort_both.png
new file mode 100644
index 0000000000000000000000000000000000000000..839ac4bb5b0b414834c950de9deafff6dd94ed2d
GIT binary patch
literal 282
zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL
zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIy<HzUE
zpI^Ot_51hlzkmNec<|ufy?c)zKfZJ4&j0`aCog>d9;k)4B*-rq$b|z2+ZRf0K#2rT
z7srr_TRjsz`4|*ASOSl%eg6Ob+(JtRwX|O@S}a^IES<M^Rar)~R7{gT(8t-3AepQp
zDW<l1NyH(qlCXQ0@AfT`xZ2rtb0gcPuhQK|E$f?fe^;k=XM|b({WbrT_5p@zx%{&^
T>QC<i+Qs1M>gTe~DWM4fR9b+X

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/sort_desc.png b/plugins/DataTables/media/images/sort_desc.png
new file mode 100644
index 0000000000000000000000000000000000000000..90b295159df995329b3a84340d32761f3d1dbade
GIT binary patch
literal 260
zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL
zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>l2Ka=y{`vF&<?H{u_y6mi^6%Qs
z|I63>J9*~+_wWBdfBFCW_y37A{>@tOZ^^2EJ(K_a|Nno&o$%d2jr=7+e!)O45@5J;
zXU0OHl#{26V@SoVqz8vtc$~v}dc~O{CLEF2anNavMpdMP)0v(X&o(k0opIq!LdvtJ
wj~up7@0`wiSoe($&y07EkGxK6U|?nlJSz0{?wJWUfu=Edy85}Sb4q9e0C9MGu>b%7

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/images/sort_desc_disabled.png b/plugins/DataTables/media/images/sort_desc_disabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..2409653dc94cd21a281a31c0e3819323b84704b7
GIT binary patch
literal 251
zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL
zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIy<HzUE
zpI^Ot_51hlzkmNec<|ufy?c)zKfZJ4&j0`aCog>d9;k)4B*-rq$b|z2+ZRf0KnX`r
z7srr_TS*TNvhX;E^>j)zMNBv%VR68zN24n8K+>5N&kUJbBQCy4c=qgBiu3mAQ)hD?
n)_oJhGvi(If!Apb49pB3GXz|UGFKJ=4P)?h^>bP0l+XkK*57ji

literal 0
HcmV?d00001

diff --git a/plugins/DataTables/media/js/jquery.dataTables.js b/plugins/DataTables/media/js/jquery.dataTables.js
new file mode 100644
index 0000000..a295d17
--- /dev/null
+++ b/plugins/DataTables/media/js/jquery.dataTables.js
@@ -0,0 +1,7440 @@
+/*
+ * File:        jquery.dataTables.js
+ * Version:     1.8.2
+ * Description: Paginate, search and sort HTML tables
+ * Author:      Allan Jardine (www.sprymedia.co.uk)
+ * Created:     28/3/2008
+ * Language:    Javascript
+ * License:     GPL v2 or BSD 3 point style
+ * Project:     Mtaala
+ * Contact:     allan.jardine@sprymedia.co.uk
+ * 
+ * Copyright 2008-2011 Allan Jardine, all rights reserved.
+ *
+ * This source file is free software, under either the GPL v2 license or a
+ * BSD style license, as supplied with this software.
+ * 
+ * This source file 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 license files for details.
+ * 
+ * For details please refer to: http://www.datatables.net
+ */
+
+/*
+ * When considering jsLint, we need to allow eval() as it it is used for reading cookies
+ */
+/*jslint evil: true, undef: true, browser: true */
+/*globals $, jQuery,_fnExternApiFunc,_fnInitialise,_fnInitComplete,_fnLanguageProcess,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnCreateTr,_fnGatherData,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnServerParams,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAdjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnArrayCmp,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn*/
+
+(function($, window, document) {
+	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+	 * Section - DataTables variables
+	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+	
+	/*
+	 * Variable: dataTableSettings
+	 * Purpose:  Store the settings for each dataTables instance
+	 * Scope:    jQuery.fn
+	 */
+	$.fn.dataTableSettings = [];
+	var _aoSettings = $.fn.dataTableSettings; /* Short reference for fast internal lookup */
+	
+	/*
+	 * Variable: dataTableExt
+	 * Purpose:  Container for customisable parts of DataTables
+	 * Scope:    jQuery.fn
+	 */
+	$.fn.dataTableExt = {};
+	var _oExt = $.fn.dataTableExt;
+	
+	
+	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+	 * Section - DataTables extensible objects
+	 * 
+	 * The _oExt object is used to provide an area where user defined plugins can be 
+	 * added to DataTables. The following properties of the object are used:
+	 *   oApi - Plug-in API functions
+	 *   aTypes - Auto-detection of types
+	 *   oSort - Sorting functions used by DataTables (based on the type)
+	 *   oPagination - Pagination functions for different input styles
+	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+	
+	/*
+	 * Variable: sVersion
+	 * Purpose:  Version string for plug-ins to check compatibility
+	 * Scope:    jQuery.fn.dataTableExt
+	 * Notes:    Allowed format is a.b.c.d.e where:
+	 *   a:int, b:int, c:int, d:string(dev|beta), e:int. d and e are optional
+	 */
+	_oExt.sVersion = "1.8.2";
+	
+	/*
+	 * Variable: sErrMode
+	 * Purpose:  How should DataTables report an error. Can take the value 'alert' or 'throw'
+	 * Scope:    jQuery.fn.dataTableExt
+	 */
+	_oExt.sErrMode = "alert";
+	
+	/*
+	 * Variable: iApiIndex
+	 * Purpose:  Index for what 'this' index API functions should use
+	 * Scope:    jQuery.fn.dataTableExt
+	 */
+	_oExt.iApiIndex = 0;
+	
+	/*
+	 * Variable: oApi
+	 * Purpose:  Container for plugin API functions
+	 * Scope:    jQuery.fn.dataTableExt
+	 */
+	_oExt.oApi = { };
+	
+	/*
+	 * Variable: aFiltering
+	 * Purpose:  Container for plugin filtering functions
+	 * Scope:    jQuery.fn.dataTableExt
+	 */
+	_oExt.afnFiltering = [ ];
+	
+	/*
+	 * Variable: aoFeatures
+	 * Purpose:  Container for plugin function functions
+	 * Scope:    jQuery.fn.dataTableExt
+	 * Notes:    Array of objects with the following parameters:
+	 *   fnInit: Function for initialisation of Feature. Takes oSettings and returns node
+	 *   cFeature: Character that will be matched in sDom - case sensitive
+	 *   sFeature: Feature name - just for completeness :-)
+	 */
+	_oExt.aoFeatures = [ ];
+	
+	/*
+	 * Variable: ofnSearch
+	 * Purpose:  Container for custom filtering functions
+	 * Scope:    jQuery.fn.dataTableExt
+	 * Notes:    This is an object (the name should match the type) for custom filtering function,
+	 *   which can be used for live DOM checking or formatted text filtering
+	 */
+	_oExt.ofnSearch = { };
+	
+	/*
+	 * Variable: afnSortData
+	 * Purpose:  Container for custom sorting data source functions
+	 * Scope:    jQuery.fn.dataTableExt
+	 * Notes:    Array (associative) of functions which is run prior to a column of this 
+	 *   'SortDataType' being sorted upon.
+	 *   Function input parameters:
+	 *     object:oSettings-  DataTables settings object
+	 *     int:iColumn - Target column number
+	 *   Return value: Array of data which exactly matched the full data set size for the column to
+	 *     be sorted upon
+	 */
+	_oExt.afnSortData = [ ];
+	
+	/*
+	 * Variable: oStdClasses
+	 * Purpose:  Storage for the various classes that DataTables uses
+	 * Scope:    jQuery.fn.dataTableExt
+	 */
+	_oExt.oStdClasses = {
+		/* Two buttons buttons */
+		"sPagePrevEnabled": "paginate_enabled_previous",
+		"sPagePrevDisabled": "paginate_disabled_previous",
+		"sPageNextEnabled": "paginate_enabled_next",
+		"sPageNextDisabled": "paginate_disabled_next",
+		"sPageJUINext": "",
+		"sPageJUIPrev": "",
+		
+		/* Full numbers paging buttons */
+		"sPageButton": "paginate_button",
+		"sPageButtonActive": "paginate_active",
+		"sPageButtonStaticDisabled": "paginate_button paginate_button_disabled",
+		"sPageFirst": "first",
+		"sPagePrevious": "previous",
+		"sPageNext": "next",
+		"sPageLast": "last",
+		
+		/* Striping classes */
+		"sStripeOdd": "odd",
+		"sStripeEven": "even",
+		
+		/* Empty row */
+		"sRowEmpty": "dataTables_empty",
+		
+		/* Features */
+		"sWrapper": "dataTables_wrapper",
+		"sFilter": "dataTables_filter",
+		"sInfo": "dataTables_info",
+		"sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
+		"sLength": "dataTables_length",
+		"sProcessing": "dataTables_processing",
+		
+		/* Sorting */
+		"sSortAsc": "sorting_asc",
+		"sSortDesc": "sorting_desc",
+		"sSortable": "sorting", /* Sortable in both directions */
+		"sSortableAsc": "sorting_asc_disabled",
+		"sSortableDesc": "sorting_desc_disabled",
+		"sSortableNone": "sorting_disabled",
+		"sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
+		"sSortJUIAsc": "",
+		"sSortJUIDesc": "",
+		"sSortJUI": "",
+		"sSortJUIAscAllowed": "",
+		"sSortJUIDescAllowed": "",
+		"sSortJUIWrapper": "",
+		"sSortIcon": "",
+		
+		/* Scrolling */
+		"sScrollWrapper": "dataTables_scroll",
+		"sScrollHead": "dataTables_scrollHead",
+		"sScrollHeadInner": "dataTables_scrollHeadInner",
+		"sScrollBody": "dataTables_scrollBody",
+		"sScrollFoot": "dataTables_scrollFoot",
+		"sScrollFootInner": "dataTables_scrollFootInner",
+		
+		/* Misc */
+		"sFooterTH": ""
+	};
+	
+	/*
+	 * Variable: oJUIClasses
+	 * Purpose:  Storage for the various classes that DataTables uses - jQuery UI suitable
+	 * Scope:    jQuery.fn.dataTableExt
+	 */
+	_oExt.oJUIClasses = {
+		/* Two buttons buttons */
+		"sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left",
+		"sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
+		"sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right",
+		"sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
+		"sPageJUINext": "ui-icon ui-icon-circle-arrow-e",
+		"sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w",
+		
+		/* Full numbers paging buttons */
+		"sPageButton": "fg-button ui-button ui-state-default",
+		"sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled",
+		"sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled",
+		"sPageFirst": "first ui-corner-tl ui-corner-bl",
+		"sPagePrevious": "previous",
+		"sPageNext": "next",
+		"sPageLast": "last ui-corner-tr ui-corner-br",
+		
+		/* Striping classes */
+		"sStripeOdd": "odd",
+		"sStripeEven": "even",
+		
+		/* Empty row */
+		"sRowEmpty": "dataTables_empty",
+		
+		/* Features */
+		"sWrapper": "dataTables_wrapper",
+		"sFilter": "dataTables_filter",
+		"sInfo": "dataTables_info",
+		"sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
+			"ui-buttonset-multi paging_", /* Note that the type is postfixed */
+		"sLength": "dataTables_length",
+		"sProcessing": "dataTables_processing",
+		
+		/* Sorting */
+		"sSortAsc": "ui-state-default",
+		"sSortDesc": "ui-state-default",
+		"sSortable": "ui-state-default",
+		"sSortableAsc": "ui-state-default",
+		"sSortableDesc": "ui-state-default",
+		"sSortableNone": "ui-state-default",
+		"sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
+		"sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n",
+		"sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s",
+		"sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s",
+		"sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n",
+		"sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s",
+		"sSortJUIWrapper": "DataTables_sort_wrapper",
+		"sSortIcon": "DataTables_sort_icon",
+		
+		/* Scrolling */
+		"sScrollWrapper": "dataTables_scroll",
+		"sScrollHead": "dataTables_scrollHead ui-state-default",
+		"sScrollHeadInner": "dataTables_scrollHeadInner",
+		"sScrollBody": "dataTables_scrollBody",
+		"sScrollFoot": "dataTables_scrollFoot ui-state-default",
+		"sScrollFootInner": "dataTables_scrollFootInner",
+		
+		/* Misc */
+		"sFooterTH": "ui-state-default"
+	};
+	
+	/*
+	 * Variable: oPagination
+	 * Purpose:  Container for the various type of pagination that dataTables supports
+	 * Scope:    jQuery.fn.dataTableExt
+	 */
+	_oExt.oPagination = {
+		/*
+		 * Variable: two_button
+		 * Purpose:  Standard two button (forward/back) pagination
+		 * Scope:    jQuery.fn.dataTableExt.oPagination
+		 */
+		"two_button": {
+			/*
+			 * Function: oPagination.two_button.fnInit
+			 * Purpose:  Initialise dom elements required for pagination with forward/back buttons only
+			 * Returns:  -
+			 * Inputs:   object:oSettings - dataTables settings object
+			 *           node:nPaging - the DIV which contains this pagination control
+			 *           function:fnCallbackDraw - draw function which must be called on update
+			 */
+			"fnInit": function ( oSettings, nPaging, fnCallbackDraw )
+			{
+				var nPrevious, nNext, nPreviousInner, nNextInner;
+				
+				/* Store the next and previous elements in the oSettings object as they can be very
+				 * usful for automation - particularly testing
+				 */
+				if ( !oSettings.bJUI )
+				{
+					nPrevious = document.createElement( 'div' );
+					nNext = document.createElement( 'div' );
+				}
+				else
+				{
+					nPrevious = document.createElement( 'a' );
+					nNext = document.createElement( 'a' );
+					
+					nNextInner = document.createElement('span');
+					nNextInner.className = oSettings.oClasses.sPageJUINext;
+					nNext.appendChild( nNextInner );
+					
+					nPreviousInner = document.createElement('span');
+					nPreviousInner.className = oSettings.oClasses.sPageJUIPrev;
+					nPrevious.appendChild( nPreviousInner );
+				}
+				
+				nPrevious.className = oSettings.oClasses.sPagePrevDisabled;
+				nNext.className = oSettings.oClasses.sPageNextDisabled;
+				
+				nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious;
+				nNext.title = oSettings.oLanguage.oPaginate.sNext;
+				
+				nPaging.appendChild( nPrevious );
+				nPaging.appendChild( nNext );
+				
+				$(nPrevious).bind( 'click.DT', function() {
+					if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) )
+					{
+						/* Only draw when the page has actually changed */
+						fnCallbackDraw( oSettings );
+					}
+				} );
+				
+				$(nNext).bind( 'click.DT', function() {
+					if ( oSettings.oApi._fnPageChange( oSettings, "next" ) )
+					{
+						fnCallbackDraw( oSettings );
+					}
+				} );
+				
+				/* Take the brutal approach to cancelling text selection */
+				$(nPrevious).bind( 'selectstart.DT', function () { return false; } );
+				$(nNext).bind( 'selectstart.DT', function () { return false; } );
+				
+				/* ID the first elements only */
+				if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" )
+				{
+					nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
+					nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' );
+					nNext.setAttribute( 'id', oSettings.sTableId+'_next' );
+				}
+			},
+			
+			/*
+			 * Function: oPagination.two_button.fnUpdate
+			 * Purpose:  Update the two button pagination at the end of the draw
+			 * Returns:  -
+			 * Inputs:   object:oSettings - dataTables settings object
+			 *           function:fnCallbackDraw - draw function to call on page change
+			 */
+			"fnUpdate": function ( oSettings, fnCallbackDraw )
+			{
+				if ( !oSettings.aanFeatures.p )
+				{
+					return;
+				}
+				
+				/* Loop over each instance of the pager */
+				var an = oSettings.aanFeatures.p;
+				for ( var i=0, iLen=an.length ; i<iLen ; i++ )
+				{
+					if ( an[i].childNodes.length !== 0 )
+					{
+						an[i].childNodes[0].className = 
+							( oSettings._iDisplayStart === 0 ) ? 
+							oSettings.oClasses.sPagePrevDisabled : oSettings.oClasses.sPagePrevEnabled;
+						
+						an[i].childNodes[1].className = 
+							( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ? 
+							oSettings.oClasses.sPageNextDisabled : oSettings.oClasses.sPageNextEnabled;
+					}
+				}
+			}
+		},
+		
+		
+		/*
+		 * Variable: iFullNumbersShowPages
+		 * Purpose:  Change the number of pages which can be seen
+		 * Scope:    jQuery.fn.dataTableExt.oPagination
+		 */
+		"iFullNumbersShowPages": 5,
+		
+		/*
+		 * Variable: full_numbers
+		 * Purpose:  Full numbers pagination
+		 * Scope:    jQuery.fn.dataTableExt.oPagination
+		 */
+		"full_numbers": {
+			/*
+			 * Function: oPagination.full_numbers.fnInit
+			 * Purpose:  Initialise dom elements required for pagination with a list of the pages
+			 * Returns:  -
+			 * Inputs:   object:oSettings - dataTables settings object
+			 *           node:nPaging - the DIV which contains this pagination control
+			 *           function:fnCallbackDraw - draw function which must be called on update
+			 */
+			"fnInit": function ( oSettings, nPaging, fnCallbackDraw )
+			{
+				var nFirst = document.createElement( 'span' );
+				var nPrevious = document.createElement( 'span' );
+				var nList = document.createElement( 'span' );
+				var nNext = document.createElement( 'span' );
+				var nLast = document.createElement( 'span' );
+				
+				nFirst.innerHTML = oSettings.oLanguage.oPaginate.sFirst;
+				nPrevious.innerHTML = oSettings.oLanguage.oPaginate.sPrevious;
+				nNext.innerHTML = oSettings.oLanguage.oPaginate.sNext;
+				nLast.innerHTML = oSettings.oLanguage.oPaginate.sLast;
+				
+				var oClasses = oSettings.oClasses;
+				nFirst.className = oClasses.sPageButton+" "+oClasses.sPageFirst;
+				nPrevious.className = oClasses.sPageButton+" "+oClasses.sPagePrevious;
+				nNext.className= oClasses.sPageButton+" "+oClasses.sPageNext;
+				nLast.className = oClasses.sPageButton+" "+oClasses.sPageLast;
+				
+				nPaging.appendChild( nFirst );
+				nPaging.appendChild( nPrevious );
+				nPaging.appendChild( nList );
+				nPaging.appendChild( nNext );
+				nPaging.appendChild( nLast );
+				
+				$(nFirst).bind( 'click.DT', function () {
+					if ( oSettings.oApi._fnPageChange( oSettings, "first" ) )
+					{
+						fnCallbackDraw( oSettings );
+					}
+				} );
+				
+				$(nPrevious).bind( 'click.DT', function() {
+					if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) )
+					{
+						fnCallbackDraw( oSettings );
+					}
+				} );
+				
+				$(nNext).bind( 'click.DT', function() {
+					if ( oSettings.oApi._fnPageChange( oSettings, "next" ) )
+					{
+						fnCallbackDraw( oSettings );
+					}
+				} );
+				
+				$(nLast).bind( 'click.DT', function() {
+					if ( oSettings.oApi._fnPageChange( oSettings, "last" ) )
+					{
+						fnCallbackDraw( oSettings );
+					}
+				} );
+				
+				/* Take the brutal approach to cancelling text selection */
+				$('span', nPaging)
+					.bind( 'mousedown.DT', function () { return false; } )
+					.bind( 'selectstart.DT', function () { return false; } );
+				
+				/* ID the first elements only */
+				if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" )
+				{
+					nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
+					nFirst.setAttribute( 'id', oSettings.sTableId+'_first' );
+					nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' );
+					nNext.setAttribute( 'id', oSettings.sTableId+'_next' );
+					nLast.setAttribute( 'id', oSettings.sTableId+'_last' );
+				}
+			},
+			
+			/*
+			 * Function: oPagination.full_numbers.fnUpdate
+			 * Purpose:  Update the list of page buttons shows
+			 * Returns:  -
+			 * Inputs:   object:oSettings - dataTables settings object
+			 *           function:fnCallbackDraw - draw function to call on page change
+			 */
+			"fnUpdate": function ( oSettings, fnCallbackDraw )
+			{
+				if ( !oSettings.aanFeatures.p )
+				{
+					return;
+				}
+				
+				var iPageCount = _oExt.oPagination.iFullNumbersShowPages;
+				var iPageCountHalf = Math.floor(iPageCount / 2);
+				var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength);
+				var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
+				var sList = "";
+				var iStartButton, iEndButton, i, iLen;
+				var oClasses = oSettings.oClasses;
+				
+				/* Pages calculation */
+				if (iPages < iPageCount)
+				{
+					iStartButton = 1;
+					iEndButton = iPages;
+				}
+				else
+				{
+					if (iCurrentPage <= iPageCountHalf)
+					{
+						iStartButton = 1;
+						iEndButton = iPageCount;
+					}
+					else
+					{
+						if (iCurrentPage >= (iPages - iPageCountHalf))
+						{
+							iStartButton = iPages - iPageCount + 1;
+							iEndButton = iPages;
+						}
+						else
+						{
+							iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1;
+							iEndButton = iStartButton + iPageCount - 1;
+						}
+					}
+				}
+				
+				/* Build the dynamic list */
+				for ( i=iStartButton ; i<=iEndButton ; i++ )
+				{
+					if ( iCurrentPage != i )
+					{
+						sList += '<span class="'+oClasses.sPageButton+'">'+i+'</span>';
+					}
+					else
+					{
+						sList += '<span class="'+oClasses.sPageButtonActive+'">'+i+'</span>';
+					}
+				}
+				
+				/* Loop over each instance of the pager */
+				var an = oSettings.aanFeatures.p;
+				var anButtons, anStatic, nPaginateList;
+				var fnClick = function(e) {
+					/* Use the information in the element to jump to the required page */
+					var iTarget = (this.innerHTML * 1) - 1;
+					oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength;
+					fnCallbackDraw( oSettings );
+					e.preventDefault();
+				};
+				var fnFalse = function () { return false; };
+				
+				for ( i=0, iLen=an.length ; i<iLen ; i++ )
+				{
+					if ( an[i].childNodes.length === 0 )
+					{
+						continue;
+					}
+					
+					/* Build up the dynamic list forst - html and listeners */
+					var qjPaginateList = $('span:eq(2)', an[i]);
+					qjPaginateList.html( sList );
+					$('span', qjPaginateList).bind( 'click.DT', fnClick ).bind( 'mousedown.DT', fnFalse )
+						.bind( 'selectstart.DT', fnFalse );
+					
+					/* Update the 'premanent botton's classes */
+					anButtons = an[i].getElementsByTagName('span');
+					anStatic = [
+						anButtons[0], anButtons[1], 
+						anButtons[anButtons.length-2], anButtons[anButtons.length-1]
+					];
+					$(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled );
+					if ( iCurrentPage == 1 )
+					{
+						anStatic[0].className += " "+oClasses.sPageButtonStaticDisabled;
+						anStatic[1].className += " "+oClasses.sPageButtonStaticDisabled;
+					}
+					else
+					{
+						anStatic[0].className += " "+oClasses.sPageButton;
+						anStatic[1].className += " "+oClasses.sPageButton;
+					}
+					
+					if ( iPages === 0 || iCurrentPage == iPages || oSettings._iDisplayLength == -1 )
+					{
+						anStatic[2].className += " "+oClasses.sPageButtonStaticDisabled;
+						anStatic[3].className += " "+oClasses.sPageButtonStaticDisabled;
+					}
+					else
+					{
+						anStatic[2].className += " "+oClasses.sPageButton;
+						anStatic[3].className += " "+oClasses.sPageButton;
+					}
+				}
+			}
+		}
+	};
+	
+	/*
+	 * Variable: oSort
+	 * Purpose:  Wrapper for the sorting functions that can be used in DataTables
+	 * Scope:    jQuery.fn.dataTableExt
+	 * Notes:    The functions provided in this object are basically standard javascript sort
+	 *   functions - they expect two inputs which they then compare and then return a priority
+	 *   result. For each sort method added, two functions need to be defined, an ascending sort and
+	 *   a descending sort.
+	 */
+	_oExt.oSort = {
+		/*
+		 * text sorting
+		 */
+		"string-asc": function ( a, b )
+		{
+			if ( typeof a != 'string' ) { a = ''; }
+			if ( typeof b != 'string' ) { b = ''; }
+			var x = a.toLowerCase();
+			var y = b.toLowerCase();
+			return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+		},
+		
+		"string-desc": function ( a, b )
+		{
+			if ( typeof a != 'string' ) { a = ''; }
+			if ( typeof b != 'string' ) { b = ''; }
+			var x = a.toLowerCase();
+			var y = b.toLowerCase();
+			return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+		},
+		
+		
+		/*
+		 * html sorting (ignore html tags)
+		 */
+		"html-asc": function ( a, b )
+		{
+			var x = a.replace( /<.*?>/g, "" ).toLowerCase();
+			var y = b.replace( /<.*?>/g, "" ).toLowerCase();
+			return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+		},
+		
+		"html-desc": function ( a, b )
+		{
+			var x = a.replace( /<.*?>/g, "" ).toLowerCase();
+			var y = b.replace( /<.*?>/g, "" ).toLowerCase();
+			return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+		},
+		
+		
+		/*
+		 * date sorting
+		 */
+		"date-asc": function ( a, b )
+		{
+			var x = Date.parse( a );
+			var y = Date.parse( b );
+			
+			if ( isNaN(x) || x==="" )
+			{
+			x = Date.parse( "01/01/1970 00:00:00" );
+			}
+			if ( isNaN(y) || y==="" )
+			{
+				y =	Date.parse( "01/01/1970 00:00:00" );
+			}
+			
+			return x - y;
+		},
+		
+		"date-desc": function ( a, b )
+		{
+			var x = Date.parse( a );
+			var y = Date.parse( b );
+			
+			if ( isNaN(x) || x==="" )
+			{
+			x = Date.parse( "01/01/1970 00:00:00" );
+			}
+			if ( isNaN(y) || y==="" )
+			{
+				y =	Date.parse( "01/01/1970 00:00:00" );
+			}
+			
+			return y - x;
+		},
+		
+		
+		/*
+		 * numerical sorting
+		 */
+		"numeric-asc": function ( a, b )
+		{
+			var x = (a=="-" || a==="") ? 0 : a*1;
+			var y = (b=="-" || b==="") ? 0 : b*1;
+			return x - y;
+		},
+		
+		"numeric-desc": function ( a, b )
+		{
+			var x = (a=="-" || a==="") ? 0 : a*1;
+			var y = (b=="-" || b==="") ? 0 : b*1;
+			return y - x;
+		}
+	};
+	
+	
+	/*
+	 * Variable: aTypes
+	 * Purpose:  Container for the various type of type detection that dataTables supports
+	 * Scope:    jQuery.fn.dataTableExt
+	 * Notes:    The functions in this array are expected to parse a string to see if it is a data
+	 *   type that it recognises. If so then the function should return the name of the type (a
+	 *   corresponding sort function should be defined!), if the type is not recognised then the
+	 *   function should return null such that the parser and move on to check the next type.
+	 *   Note that ordering is important in this array - the functions are processed linearly,
+	 *   starting at index 0.
+	 *   Note that the input for these functions is always a string! It cannot be any other data
+	 *   type
+	 */
+	_oExt.aTypes = [
+		/*
+		 * Function: -
+		 * Purpose:  Check to see if a string is numeric
+		 * Returns:  string:'numeric' or null
+		 * Inputs:   mixed:sText - string to check
+		 */
+		function ( sData )
+		{
+			/* Allow zero length strings as a number */
+			if ( typeof sData == 'number' )
+			{
+				return 'numeric';
+			}
+			else if ( typeof sData != 'string' )
+			{
+				return null;
+			}
+			
+			var sValidFirstChars = "0123456789-";
+			var sValidChars = "0123456789.";
+			var Char;
+			var bDecimal = false;
+			
+			/* Check for a valid first char (no period and allow negatives) */
+			Char = sData.charAt(0); 
+			if (sValidFirstChars.indexOf(Char) == -1) 
+			{
+				return null;
+			}
+			
+			/* Check all the other characters are valid */
+			for ( var i=1 ; i<sData.length ; i++ ) 
+			{
+				Char = sData.charAt(i); 
+				if (sValidChars.indexOf(Char) == -1) 
+				{
+					return null;
+				}
+				
+				/* Only allowed one decimal place... */
+				if ( Char == "." )
+				{
+					if ( bDecimal )
+					{
+						return null;
+					}
+					bDecimal = true;
+				}
+			}
+			
+			return 'numeric';
+		},
+		
+		/*
+		 * Function: -
+		 * Purpose:  Check to see if a string is actually a formatted date
+		 * Returns:  string:'date' or null
+		 * Inputs:   string:sText - string to check
+		 */
+		function ( sData )
+		{
+			var iParse = Date.parse(sData);
+			if ( (iParse !== null && !isNaN(iParse)) || (typeof sData == 'string' && sData.length === 0) )
+			{
+				return 'date';
+			}
+			return null;
+		},
+		
+		/*
+		 * Function: -
+		 * Purpose:  Check to see if a string should be treated as an HTML string
+		 * Returns:  string:'html' or null
+		 * Inputs:   string:sText - string to check
+		 */
+		function ( sData )
+		{
+			if ( typeof sData == 'string' && sData.indexOf('<') != -1 && sData.indexOf('>') != -1 )
+			{
+				return 'html';
+			}
+			return null;
+		}
+	];
+	
+	/*
+	 * Function: fnVersionCheck
+	 * Purpose:  Check a version string against this version of DataTables. Useful for plug-ins
+	 * Returns:  bool:true -this version of DataTables is greater or equal to the required version
+	 *                false -this version of DataTales is not suitable
+	 * Inputs:   string:sVersion - the version to check against. May be in the following formats:
+	 *             "a", "a.b" or "a.b.c"
+	 * Notes:    This function will only check the first three parts of a version string. It is
+	 *   assumed that beta and dev versions will meet the requirements. This might change in future
+	 */
+	_oExt.fnVersionCheck = function( sVersion )
+	{
+		/* This is cheap, but very effective */
+		var fnZPad = function (Zpad, count)
+		{
+			while(Zpad.length < count) {
+				Zpad += '0';
+			}
+			return Zpad;
+		};
+		var aThis = _oExt.sVersion.split('.');
+		var aThat = sVersion.split('.');
+		var sThis = '', sThat = '';
+		
+		for ( var i=0, iLen=aThat.length ; i<iLen ; i++ )
+		{
+			sThis += fnZPad( aThis[i], 3 );
+			sThat += fnZPad( aThat[i], 3 );
+		}
+		
+		return parseInt(sThis, 10) >= parseInt(sThat, 10);
+	};
+	
+	/*
+	 * Variable: _oExternConfig
+	 * Purpose:  Store information for DataTables to access globally about other instances
+	 * Scope:    jQuery.fn.dataTableExt
+	 */
+	_oExt._oExternConfig = {
+		/* int:iNextUnique - next unique number for an instance */
+		"iNextUnique": 0
+	};
+	
+	
+	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+	 * Section - DataTables prototype
+	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+	
+	/*
+	 * Function: dataTable
+	 * Purpose:  DataTables information
+	 * Returns:  -
+	 * Inputs:   object:oInit - initialisation options for the table
+	 */
+	$.fn.dataTable = function( oInit )
+	{
+		/*
+		 * Function: classSettings
+		 * Purpose:  Settings container function for all 'class' properties which are required
+		 *   by dataTables
+		 * Returns:  -
+		 * Inputs:   -
+		 */
+		function classSettings ()
+		{
+			this.fnRecordsTotal = function ()
+			{
+				if ( this.oFeatures.bServerSide ) {
+					return parseInt(this._iRecordsTotal, 10);
+				} else {
+					return this.aiDisplayMaster.length;
+				}
+			};
+			
+			this.fnRecordsDisplay = function ()
+			{
+				if ( this.oFeatures.bServerSide ) {
+					return parseInt(this._iRecordsDisplay, 10);
+				} else {
+					return this.aiDisplay.length;
+				}
+			};
+			
+			this.fnDisplayEnd = function ()
+			{
+				if ( this.oFeatures.bServerSide ) {
+					if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) {
+						return this._iDisplayStart+this.aiDisplay.length;
+					} else {
+						return Math.min( this._iDisplayStart+this._iDisplayLength, 
+							this._iRecordsDisplay );
+					}
+				} else {
+					return this._iDisplayEnd;
+				}
+			};
+			
+			/*
+			 * Variable: oInstance
+			 * Purpose:  The DataTables object for this table
+			 * Scope:    jQuery.dataTable.classSettings 
+			 */
+			this.oInstance = null;
+			
+			/*
+			 * Variable: sInstance
+			 * Purpose:  Unique idendifier for each instance of the DataTables object
+			 * Scope:    jQuery.dataTable.classSettings 
+			 */
+			this.sInstance = null;
+			
+			/*
+			 * Variable: oFeatures
+			 * Purpose:  Indicate the enablement of key dataTable features
+			 * Scope:    jQuery.dataTable.classSettings 
+			 */
+			this.oFeatures = {
+				"bPaginate": true,
+				"bLengthChange": true,
+				"bFilter": true,
+				"bSort": true,
+				"bInfo": true,
+				"bAutoWidth": true,
+				"bProcessing": false,
+				"bSortClasses": true,
+				"bStateSave": false,
+				"bServerSide": false,
+				"bDeferRender": false
+			};
+			
+			/*
+			 * Variable: oScroll
+			 * Purpose:  Container for scrolling options
+			 * Scope:    jQuery.dataTable.classSettings 
+			 */
+			this.oScroll = {
+				"sX": "",
+				"sXInner": "",
+				"sY": "",
+				"bCollapse": false,
+				"bInfinite": false,
+				"iLoadGap": 100,
+				"iBarWidth": 0,
+				"bAutoCss": true
+			};
+			
+			/*
+			 * Variable: aanFeatures
+			 * Purpose:  Array referencing the nodes which are used for the features
+			 * Scope:    jQuery.dataTable.classSettings 
+			 * Notes:    The parameters of this object match what is allowed by sDom - i.e.
+			 *   'l' - Length changing
+			 *   'f' - Filtering input
+			 *   't' - The table!
+			 *   'i' - Information
+			 *   'p' - Pagination
+			 *   'r' - pRocessing
+			 */
+			this.aanFeatures = [];
+			
+			/*
+			 * Variable: oLanguage
+			 * Purpose:  Store the language strings used by dataTables
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    The words in the format _VAR_ are variables which are dynamically replaced
+			 *   by javascript
+			 */
+			this.oLanguage = {
+				"sProcessing": "Processing...",
+				"sLengthMenu": "Show _MENU_ entries",
+				"sZeroRecords": "No matching records found",
+				"sEmptyTable": "No data available in table",
+				"sLoadingRecords": "Loading...",
+				"sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
+				"sInfoEmpty": "Showing 0 to 0 of 0 entries",
+				"sInfoFiltered": "(filtered from _MAX_ total entries)",
+				"sInfoPostFix": "",
+				"sInfoThousands": ",",
+				"sSearch": "Search:",
+				"sUrl": "",
+				"oPaginate": {
+					"sFirst":    "First",
+					"sPrevious": "Previous",
+					"sNext":     "Next",
+					"sLast":     "Last"
+				},
+				"fnInfoCallback": null
+			};
+			
+			/*
+			 * Variable: aoData
+			 * Purpose:  Store data information
+			 * Scope:    jQuery.dataTable.classSettings 
+			 * Notes:    This is an array of objects with the following parameters:
+			 *   int: _iId - internal id for tracking
+			 *   array: _aData - internal data - used for sorting / filtering etc
+			 *   node: nTr - display node
+			 *   array node: _anHidden - hidden TD nodes
+			 *   string: _sRowStripe
+			 */
+			this.aoData = [];
+			
+			/*
+			 * Variable: aiDisplay
+			 * Purpose:  Array of indexes which are in the current display (after filtering etc)
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.aiDisplay = [];
+			
+			/*
+			 * Variable: aiDisplayMaster
+			 * Purpose:  Array of indexes for display - no filtering
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.aiDisplayMaster = [];
+							
+			/*
+			 * Variable: aoColumns
+			 * Purpose:  Store information about each column that is in use
+			 * Scope:    jQuery.dataTable.classSettings 
+			 */
+			this.aoColumns = [];
+							
+			/*
+			 * Variable: aoHeader
+			 * Purpose:  Store information about the table's header
+			 * Scope:    jQuery.dataTable.classSettings 
+			 */
+			this.aoHeader = [];
+							
+			/*
+			 * Variable: aoFooter
+			 * Purpose:  Store information about the table's footer
+			 * Scope:    jQuery.dataTable.classSettings 
+			 */
+			this.aoFooter = [];
+			
+			/*
+			 * Variable: iNextId
+			 * Purpose:  Store the next unique id to be used for a new row
+			 * Scope:    jQuery.dataTable.classSettings 
+			 */
+			this.iNextId = 0;
+			
+			/*
+			 * Variable: asDataSearch
+			 * Purpose:  Search data array for regular expression searching
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.asDataSearch = [];
+			
+			/*
+			 * Variable: oPreviousSearch
+			 * Purpose:  Store the previous search incase we want to force a re-search
+			 *   or compare the old search to a new one
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.oPreviousSearch = {
+				"sSearch": "",
+				"bRegex": false,
+				"bSmart": true
+			};
+			
+			/*
+			 * Variable: aoPreSearchCols
+			 * Purpose:  Store the previous search for each column
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.aoPreSearchCols = [];
+			
+			/*
+			 * Variable: aaSorting
+			 * Purpose:  Sorting information
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    Index 0 - column number
+			 *           Index 1 - current sorting direction
+			 *           Index 2 - index of asSorting for this column
+			 */
+			this.aaSorting = [ [0, 'asc', 0] ];
+			
+			/*
+			 * Variable: aaSortingFixed
+			 * Purpose:  Sorting information that is always applied
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.aaSortingFixed = null;
+			
+			/*
+			 * Variable: asStripeClasses
+			 * Purpose:  Classes to use for the striping of a table
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.asStripeClasses = [];
+			
+			/*
+			 * Variable: asDestroyStripes
+			 * Purpose:  If restoring a table - we should restore its striping classes as well
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.asDestroyStripes = [];
+			
+			/*
+			 * Variable: sDestroyWidth
+			 * Purpose:  If restoring a table - we should restore its width
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.sDestroyWidth = 0;
+			
+			/*
+			 * Variable: fnRowCallback
+			 * Purpose:  Call this function every time a row is inserted (draw)
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.fnRowCallback = null;
+			
+			/*
+			 * Variable: fnHeaderCallback
+			 * Purpose:  Callback function for the header on each draw
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.fnHeaderCallback = null;
+			
+			/*
+			 * Variable: fnFooterCallback
+			 * Purpose:  Callback function for the footer on each draw
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.fnFooterCallback = null;
+			
+			/*
+			 * Variable: aoDrawCallback
+			 * Purpose:  Array of callback functions for draw callback functions
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    Each array element is an object with the following parameters:
+			 *   function:fn - function to call
+			 *   string:sName - name callback (feature). useful for arranging array
+			 */
+			this.aoDrawCallback = [];
+			
+			/*
+			 * Variable: fnPreDrawCallback
+			 * Purpose:  Callback function for just before the table is redrawn. A return of false
+			 *           will be used to cancel the draw.
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.fnPreDrawCallback = null;
+			
+			/*
+			 * Variable: fnInitComplete
+			 * Purpose:  Callback function for when the table has been initialised
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.fnInitComplete = null;
+			
+			/*
+			 * Variable: sTableId
+			 * Purpose:  Cache the table ID for quick access
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.sTableId = "";
+			
+			/*
+			 * Variable: nTable
+			 * Purpose:  Cache the table node for quick access
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.nTable = null;
+			
+			/*
+			 * Variable: nTHead
+			 * Purpose:  Permanent ref to the thead element
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.nTHead = null;
+			
+			/*
+			 * Variable: nTFoot
+			 * Purpose:  Permanent ref to the tfoot element - if it exists
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.nTFoot = null;
+			
+			/*
+			 * Variable: nTBody
+			 * Purpose:  Permanent ref to the tbody element
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.nTBody = null;
+			
+			/*
+			 * Variable: nTableWrapper
+			 * Purpose:  Cache the wrapper node (contains all DataTables controlled elements)
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.nTableWrapper = null;
+			
+			/*
+			 * Variable: bDeferLoading
+			 * Purpose:  Indicate if when using server-side processing the loading of data 
+			 *           should be deferred until the second draw
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.bDeferLoading = false;
+			
+			/*
+			 * Variable: bInitialised
+			 * Purpose:  Indicate if all required information has been read in
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.bInitialised = false;
+			
+			/*
+			 * Variable: aoOpenRows
+			 * Purpose:  Information about open rows
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    Has the parameters 'nTr' and 'nParent'
+			 */
+			this.aoOpenRows = [];
+			
+			/*
+			 * Variable: sDom
+			 * Purpose:  Dictate the positioning that the created elements will take
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    
+			 *   The following options are allowed:
+			 *     'l' - Length changing
+			 *     'f' - Filtering input
+			 *     't' - The table!
+			 *     'i' - Information
+			 *     'p' - Pagination
+			 *     'r' - pRocessing
+			 *   The following constants are allowed:
+			 *     'H' - jQueryUI theme "header" classes
+			 *     'F' - jQueryUI theme "footer" classes
+			 *   The following syntax is expected:
+			 *     '<' and '>' - div elements
+			 *     '<"class" and '>' - div with a class
+			 *   Examples:
+			 *     '<"wrapper"flipt>', '<lf<t>ip>'
+			 */
+			this.sDom = 'lfrtip';
+			
+			/*
+			 * Variable: sPaginationType
+			 * Purpose:  Note which type of sorting should be used
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.sPaginationType = "two_button";
+			
+			/*
+			 * Variable: iCookieDuration
+			 * Purpose:  The cookie duration (for bStateSave) in seconds - default 2 hours
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.iCookieDuration = 60 * 60 * 2;
+			
+			/*
+			 * Variable: sCookiePrefix
+			 * Purpose:  The cookie name prefix
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.sCookiePrefix = "SpryMedia_DataTables_";
+			
+			/*
+			 * Variable: fnCookieCallback
+			 * Purpose:  Callback function for cookie creation
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.fnCookieCallback = null;
+			
+			/*
+			 * Variable: aoStateSave
+			 * Purpose:  Array of callback functions for state saving
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    Each array element is an object with the following parameters:
+			 *   function:fn - function to call. Takes two parameters, oSettings and the JSON string to
+			 *     save that has been thus far created. Returns a JSON string to be inserted into a 
+			 *     json object (i.e. '"param": [ 0, 1, 2]')
+			 *   string:sName - name of callback
+			 */
+			this.aoStateSave = [];
+			
+			/*
+			 * Variable: aoStateLoad
+			 * Purpose:  Array of callback functions for state loading
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    Each array element is an object with the following parameters:
+			 *   function:fn - function to call. Takes two parameters, oSettings and the object stored.
+			 *     May return false to cancel state loading.
+			 *   string:sName - name of callback
+			 */
+			this.aoStateLoad = [];
+			
+			/*
+			 * Variable: oLoadedState
+			 * Purpose:  State that was loaded from the cookie. Useful for back reference
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.oLoadedState = null;
+			
+			/*
+			 * Variable: sAjaxSource
+			 * Purpose:  Source url for AJAX data for the table
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.sAjaxSource = null;
+			
+			/*
+			 * Variable: sAjaxDataProp
+			 * Purpose:  Property from a given object from which to read the table data from. This can
+			 *           be an empty string (when not server-side processing), in which case it is 
+			 *           assumed an an array is given directly.
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.sAjaxDataProp = 'aaData';
+			
+			/*
+			 * Variable: bAjaxDataGet
+			 * Purpose:  Note if draw should be blocked while getting data
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.bAjaxDataGet = true;
+			
+			/*
+			 * Variable: jqXHR
+			 * Purpose:  The last jQuery XHR object that was used for server-side data gathering. 
+			 *           This can be used for working with the XHR information in one of the callbacks
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.jqXHR = null;
+			
+			/*
+			 * Variable: fnServerData
+			 * Purpose:  Function to get the server-side data - can be overruled by the developer
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.fnServerData = function ( url, data, callback, settings ) {
+				settings.jqXHR = $.ajax( {
+					"url": url,
+					"data": data,
+					"success": function (json) {
+						$(settings.oInstance).trigger('xhr', settings);
+						callback( json );
+					},
+					"dataType": "json",
+					"cache": false,
+					"error": function (xhr, error, thrown) {
+						if ( error == "parsererror" ) {
+							alert( "DataTables warning: JSON data from server could not be parsed. "+
+								"This is caused by a JSON formatting error." );
+						}
+					}
+				} );
+			};
+			
+			/*
+			 * Variable: aoServerParams
+			 * Purpose:  Functions which are called prior to sending an Ajax request so extra parameters
+			 *           can easily be sent to the server
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    Each array element is an object with the following parameters:
+			 *   function:fn - function to call
+			 *   string:sName - name callback - useful for knowing where it came from (plugin etc)
+			 */
+			this.aoServerParams = [];
+			
+			/*
+			 * Variable: fnFormatNumber
+			 * Purpose:  Format numbers for display
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.fnFormatNumber = function ( iIn )
+			{
+				if ( iIn < 1000 )
+				{
+					/* A small optimisation for what is likely to be the vast majority of use cases */
+					return iIn;
+				}
+				else
+				{
+					var s=(iIn+""), a=s.split(""), out="", iLen=s.length;
+					
+					for ( var i=0 ; i<iLen ; i++ )
+					{
+						if ( i%3 === 0 && i !== 0 )
+						{
+							out = this.oLanguage.sInfoThousands+out;
+						}
+						out = a[iLen-i-1]+out;
+					}
+				}
+				return out;
+			};
+			
+			/*
+			 * Variable: aLengthMenu
+			 * Purpose:  List of options that can be used for the user selectable length menu
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Note:     This varaible can take for form of a 1D array, in which case the value and the 
+			 *   displayed value in the menu are the same, or a 2D array in which case the value comes
+			 *   from the first array, and the displayed value to the end user comes from the second
+			 *   array. 2D example: [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, 'All' ] ];
+			 */
+			this.aLengthMenu = [ 10, 25, 50, 100 ];
+			
+			/*
+			 * Variable: iDraw
+			 * Purpose:  Counter for the draws that the table does. Also used as a tracker for
+			 *   server-side processing
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.iDraw = 0;
+			
+			/*
+			 * Variable: bDrawing
+			 * Purpose:  Indicate if a redraw is being done - useful for Ajax
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.bDrawing = 0;
+			
+			/*
+			 * Variable: iDrawError
+			 * Purpose:  Last draw error
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.iDrawError = -1;
+			
+			/*
+			 * Variable: _iDisplayLength, _iDisplayStart, _iDisplayEnd
+			 * Purpose:  Display length variables
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    These variable must NOT be used externally to get the data length. Rather, use
+			 *   the fnRecordsTotal() (etc) functions.
+			 */
+			this._iDisplayLength = 10;
+			this._iDisplayStart = 0;
+			this._iDisplayEnd = 10;
+			
+			/*
+			 * Variable: _iRecordsTotal, _iRecordsDisplay
+			 * Purpose:  Display length variables used for server side processing
+			 * Scope:    jQuery.dataTable.classSettings
+			 * Notes:    These variable must NOT be used externally to get the data length. Rather, use
+			 *   the fnRecordsTotal() (etc) functions.
+			 */
+			this._iRecordsTotal = 0;
+			this._iRecordsDisplay = 0;
+			
+			/*
+			 * Variable: bJUI
+			 * Purpose:  Should we add the markup needed for jQuery UI theming?
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.bJUI = false;
+			
+			/*
+			 * Variable: oClasses
+			 * Purpose:  Should we add the markup needed for jQuery UI theming?
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.oClasses = _oExt.oStdClasses;
+			
+			/*
+			 * Variable: bFiltered and bSorted
+			 * Purpose:  Flags to allow callback functions to see what actions have been performed
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.bFiltered = false;
+			this.bSorted = false;
+			
+			/*
+			 * Variable: bSortCellsTop
+			 * Purpose:  Indicate that if multiple rows are in the header and there is more than one
+			 *           unique cell per column, if the top one (true) or bottom one (false) should
+			 *           be used for sorting / title by DataTables
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.bSortCellsTop = false;
+			
+			/*
+			 * Variable: oInit
+			 * Purpose:  Initialisation object that is used for the table
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.oInit = null;
+			
+			/*
+			 * Variable: aoDestroyCallback
+			 * Purpose:  Destroy callback functions
+			 * Scope:    jQuery.dataTable.classSettings
+			 */
+			this.aoDestroyCallback = [];
+		}
+		
+		/*
+		 * Variable: oApi
+		 * Purpose:  Container for publicly exposed 'private' functions
+		 * Scope:    jQuery.dataTable
+		 */
+		this.oApi = {};
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - API functions
+		 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+		
+		/*
+		 * Function: fnDraw
+		 * Purpose:  Redraw the table
+		 * Returns:  -
+		 * Inputs:   bool:bComplete - Refilter and resort (if enabled) the table before the draw.
+		 *             Optional: default - true
+		 */
+		this.fnDraw = function( bComplete )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			if ( typeof bComplete != 'undefined' && bComplete === false )
+			{
+				_fnCalculateEnd( oSettings );
+				_fnDraw( oSettings );
+			}
+			else
+			{
+				_fnReDraw( oSettings );
+			}
+		};
+		
+		/*
+		 * Function: fnFilter
+		 * Purpose:  Filter the input based on data
+		 * Returns:  -
+		 * Inputs:   string:sInput - string to filter the table on
+		 *           int:iColumn - optional - column to limit filtering to
+		 *           bool:bRegex - optional - treat as regular expression or not - default false
+		 *           bool:bSmart - optional - perform smart filtering or not - default true
+		 *           bool:bShowGlobal - optional - show the input global filter in it's input box(es)
+		 *              - default true
+		 */
+		this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			
+			if ( !oSettings.oFeatures.bFilter )
+			{
+				return;
+			}
+			
+			if ( typeof bRegex == 'undefined' )
+			{
+				bRegex = false;
+			}
+			
+			if ( typeof bSmart == 'undefined' )
+			{
+				bSmart = true;
+			}
+			
+			if ( typeof bShowGlobal == 'undefined' )
+			{
+				bShowGlobal = true;
+			}
+			
+			if ( typeof iColumn == "undefined" || iColumn === null )
+			{
+				/* Global filter */
+				_fnFilterComplete( oSettings, {
+					"sSearch":sInput,
+					"bRegex": bRegex,
+					"bSmart": bSmart
+				}, 1 );
+				
+				if ( bShowGlobal && typeof oSettings.aanFeatures.f != 'undefined' )
+				{
+					var n = oSettings.aanFeatures.f;
+					for ( var i=0, iLen=n.length ; i<iLen ; i++ )
+					{
+						$('input', n[i]).val( sInput );
+					}
+				}
+			}
+			else
+			{
+				/* Single column filter */
+				oSettings.aoPreSearchCols[ iColumn ].sSearch = sInput;
+				oSettings.aoPreSearchCols[ iColumn ].bRegex = bRegex;
+				oSettings.aoPreSearchCols[ iColumn ].bSmart = bSmart;
+				_fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
+			}
+		};
+		
+		/*
+		 * Function: fnSettings
+		 * Purpose:  Get the settings for a particular table for extern. manipulation
+		 * Returns:  -
+		 * Inputs:   -
+		 */
+		this.fnSettings = function( nNode  )
+		{
+			return _fnSettingsFromNode( this[_oExt.iApiIndex] );
+		};
+		
+		/*
+		 * Function: fnVersionCheck
+		 * Notes:    The function is the same as the 'static' function provided in the ext variable
+		 */
+		this.fnVersionCheck = _oExt.fnVersionCheck;
+		
+		/*
+		 * Function: fnSort
+		 * Purpose:  Sort the table by a particular row
+		 * Returns:  -
+		 * Inputs:   int:iCol - the data index to sort on. Note that this will
+		 *   not match the 'display index' if you have hidden data entries
+		 */
+		this.fnSort = function( aaSort )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			oSettings.aaSorting = aaSort;
+			_fnSort( oSettings );
+		};
+		
+		/*
+		 * Function: fnSortListener
+		 * Purpose:  Attach a sort listener to an element for a given column
+		 * Returns:  -
+		 * Inputs:   node:nNode - the element to attach the sort listener to
+		 *           int:iColumn - the column that a click on this node will sort on
+		 *           function:fnCallback - callback function when sort is run - optional
+		 */
+		this.fnSortListener = function( nNode, iColumn, fnCallback )
+		{
+			_fnSortAttachListener( _fnSettingsFromNode( this[_oExt.iApiIndex] ), nNode, iColumn,
+			 	fnCallback );
+		};
+		
+		/*
+		 * Function: fnAddData
+		 * Purpose:  Add new row(s) into the table
+		 * Returns:  array int: array of indexes (aoData) which have been added (zero length on error)
+		 * Inputs:   array:mData - the data to be added. The length must match
+		 *               the original data from the DOM
+		 *             or
+		 *             array array:mData - 2D array of data to be added
+		 *           bool:bRedraw - redraw the table or not - default true
+		 * Notes:    Warning - the refilter here will cause the table to redraw
+		 *             starting at zero
+		 * Notes:    Thanks to Yekimov Denis for contributing the basis for this function!
+		 */
+		this.fnAddData = function( mData, bRedraw )
+		{
+			if ( mData.length === 0 )
+			{
+				return [];
+			}
+			
+			var aiReturn = [];
+			var iTest;
+			
+			/* Find settings from table node */
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			
+			/* Check if we want to add multiple rows or not */
+			if ( typeof mData[0] == "object" )
+			{
+				for ( var i=0 ; i<mData.length ; i++ )
+				{
+					iTest = _fnAddData( oSettings, mData[i] );
+					if ( iTest == -1 )
+					{
+						return aiReturn;
+					}
+					aiReturn.push( iTest );
+				}
+			}
+			else
+			{
+				iTest = _fnAddData( oSettings, mData );
+				if ( iTest == -1 )
+				{
+					return aiReturn;
+				}
+				aiReturn.push( iTest );
+			}
+			
+			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+			
+			if ( typeof bRedraw == 'undefined' || bRedraw )
+			{
+				_fnReDraw( oSettings );
+			}
+			return aiReturn;
+		};
+		
+		/*
+		 * Function: fnDeleteRow
+		 * Purpose:  Remove a row for the table
+		 * Returns:  array:aReturn - the row that was deleted
+		 * Inputs:   mixed:mTarget - 
+		 *             int: - index of aoData to be deleted, or
+		 *             node(TR): - TR element you want to delete
+		 *           function:fnCallBack - callback function - default null
+		 *           bool:bRedraw - redraw the table or not - default true
+		 */
+		this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw )
+		{
+			/* Find settings from table node */
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			var i, iAODataIndex;
+			
+			iAODataIndex = (typeof mTarget == 'object') ? 
+				_fnNodeToDataIndex(oSettings, mTarget) : mTarget;
+			
+			/* Return the data array from this row */
+			var oData = oSettings.aoData.splice( iAODataIndex, 1 );
+			
+			/* Remove the target row from the search array */
+			var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay );
+			oSettings.asDataSearch.splice( iDisplayIndex, 1 );
+			
+			/* Delete from the display arrays */
+			_fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex );
+			_fnDeleteIndex( oSettings.aiDisplay, iAODataIndex );
+			
+			/* If there is a user callback function - call it */
+			if ( typeof fnCallBack == "function" )
+			{
+				fnCallBack.call( this, oSettings, oData );
+			}
+			
+			/* Check for an 'overflow' they case for dislaying the table */
+			if ( oSettings._iDisplayStart >= oSettings.aiDisplay.length )
+			{
+				oSettings._iDisplayStart -= oSettings._iDisplayLength;
+				if ( oSettings._iDisplayStart < 0 )
+				{
+					oSettings._iDisplayStart = 0;
+				}
+			}
+			
+			if ( typeof bRedraw == 'undefined' || bRedraw )
+			{
+				_fnCalculateEnd( oSettings );
+				_fnDraw( oSettings );
+			}
+			
+			return oData;
+		};
+		
+		/*
+		 * Function: fnClearTable
+		 * Purpose:  Quickly and simply clear a table
+		 * Returns:  -
+		 * Inputs:   bool:bRedraw - redraw the table or not - default true
+		 * Notes:    Thanks to Yekimov Denis for contributing the basis for this function!
+		 */
+		this.fnClearTable = function( bRedraw )
+		{
+			/* Find settings from table node */
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			_fnClearTable( oSettings );
+			
+			if ( typeof bRedraw == 'undefined' || bRedraw )
+			{
+				_fnDraw( oSettings );
+			}
+		};
+		
+		/*
+		 * Function: fnOpen
+		 * Purpose:  Open a display row (append a row after the row in question)
+		 * Returns:  node:nNewRow - the row opened
+		 * Inputs:   node:nTr - the table row to 'open'
+		 *           string|node|jQuery:mHtml - the HTML to put into the row
+		 *           string:sClass - class to give the new TD cell
+		 */
+		this.fnOpen = function( nTr, mHtml, sClass )
+		{
+			/* Find settings from table node */
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			
+			/* the old open one if there is one */
+			this.fnClose( nTr );
+			
+			var nNewRow = document.createElement("tr");
+			var nNewCell = document.createElement("td");
+			nNewRow.appendChild( nNewCell );
+			nNewCell.className = sClass;
+			nNewCell.colSpan = _fnVisbleColumns( oSettings );
+
+			if( typeof mHtml.jquery != 'undefined' || typeof mHtml == "object" )
+			{
+				nNewCell.appendChild( mHtml );
+			}
+			else
+			{
+				nNewCell.innerHTML = mHtml;
+			}
+
+			/* If the nTr isn't on the page at the moment - then we don't insert at the moment */
+			var nTrs = $('tr', oSettings.nTBody);
+			if ( $.inArray(nTr, nTrs) != -1 )
+			{
+				$(nNewRow).insertAfter(nTr);
+			}
+			
+			oSettings.aoOpenRows.push( {
+				"nTr": nNewRow,
+				"nParent": nTr
+			} );
+			
+			return nNewRow;
+		};
+		
+		/*
+		 * Function: fnClose
+		 * Purpose:  Close a display row
+		 * Returns:  int: 0 (success) or 1 (failed)
+		 * Inputs:   node:nTr - the table row to 'close'
+		 */
+		this.fnClose = function( nTr )
+		{
+			/* Find settings from table node */
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			
+			for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
+			{
+				if ( oSettings.aoOpenRows[i].nParent == nTr )
+				{
+					var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
+					if ( nTrParent )
+					{
+						/* Remove it if it is currently on display */
+						nTrParent.removeChild( oSettings.aoOpenRows[i].nTr );
+					}
+					oSettings.aoOpenRows.splice( i, 1 );
+					return 0;
+				}
+			}
+			return 1;
+		};
+		
+		/*
+		 * Function: fnGetData
+		 * Purpose:  Return an array with the data which is used to make up the table
+		 * Returns:  array array string: 2d data array ([row][column]) or array string: 1d data array
+		 *           or string if both row and column are given
+		 * Inputs:   mixed:mRow - optional - if not present, then the full 2D array for the table 
+		 *             if given then:
+		 *               int: - return data object for aoData entry of this index
+		 *               node(TR): - return data object for this TR element
+		 *           int:iCol - optional - the column that you want the data of. This will take into
+		 *               account mDataProp and return the value DataTables uses for this column
+		 */
+		this.fnGetData = function( mRow, iCol )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			
+			if ( typeof mRow != 'undefined' )
+			{
+				var iRow = (typeof mRow == 'object') ? 
+					_fnNodeToDataIndex(oSettings, mRow) : mRow;
+				
+				if ( typeof iCol != 'undefined' )
+				{
+					return _fnGetCellData( oSettings, iRow, iCol, '' );
+				}
+				return (typeof oSettings.aoData[iRow] != 'undefined') ? 
+					oSettings.aoData[iRow]._aData : null;
+			}
+			return _fnGetDataMaster( oSettings );
+		};
+		
+		/*
+		 * Function: fnGetNodes
+		 * Purpose:  Return an array with the TR nodes used for drawing the table
+		 * Returns:  array node: TR elements
+		 *           or
+		 *           node (if iRow specified)
+		 * Inputs:   int:iRow - optional - if present then the array returned will be the node for
+		 *             the row with the index 'iRow'
+		 */
+		this.fnGetNodes = function( iRow )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			
+			if ( typeof iRow != 'undefined' )
+			{
+				return (typeof oSettings.aoData[iRow] != 'undefined') ? oSettings.aoData[iRow].nTr : null;
+			}
+			return _fnGetTrNodes( oSettings );
+		};
+		
+		/*
+		 * Function: fnGetPosition
+		 * Purpose:  Get the array indexes of a particular cell from it's DOM element
+		 * Returns:  int: - row index, or array[ int, int, int ]: - row index, column index (visible)
+		 *             and column index including hidden columns
+		 * Inputs:   node:nNode - this can either be a TR, TD or TH in the table's body, the return is
+		 *             dependent on this input
+		 */
+		this.fnGetPosition = function( nNode )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			var sNodeName = nNode.nodeName.toUpperCase();
+			
+			if ( sNodeName == "TR" )
+			{
+				return _fnNodeToDataIndex(oSettings, nNode);
+			}
+			else if ( sNodeName == "TD" || sNodeName == "TH" )
+			{
+				var iDataIndex = _fnNodeToDataIndex(oSettings, nNode.parentNode);
+				var anCells = _fnGetTdNodes( oSettings, iDataIndex );
+
+				for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
+				{
+					if ( anCells[i] == nNode )
+					{
+						return [ iDataIndex, _fnColumnIndexToVisible(oSettings, i ), i ];
+					}
+				}
+			}
+			return null;
+		};
+		
+		/*
+		 * Function: fnUpdate
+		 * Purpose:  Update a table cell or row - this method will accept either a single value to
+		 *             update the cell with, an array of values with one element for each column or
+		 *             an object in the same format as the original data source. The function is
+		 *             self-referencing in order to make the multi column updates easier.
+		 * Returns:  int: 0 okay, 1 error
+		 * Inputs:   object | array string | string:mData - data to update the cell/row with
+		 *           mixed:mRow - 
+		 *             int: - index of aoData to be updated, or
+		 *             node(TR): - TR element you want to update
+		 *           int:iColumn - the column to update - optional (not used of mData is an array or object)
+		 *           bool:bRedraw - redraw the table or not - default true
+		 *           bool:bAction - perform predraw actions or not (you will want this as 'true' if
+		 *             you have bRedraw as true) - default true
+		 */
+		this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			var iVisibleColumn, i, iLen, sDisplay;
+			var iRow = (typeof mRow == 'object') ? 
+				_fnNodeToDataIndex(oSettings, mRow) : mRow;
+			
+			if ( $.isArray(mData) && typeof mData == 'object' )
+			{
+				/* Array update - update the whole row */
+				oSettings.aoData[iRow]._aData = mData.slice();
+
+				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+				{
+					this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
+				}
+			}
+			else if ( mData !== null && typeof mData == 'object' )
+			{
+				/* Object update - update the whole row - assume the developer gets the object right */
+				oSettings.aoData[iRow]._aData = $.extend( true, {}, mData );
+
+				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+				{
+					this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
+				}
+			}
+			else
+			{
+				/* Individual cell update */
+				sDisplay = mData;
+				_fnSetCellData( oSettings, iRow, iColumn, sDisplay );
+				
+				if ( oSettings.aoColumns[iColumn].fnRender !== null )
+				{
+					sDisplay = oSettings.aoColumns[iColumn].fnRender( {
+						"iDataRow": iRow,
+						"iDataColumn": iColumn,
+						"aData": oSettings.aoData[iRow]._aData,
+						"oSettings": oSettings
+					} );
+					
+					if ( oSettings.aoColumns[iColumn].bUseRendered )
+					{
+						_fnSetCellData( oSettings, iRow, iColumn, sDisplay );
+					}
+				}
+				
+				if ( oSettings.aoData[iRow].nTr !== null )
+				{
+					/* Do the actual HTML update */
+					_fnGetTdNodes( oSettings, iRow )[iColumn].innerHTML = sDisplay;
+				}
+			}
+			
+			/* Modify the search index for this row (strictly this is likely not needed, since fnReDraw
+			 * will rebuild the search array - however, the redraw might be disabled by the user)
+			 */
+			var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay );
+			oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow( oSettings, 
+				_fnGetRowData( oSettings, iRow, 'filter' ) );
+			
+			/* Perform pre-draw actions */
+			if ( typeof bAction == 'undefined' || bAction )
+			{
+				_fnAdjustColumnSizing( oSettings );
+			}
+			
+			/* Redraw the table */
+			if ( typeof bRedraw == 'undefined' || bRedraw )
+			{
+				_fnReDraw( oSettings );
+			}
+			return 0;
+		};
+		
+		
+		/*
+		 * Function: fnShowColoumn
+		 * Purpose:  Show a particular column
+		 * Returns:  -
+		 * Inputs:   int:iCol - the column whose display should be changed
+		 *           bool:bShow - show (true) or hide (false) the column
+		 *           bool:bRedraw - redraw the table or not - default true
+		 */
+		this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			var i, iLen;
+			var iColumns = oSettings.aoColumns.length;
+			var nTd, nCell, anTrs, jqChildren, bAppend, iBefore;
+			
+			/* No point in doing anything if we are requesting what is already true */
+			if ( oSettings.aoColumns[iCol].bVisible == bShow )
+			{
+				return;
+			}
+			
+			/* Show the column */
+			if ( bShow )
+			{
+				var iInsert = 0;
+				for ( i=0 ; i<iCol ; i++ )
+				{
+					if ( oSettings.aoColumns[i].bVisible )
+					{
+						iInsert++;
+					}
+				}
+				
+				/* Need to decide if we should use appendChild or insertBefore */
+				bAppend = (iInsert >= _fnVisbleColumns( oSettings ));
+
+				/* Which coloumn should we be inserting before? */
+				if ( !bAppend )
+				{
+					for ( i=iCol ; i<iColumns ; i++ )
+					{
+						if ( oSettings.aoColumns[i].bVisible )
+						{
+							iBefore = i;
+							break;
+						}
+					}
+				}
+
+				for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+				{
+					if ( oSettings.aoData[i].nTr !== null )
+					{
+						if ( bAppend )
+						{
+							oSettings.aoData[i].nTr.appendChild( 
+								oSettings.aoData[i]._anHidden[iCol]
+							);
+						}
+						else
+						{
+							oSettings.aoData[i].nTr.insertBefore(
+								oSettings.aoData[i]._anHidden[iCol], 
+								_fnGetTdNodes( oSettings, i )[iBefore] );
+						}
+					}
+				}
+			}
+			else
+			{
+				/* Remove a column from display */
+				for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+				{
+					if ( oSettings.aoData[i].nTr !== null )
+					{
+						nTd = _fnGetTdNodes( oSettings, i )[iCol];
+						oSettings.aoData[i]._anHidden[iCol] = nTd;
+						nTd.parentNode.removeChild( nTd );
+					}
+				}
+			}
+
+			/* Clear to set the visible flag */
+			oSettings.aoColumns[iCol].bVisible = bShow;
+
+			/* Redraw the header and footer based on the new column visibility */
+			_fnDrawHead( oSettings, oSettings.aoHeader );
+			if ( oSettings.nTFoot )
+			{
+				_fnDrawHead( oSettings, oSettings.aoFooter );
+			}
+			
+			/* If there are any 'open' rows, then we need to alter the colspan for this col change */
+			for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ )
+			{
+				oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings );
+			}
+			
+			/* Do a redraw incase anything depending on the table columns needs it 
+			 * (built-in: scrolling) 
+			 */
+			if ( typeof bRedraw == 'undefined' || bRedraw )
+			{
+				_fnAdjustColumnSizing( oSettings );
+				_fnDraw( oSettings );
+			}
+			
+			_fnSaveState( oSettings );
+		};
+		
+		/*
+		 * Function: fnPageChange
+		 * Purpose:  Change the pagination
+		 * Returns:  -
+		 * Inputs:   string:sAction - paging action to take: "first", "previous", "next" or "last"
+		 *           bool:bRedraw - redraw the table or not - optional - default true
+		 */
+		this.fnPageChange = function ( sAction, bRedraw )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			_fnPageChange( oSettings, sAction );
+			_fnCalculateEnd( oSettings );
+			
+			if ( typeof bRedraw == 'undefined' || bRedraw )
+			{
+				_fnDraw( oSettings );
+			}
+		};
+		
+		/*
+		 * Function: fnDestroy
+		 * Purpose:  Destructor for the DataTable
+		 * Returns:  -
+		 * Inputs:   -
+		 */
+		this.fnDestroy = function ( )
+		{
+			var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+			var nOrig = oSettings.nTableWrapper.parentNode;
+			var nBody = oSettings.nTBody;
+			var i, iLen;
+			
+			/* Flag to note that the table is currently being destoryed - no action should be taken */
+			oSettings.bDestroying = true;
+			
+			/* Restore hidden columns */
+			for ( i=0, iLen=oSettings.aoDestroyCallback.length ; i<iLen ; i++ ) {
+				oSettings.aoDestroyCallback[i].fn();
+			}
+			
+			/* Restore hidden columns */
+			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+			{
+				if ( oSettings.aoColumns[i].bVisible === false )
+				{
+					this.fnSetColumnVis( i, true );
+				}
+			}
+			
+			/* Blitz all DT events */
+			$(oSettings.nTableWrapper).find('*').andSelf().unbind('.DT');
+			
+			/* If there is an 'empty' indicator row, remove it */
+			$('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove();
+			
+			/* When scrolling we had to break the table up - restore it */
+			if ( oSettings.nTable != oSettings.nTHead.parentNode )
+			{
+				$(oSettings.nTable).children('thead').remove();
+				oSettings.nTable.appendChild( oSettings.nTHead );
+			}
+			
+			if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode )
+			{
+				$(oSettings.nTable).children('tfoot').remove();
+				oSettings.nTable.appendChild( oSettings.nTFoot );
+			}
+			
+			/* Remove the DataTables generated nodes, events and classes */
+			oSettings.nTable.parentNode.removeChild( oSettings.nTable );
+			$(oSettings.nTableWrapper).remove();
+			
+			oSettings.aaSorting = [];
+			oSettings.aaSortingFixed = [];
+			_fnSortingClasses( oSettings );
+			
+			$(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripeClasses.join(' ') );
+			
+			if ( !oSettings.bJUI )
+			{
+				$('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable,
+					_oExt.oStdClasses.sSortableAsc,
+					_oExt.oStdClasses.sSortableDesc,
+					_oExt.oStdClasses.sSortableNone ].join(' ')
+				);
+			}
+			else
+			{
+				$('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable,
+					_oExt.oJUIClasses.sSortableAsc,
+					_oExt.oJUIClasses.sSortableDesc,
+					_oExt.oJUIClasses.sSortableNone ].join(' ')
+				);
+				$('th span.'+_oExt.oJUIClasses.sSortIcon, oSettings.nTHead).remove();
+
+				$('th', oSettings.nTHead).each( function () {
+					var jqWrapper = $('div.'+_oExt.oJUIClasses.sSortJUIWrapper, this);
+					var kids = jqWrapper.contents();
+					$(this).append( kids );
+					jqWrapper.remove();
+				} );
+			}
+			
+			/* Add the TR elements back into the table in their original order */
+			if ( oSettings.nTableReinsertBefore )
+			{
+				nOrig.insertBefore( oSettings.nTable, oSettings.nTableReinsertBefore );
+			}
+			else
+			{
+				nOrig.appendChild( oSettings.nTable );
+			}
+
+			for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+			{
+				if ( oSettings.aoData[i].nTr !== null )
+				{
+					nBody.appendChild( oSettings.aoData[i].nTr );
+				}
+			}
+			
+			/* Restore the width of the original table */
+			if ( oSettings.oFeatures.bAutoWidth === true )
+			{
+			  oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
+			}
+			
+			/* If the were originally odd/even type classes - then we add them back here. Note
+			 * this is not fool proof (for example if not all rows as odd/even classes - but 
+			 * it's a good effort without getting carried away
+			 */
+			$(nBody).children('tr:even').addClass( oSettings.asDestroyStripes[0] );
+			$(nBody).children('tr:odd').addClass( oSettings.asDestroyStripes[1] );
+			
+			/* Remove the settings object from the settings array */
+			for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ )
+			{
+				if ( _aoSettings[i] == oSettings )
+				{
+					_aoSettings.splice( i, 1 );
+				}
+			}
+			
+			/* End it all */
+			oSettings = null;
+		};
+		
+		/*
+		 * Function: fnAdjustColumnSizing
+		 * Purpose:  Update table sizing based on content. This would most likely be used for scrolling
+		 *   and will typically need a redraw after it.
+		 * Returns:  -
+		 * Inputs:   bool:bRedraw - redraw the table or not, you will typically want to - default true
+		 */
+		this.fnAdjustColumnSizing = function ( bRedraw )
+		{
+			var oSettings = _fnSettingsFromNode(this[_oExt.iApiIndex]);
+			_fnAdjustColumnSizing( oSettings );
+			
+			if ( typeof bRedraw == 'undefined' || bRedraw )
+			{
+				this.fnDraw( false );
+			}
+			else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
+			{
+				/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
+				this.oApi._fnScrollDraw(oSettings);
+			}
+		};
+		
+		/*
+		 * Plugin API functions
+		 * 
+		 * This call will add the functions which are defined in _oExt.oApi to the
+		 * DataTables object, providing a rather nice way to allow plug-in API functions. Note that
+		 * this is done here, so that API function can actually override the built in API functions if
+		 * required for a particular purpose.
+		 */
+		
+		/*
+		 * Function: _fnExternApiFunc
+		 * Purpose:  Create a wrapper function for exporting an internal func to an external API func
+		 * Returns:  function: - wrapped function
+		 * Inputs:   string:sFunc - API function name
+		 */
+		function _fnExternApiFunc (sFunc)
+		{
+			return function() {
+					var aArgs = [_fnSettingsFromNode(this[_oExt.iApiIndex])].concat( 
+						Array.prototype.slice.call(arguments) );
+					return _oExt.oApi[sFunc].apply( this, aArgs );
+				};
+		}
+		
+		for ( var sFunc in _oExt.oApi )
+		{
+			if ( sFunc )
+			{
+				/*
+				 * Function: anon
+				 * Purpose:  Wrap the plug-in API functions in order to provide the settings as 1st arg 
+				 *   and execute in this scope
+				 * Returns:  -
+				 * Inputs:   -
+				 */
+				this[sFunc] = _fnExternApiFunc(sFunc);
+			}
+		}
+		
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Local functions
+		 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Initialisation
+		 */
+		
+		/*
+		 * Function: _fnInitialise
+		 * Purpose:  Draw the table for the first time, adding all required features
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnInitialise ( oSettings )
+		{
+			var i, iLen, iAjaxStart=oSettings.iInitDisplayStart;
+			
+			/* Ensure that the table data is fully initialised */
+			if ( oSettings.bInitialised === false )
+			{
+				setTimeout( function(){ _fnInitialise( oSettings ); }, 200 );
+				return;
+			}
+			
+			/* Show the display HTML options */
+			_fnAddOptionsHtml( oSettings );
+			
+			/* Build and draw the header / footer for the table */
+			_fnBuildHead( oSettings );
+			_fnDrawHead( oSettings, oSettings.aoHeader );
+			if ( oSettings.nTFoot )
+			{
+				_fnDrawHead( oSettings, oSettings.aoFooter );
+			}
+
+			/* Okay to show that something is going on now */
+			_fnProcessingDisplay( oSettings, true );
+			
+			/* Calculate sizes for columns */
+			if ( oSettings.oFeatures.bAutoWidth )
+			{
+				_fnCalculateColumnWidths( oSettings );
+			}
+			
+			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+			{
+				if ( oSettings.aoColumns[i].sWidth !== null )
+				{
+					oSettings.aoColumns[i].nTh.style.width = _fnStringToCss( oSettings.aoColumns[i].sWidth );
+				}
+			}
+			
+			/* If there is default sorting required - let's do it. The sort function will do the
+			 * drawing for us. Otherwise we draw the table regardless of the Ajax source - this allows
+			 * the table to look initialised for Ajax sourcing data (show 'loading' message possibly)
+			 */
+			if ( oSettings.oFeatures.bSort )
+			{
+				_fnSort( oSettings );
+			}
+			else if ( oSettings.oFeatures.bFilter )
+			{
+				_fnFilterComplete( oSettings, oSettings.oPreviousSearch );
+			}
+			else
+			{
+				oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+				_fnCalculateEnd( oSettings );
+				_fnDraw( oSettings );
+			}
+			
+			/* if there is an ajax source load the data */
+			if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
+			{
+				var aoData = [];
+				_fnServerParams( oSettings, aoData );
+				oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData, function(json) {
+					var aData = json;
+					if ( oSettings.sAjaxDataProp !== "" )
+					{
+						var fnDataSrc = _fnGetObjectDataFn( oSettings.sAjaxDataProp );
+						aData = fnDataSrc( json );
+					}
+
+					/* Got the data - add it to the table */
+					for ( i=0 ; i<aData.length ; i++ )
+					{
+						_fnAddData( oSettings, aData[i] );
+					}
+					
+					/* Reset the init display for cookie saving. We've already done a filter, and
+					 * therefore cleared it before. So we need to make it appear 'fresh'
+					 */
+					oSettings.iInitDisplayStart = iAjaxStart;
+					
+					if ( oSettings.oFeatures.bSort )
+					{
+						_fnSort( oSettings );
+					}
+					else
+					{
+						oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+						_fnCalculateEnd( oSettings );
+						_fnDraw( oSettings );
+					}
+					
+					_fnProcessingDisplay( oSettings, false );
+					_fnInitComplete( oSettings, json );
+				}, oSettings );
+				return;
+			}
+			
+			/* Server-side processing initialisation complete is done at the end of _fnDraw */
+			if ( !oSettings.oFeatures.bServerSide )
+			{
+				_fnProcessingDisplay( oSettings, false );
+				_fnInitComplete( oSettings );
+			}
+		}
+		
+		/*
+		 * Function: _fnInitComplete
+		 * Purpose:  Draw the table for the first time, adding all required features
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnInitComplete ( oSettings, json )
+		{
+			oSettings._bInitComplete = true;
+			if ( typeof oSettings.fnInitComplete == 'function' )
+			{
+				if ( typeof json != 'undefined' )
+				{
+					oSettings.fnInitComplete.call( oSettings.oInstance, oSettings, json );
+				}
+				else
+				{
+					oSettings.fnInitComplete.call( oSettings.oInstance, oSettings );
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnLanguageProcess
+		 * Purpose:  Copy language variables from remote object to a local one
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           object:oLanguage - Language information
+		 *           bool:bInit - init once complete
+		 */
+		function _fnLanguageProcess( oSettings, oLanguage, bInit )
+		{
+			oSettings.oLanguage = $.extend( true, oSettings.oLanguage, oLanguage );
+			
+			/* Backwards compatibility - if there is no sEmptyTable given, then use the same as
+			 * sZeroRecords - assuming that is given.
+			 */
+			if ( typeof oLanguage.sEmptyTable == 'undefined' && 
+			     typeof oLanguage.sZeroRecords != 'undefined' )
+			{
+				_fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' );
+			}
+
+			/* Likewise with loading records */
+			if ( typeof oLanguage.sLoadingRecords == 'undefined' && 
+			     typeof oLanguage.sZeroRecords != 'undefined' )
+			{
+				_fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords', 'sLoadingRecords' );
+			}
+			
+			if ( bInit )
+			{
+				_fnInitialise( oSettings );
+			}
+		}
+		
+		/*
+		 * Function: _fnAddColumn
+		 * Purpose:  Add a column to the list used for the table with default values
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           node:nTh - the th element for this column
+		 */
+		function _fnAddColumn( oSettings, nTh )
+		{
+			var iCol = oSettings.aoColumns.length;
+			var oCol = {
+				"sType": null,
+				"_bAutoType": true,
+				"bVisible": true,
+				"bSearchable": true,
+				"bSortable": true,
+				"asSorting": [ 'asc', 'desc' ],
+				"sSortingClass": oSettings.oClasses.sSortable,
+				"sSortingClassJUI": oSettings.oClasses.sSortJUI,
+				"sTitle": nTh ? nTh.innerHTML : '',
+				"sName": '',
+				"sWidth": null,
+				"sWidthOrig": null,
+				"sClass": null,
+				"fnRender": null,
+				"bUseRendered": true,
+				"iDataSort": iCol,
+				"mDataProp": iCol,
+				"fnGetData": null,
+				"fnSetData": null,
+				"sSortDataType": 'std',
+				"sDefaultContent": null,
+				"sContentPadding": "",
+				"nTh": nTh ? nTh : document.createElement('th'),
+				"nTf": null
+			};
+			oSettings.aoColumns.push( oCol );
+			
+			/* Add a column specific filter */
+			if ( typeof oSettings.aoPreSearchCols[ iCol ] == 'undefined' ||
+			     oSettings.aoPreSearchCols[ iCol ] === null )
+			{
+				oSettings.aoPreSearchCols[ iCol ] = {
+					"sSearch": "",
+					"bRegex": false,
+					"bSmart": true
+				};
+			}
+			else
+			{
+				/* Don't require that the user must specify bRegex and / or bSmart */
+				if ( typeof oSettings.aoPreSearchCols[ iCol ].bRegex == 'undefined' )
+				{
+					oSettings.aoPreSearchCols[ iCol ].bRegex = true;
+				}
+				
+				if ( typeof oSettings.aoPreSearchCols[ iCol ].bSmart == 'undefined' )
+				{
+					oSettings.aoPreSearchCols[ iCol ].bSmart = true;
+				}
+			}
+			
+			/* Use the column options function to initialise classes etc */
+			_fnColumnOptions( oSettings, iCol, null );
+		}
+		
+		/*
+		 * Function: _fnColumnOptions
+		 * Purpose:  Apply options for a column
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iCol - column index to consider
+		 *           object:oOptions - object with sType, bVisible and bSearchable
+		 */
+		function _fnColumnOptions( oSettings, iCol, oOptions )
+		{
+			var oCol = oSettings.aoColumns[ iCol ];
+			
+			/* User specified column options */
+			if ( typeof oOptions != 'undefined' && oOptions !== null )
+			{
+				if ( typeof oOptions.sType != 'undefined' )
+				{
+					oCol.sType = oOptions.sType;
+					oCol._bAutoType = false;
+				}
+				
+				_fnMap( oCol, oOptions, "bVisible" );
+				_fnMap( oCol, oOptions, "bSearchable" );
+				_fnMap( oCol, oOptions, "bSortable" );
+				_fnMap( oCol, oOptions, "sTitle" );
+				_fnMap( oCol, oOptions, "sName" );
+				_fnMap( oCol, oOptions, "sWidth" );
+				_fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
+				_fnMap( oCol, oOptions, "sClass" );
+				_fnMap( oCol, oOptions, "fnRender" );
+				_fnMap( oCol, oOptions, "bUseRendered" );
+				_fnMap( oCol, oOptions, "iDataSort" );
+				_fnMap( oCol, oOptions, "mDataProp" );
+				_fnMap( oCol, oOptions, "asSorting" );
+				_fnMap( oCol, oOptions, "sSortDataType" );
+				_fnMap( oCol, oOptions, "sDefaultContent" );
+				_fnMap( oCol, oOptions, "sContentPadding" );
+			}
+
+			/* Cache the data get and set functions for speed */
+			oCol.fnGetData = _fnGetObjectDataFn( oCol.mDataProp );
+			oCol.fnSetData = _fnSetObjectDataFn( oCol.mDataProp );
+			
+			/* Feature sorting overrides column specific when off */
+			if ( !oSettings.oFeatures.bSort )
+			{
+				oCol.bSortable = false;
+			}
+			
+			/* Check that the class assignment is correct for sorting */
+			if ( !oCol.bSortable ||
+				 ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) )
+			{
+				oCol.sSortingClass = oSettings.oClasses.sSortableNone;
+				oCol.sSortingClassJUI = "";
+			}
+			else if ( oCol.bSortable ||
+			          ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) )
+			{
+			  oCol.sSortingClass = oSettings.oClasses.sSortable;
+			  oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI;
+			}
+			else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 )
+			{
+				oCol.sSortingClass = oSettings.oClasses.sSortableAsc;
+				oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed;
+			}
+			else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 )
+			{
+				oCol.sSortingClass = oSettings.oClasses.sSortableDesc;
+				oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed;
+			}
+		}
+		
+		/*
+		 * Function: _fnAddData
+		 * Purpose:  Add a data array to the table, creating DOM node etc
+		 * Returns:  int: - >=0 if successful (index of new aoData entry), -1 if failed
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           array:aData - data array to be added
+		 * Notes:    There are two basic methods for DataTables to get data to display - a JS array
+		 *   (which is dealt with by this function), and the DOM, which has it's own optimised
+		 *   function (_fnGatherData). Be careful to make the same changes here as there and vice-versa
+		 */
+		function _fnAddData ( oSettings, aDataSupplied )
+		{
+			var oCol;
+			
+			/* Take an independent copy of the data source so we can bash it about as we wish */
+			var aDataIn = ($.isArray(aDataSupplied)) ?
+				aDataSupplied.slice() :
+				$.extend( true, {}, aDataSupplied );
+			
+			/* Create the object for storing information about this new row */
+			var iRow = oSettings.aoData.length;
+			var oData = {
+				"nTr": null,
+				"_iId": oSettings.iNextId++,
+				"_aData": aDataIn,
+				"_anHidden": [],
+				"_sRowStripe": ""
+			};
+			oSettings.aoData.push( oData );
+
+			/* Create the cells */
+			var nTd, sThisType;
+			for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+			{
+				oCol = oSettings.aoColumns[i];
+
+				/* Use rendered data for filtering/sorting */
+				if ( typeof oCol.fnRender == 'function' && oCol.bUseRendered && oCol.mDataProp !== null )
+				{
+					_fnSetCellData( oSettings, iRow, i, oCol.fnRender( {
+						"iDataRow": iRow,
+						"iDataColumn": i,
+						"aData": oData._aData,
+						"oSettings": oSettings
+					} ) );
+				}
+				
+				/* See if we should auto-detect the column type */
+				if ( oCol._bAutoType && oCol.sType != 'string' )
+				{
+					/* Attempt to auto detect the type - same as _fnGatherData() */
+					var sVarType = _fnGetCellData( oSettings, iRow, i, 'type' );
+					if ( sVarType !== null && sVarType !== '' )
+					{
+						sThisType = _fnDetectType( sVarType );
+						if ( oCol.sType === null )
+						{
+							oCol.sType = sThisType;
+						}
+						else if ( oCol.sType != sThisType && oCol.sType != "html" )
+						{
+							/* String is always the 'fallback' option */
+							oCol.sType = 'string';
+						}
+					}
+				}
+			}
+			
+			/* Add to the display array */
+			oSettings.aiDisplayMaster.push( iRow );
+
+			/* Create the DOM imformation */
+			if ( !oSettings.oFeatures.bDeferRender )
+			{
+				_fnCreateTr( oSettings, iRow );
+			}
+
+			return iRow;
+		}
+		
+		/*
+		 * Function: _fnCreateTr
+		 * Purpose:  Create a new TR element (and it's TD children) for a row
+		 * Returns:  void
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iRow - Row to consider
+		 */
+		function _fnCreateTr ( oSettings, iRow )
+		{
+			var oData = oSettings.aoData[iRow];
+			var nTd;
+
+			if ( oData.nTr === null )
+			{
+				oData.nTr = document.createElement('tr');
+
+				/* Special parameters can be given by the data source to be used on the row */
+				if ( typeof oData._aData.DT_RowId != 'undefined' )
+				{
+					oData.nTr.setAttribute( 'id', oData._aData.DT_RowId );
+				}
+
+				if ( typeof oData._aData.DT_RowClass != 'undefined' )
+				{
+					$(oData.nTr).addClass( oData._aData.DT_RowClass );
+				}
+
+				/* Process each column */
+				for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+				{
+					var oCol = oSettings.aoColumns[i];
+					nTd = document.createElement('td');
+
+					/* Render if needed - if bUseRendered is true then we already have the rendered
+					 * value in the data source - so can just use that
+					 */
+					if ( typeof oCol.fnRender == 'function' && (!oCol.bUseRendered || oCol.mDataProp === null) )
+					{
+						nTd.innerHTML = oCol.fnRender( {
+							"iDataRow": iRow,
+							"iDataColumn": i,
+							"aData": oData._aData,
+							"oSettings": oSettings
+						} );
+					}
+					else
+					{
+						nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
+					}
+				
+					/* Add user defined class */
+					if ( oCol.sClass !== null )
+					{
+						nTd.className = oCol.sClass;
+					}
+					
+					if ( oCol.bVisible )
+					{
+						oData.nTr.appendChild( nTd );
+						oData._anHidden[i] = null;
+					}
+					else
+					{
+						oData._anHidden[i] = nTd;
+					}
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnGatherData
+		 * Purpose:  Read in the data from the target table from the DOM
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 * Notes:    This is a optimised version of _fnAddData (more or less) for reading information
+		 *   from the DOM. The basic actions must be identical in the two functions.
+		 */
+		function _fnGatherData( oSettings )
+		{
+			var iLoop, i, iLen, j, jLen, jInner,
+			 	nTds, nTrs, nTd, aLocalData, iThisIndex,
+				iRow, iRows, iColumn, iColumns, sNodeName;
+			
+			/*
+			 * Process by row first
+			 * Add the data object for the whole table - storing the tr node. Note - no point in getting
+			 * DOM based data if we are going to go and replace it with Ajax source data.
+			 */
+			if ( oSettings.bDeferLoading || oSettings.sAjaxSource === null )
+			{
+				nTrs = oSettings.nTBody.childNodes;
+				for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
+				{
+					if ( nTrs[i].nodeName.toUpperCase() == "TR" )
+					{
+						iThisIndex = oSettings.aoData.length;
+						oSettings.aoData.push( {
+							"nTr": nTrs[i],
+							"_iId": oSettings.iNextId++,
+							"_aData": [],
+							"_anHidden": [],
+							"_sRowStripe": ''
+						} );
+						
+						oSettings.aiDisplayMaster.push( iThisIndex );
+						nTds = nTrs[i].childNodes;
+						jInner = 0;
+						
+						for ( j=0, jLen=nTds.length ; j<jLen ; j++ )
+						{
+							sNodeName = nTds[j].nodeName.toUpperCase();
+							if ( sNodeName == "TD" || sNodeName == "TH" )
+							{
+								_fnSetCellData( oSettings, iThisIndex, jInner, $.trim(nTds[j].innerHTML) );
+								jInner++;
+							}
+						}
+					}
+				}
+			}
+			
+			/* Gather in the TD elements of the Table - note that this is basically the same as
+			 * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
+			 * setup!
+			 */
+			nTrs = _fnGetTrNodes( oSettings );
+			nTds = [];
+			for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
+			{
+				for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ )
+				{
+					nTd = nTrs[i].childNodes[j];
+					sNodeName = nTd.nodeName.toUpperCase();
+					if ( sNodeName == "TD" || sNodeName == "TH" )
+					{
+						nTds.push( nTd );
+					}
+				}
+			}
+			
+			/* Sanity check */
+			if ( nTds.length != nTrs.length * oSettings.aoColumns.length )
+			{
+				_fnLog( oSettings, 1, "Unexpected number of TD elements. Expected "+
+					(nTrs.length * oSettings.aoColumns.length)+" and got "+nTds.length+". DataTables does "+
+					"not support rowspan / colspan in the table body, and there must be one cell for each "+
+					"row/column combination." );
+			}
+			
+			/* Now process by column */
+			for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
+			{
+				/* Get the title of the column - unless there is a user set one */
+				if ( oSettings.aoColumns[iColumn].sTitle === null )
+				{
+					oSettings.aoColumns[iColumn].sTitle = oSettings.aoColumns[iColumn].nTh.innerHTML;
+				}
+				
+				var
+					bAutoType = oSettings.aoColumns[iColumn]._bAutoType,
+					bRender = typeof oSettings.aoColumns[iColumn].fnRender == 'function',
+					bClass = oSettings.aoColumns[iColumn].sClass !== null,
+					bVisible = oSettings.aoColumns[iColumn].bVisible,
+					nCell, sThisType, sRendered, sValType;
+				
+				/* A single loop to rule them all (and be more efficient) */
+				if ( bAutoType || bRender || bClass || !bVisible )
+				{
+					for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ )
+					{
+						nCell = nTds[ (iRow*iColumns) + iColumn ];
+						
+						/* Type detection */
+						if ( bAutoType && oSettings.aoColumns[iColumn].sType != 'string' )
+						{
+							sValType = _fnGetCellData( oSettings, iRow, iColumn, 'type' );
+							if ( sValType !== '' )
+							{
+								sThisType = _fnDetectType( sValType );
+								if ( oSettings.aoColumns[iColumn].sType === null )
+								{
+									oSettings.aoColumns[iColumn].sType = sThisType;
+								}
+								else if ( oSettings.aoColumns[iColumn].sType != sThisType && 
+								          oSettings.aoColumns[iColumn].sType != "html" )
+								{
+									/* String is always the 'fallback' option */
+									oSettings.aoColumns[iColumn].sType = 'string';
+								}
+							}
+						}
+						
+						/* Rendering */
+						if ( bRender )
+						{
+							sRendered = oSettings.aoColumns[iColumn].fnRender( {
+									"iDataRow": iRow,
+									"iDataColumn": iColumn,
+									"aData": oSettings.aoData[iRow]._aData,
+									"oSettings": oSettings
+								} );
+							nCell.innerHTML = sRendered;
+							if ( oSettings.aoColumns[iColumn].bUseRendered )
+							{
+								/* Use the rendered data for filtering/sorting */
+								_fnSetCellData( oSettings, iRow, iColumn, sRendered );
+							}
+						}
+						
+						/* Classes */
+						if ( bClass )
+						{
+							nCell.className += ' '+oSettings.aoColumns[iColumn].sClass;
+						}
+						
+						/* Column visability */
+						if ( !bVisible )
+						{
+							oSettings.aoData[iRow]._anHidden[iColumn] = nCell;
+							nCell.parentNode.removeChild( nCell );
+						}
+						else
+						{
+							oSettings.aoData[iRow]._anHidden[iColumn] = null;
+						}
+					}
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnBuildHead
+		 * Purpose:  Create the HTML header for the table
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnBuildHead( oSettings )
+		{
+			var i, nTh, iLen, j, jLen;
+			var anTr = oSettings.nTHead.getElementsByTagName('tr');
+			var iThs = oSettings.nTHead.getElementsByTagName('th').length;
+			var iCorrector = 0;
+			var jqChildren;
+			
+			/* If there is a header in place - then use it - otherwise it's going to get nuked... */
+			if ( iThs !== 0 )
+			{
+				/* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */
+				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+				{
+					nTh = oSettings.aoColumns[i].nTh;
+					
+					if ( oSettings.aoColumns[i].sClass !== null )
+					{
+						$(nTh).addClass( oSettings.aoColumns[i].sClass );
+					}
+					
+					/* Set the title of the column if it is user defined (not what was auto detected) */
+					if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML )
+					{
+						nTh.innerHTML = oSettings.aoColumns[i].sTitle;
+					}
+				}
+			}
+			else
+			{
+				/* We don't have a header in the DOM - so we are going to have to create one */
+				var nTr = document.createElement( "tr" );
+				
+				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+				{
+					nTh = oSettings.aoColumns[i].nTh;
+					nTh.innerHTML = oSettings.aoColumns[i].sTitle;
+					
+					if ( oSettings.aoColumns[i].sClass !== null )
+					{
+						$(nTh).addClass( oSettings.aoColumns[i].sClass );
+					}
+					
+					nTr.appendChild( nTh );
+				}
+				$(oSettings.nTHead).html( '' )[0].appendChild( nTr );
+				_fnDetectHeader( oSettings.aoHeader, oSettings.nTHead );
+			}
+			
+			/* Add the extra markup needed by jQuery UI's themes */
+			if ( oSettings.bJUI )
+			{
+				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+				{
+					nTh = oSettings.aoColumns[i].nTh;
+					
+					var nDiv = document.createElement('div');
+					nDiv.className = oSettings.oClasses.sSortJUIWrapper;
+					$(nTh).contents().appendTo(nDiv);
+					
+					var nSpan = document.createElement('span');
+					nSpan.className = oSettings.oClasses.sSortIcon;
+					nDiv.appendChild( nSpan );
+					nTh.appendChild( nDiv );
+				}
+			}
+			
+			/* Add sort listener */
+			var fnNoSelect = function (e) {
+				this.onselectstart = function() { return false; };
+				return false;
+			};
+			
+			if ( oSettings.oFeatures.bSort )
+			{
+				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+				{
+					if ( oSettings.aoColumns[i].bSortable !== false )
+					{
+						_fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
+						
+						/* Take the brutal approach to cancelling text selection in header */
+						$(oSettings.aoColumns[i].nTh).bind( 'mousedown.DT', fnNoSelect );
+					}
+					else
+					{
+						$(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone );
+					}
+				}
+			}
+			
+			/* Deal with the footer - add classes if required */
+			if ( oSettings.oClasses.sFooterTH !== "" )
+			{
+				$(oSettings.nTFoot).children('tr').children('th').addClass( oSettings.oClasses.sFooterTH );
+			}
+			
+			/* Cache the footer elements */
+			if ( oSettings.nTFoot !== null )
+			{
+				var anCells = _fnGetUniqueThs( oSettings, null, oSettings.aoFooter );
+				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+				{
+					if ( typeof anCells[i] != 'undefined' )
+					{
+						oSettings.aoColumns[i].nTf = anCells[i];
+					}
+				}
+			}
+		}
+
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Drawing functions
+		 */
+		
+		/*
+		 * Function: _fnDrawHead
+		 * Purpose:  Draw the header (or footer) element based on the column visibility states. The
+		 *           methodology here is to use the layout array from _fnDetectHeader, modified for
+		 *           the instantaneous column visibility, to construct the new layout. The grid is
+		 *           traversed over cell at a time in a rows x columns grid fashion, although each 
+		 *           cell insert can cover multiple elements in the grid - which is tracks using the
+		 *           aApplied array. Cell inserts in the grid will only occur where there isn't
+		 *           already a cell in that position.
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           array objects:aoSource - Layout array from _fnDetectHeader
+		 *           boolean:bIncludeHidden - If true then include the hidden columns in the calc, 
+		 *             - optional: default false
+		 */
+		function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
+		{
+			var i, iLen, j, jLen, k, kLen;
+			var aoLocal = [];
+			var aApplied = [];
+			var iColumns = oSettings.aoColumns.length;
+			var iRowspan, iColspan;
+
+			if ( typeof bIncludeHidden == 'undefined' )
+			{
+				bIncludeHidden = false;
+			}
+
+			/* Make a copy of the master layout array, but without the visible columns in it */
+			for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
+			{
+				aoLocal[i] = aoSource[i].slice();
+				aoLocal[i].nTr = aoSource[i].nTr;
+
+				/* Remove any columns which are currently hidden */
+				for ( j=iColumns-1 ; j>=0 ; j-- )
+				{
+					if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
+					{
+						aoLocal[i].splice( j, 1 );
+					}
+				}
+
+				/* Prep the applied array - it needs an element for each row */
+				aApplied.push( [] );
+			}
+
+			for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
+			{
+				/* All cells are going to be replaced, so empty out the row */
+				if ( aoLocal[i].nTr )
+				{
+					for ( k=0, kLen=aoLocal[i].nTr.childNodes.length ; k<kLen ; k++ )
+					{
+						aoLocal[i].nTr.removeChild( aoLocal[i].nTr.childNodes[0] );
+					}
+				}
+
+				for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
+				{
+					iRowspan = 1;
+					iColspan = 1;
+
+					/* Check to see if there is already a cell (row/colspan) covering our target
+					 * insert point. If there is, then there is nothing to do.
+					 */
+					if ( typeof aApplied[i][j] == 'undefined' )
+					{
+						aoLocal[i].nTr.appendChild( aoLocal[i][j].cell );
+						aApplied[i][j] = 1;
+
+						/* Expand the cell to cover as many rows as needed */
+						while ( typeof aoLocal[i+iRowspan] != 'undefined' &&
+						        aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
+						{
+							aApplied[i+iRowspan][j] = 1;
+							iRowspan++;
+						}
+
+						/* Expand the cell to cover as many columns as needed */
+						while ( typeof aoLocal[i][j+iColspan] != 'undefined' &&
+						        aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
+						{
+							/* Must update the applied array over the rows for the columns */
+							for ( k=0 ; k<iRowspan ; k++ )
+							{
+								aApplied[i+k][j+iColspan] = 1;
+							}
+							iColspan++;
+						}
+
+						/* Do the actual expansion in the DOM */
+						aoLocal[i][j].cell.rowSpan = iRowspan;
+						aoLocal[i][j].cell.colSpan = iColspan;
+					}
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnDraw
+		 * Purpose:  Insert the required TR nodes into the table for display
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnDraw( oSettings )
+		{
+			var i, iLen;
+			var anRows = [];
+			var iRowCount = 0;
+			var bRowError = false;
+			var iStripes = oSettings.asStripeClasses.length;
+			var iOpenRows = oSettings.aoOpenRows.length;
+			
+			/* Provide a pre-callback function which can be used to cancel the draw is false is returned */
+			if ( oSettings.fnPreDrawCallback !== null &&
+			     oSettings.fnPreDrawCallback.call( oSettings.oInstance, oSettings ) === false )
+			{
+			     return;
+			}
+			
+			oSettings.bDrawing = true;
+			
+			/* Check and see if we have an initial draw position from state saving */
+			if ( typeof oSettings.iInitDisplayStart != 'undefined' && oSettings.iInitDisplayStart != -1 )
+			{
+				if ( oSettings.oFeatures.bServerSide )
+				{
+					oSettings._iDisplayStart = oSettings.iInitDisplayStart;
+				}
+				else
+				{
+					oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ?
+						0 : oSettings.iInitDisplayStart;
+				}
+				oSettings.iInitDisplayStart = -1;
+				_fnCalculateEnd( oSettings );
+			}
+			
+			/* Server-side processing draw intercept */
+			if ( oSettings.bDeferLoading )
+			{
+				oSettings.bDeferLoading = false;
+				oSettings.iDraw++;
+			}
+			else if ( !oSettings.oFeatures.bServerSide )
+			{
+				oSettings.iDraw++;
+			}
+			else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
+			{
+				return;
+			}
+			
+			if ( oSettings.aiDisplay.length !== 0 )
+			{
+				var iStart = oSettings._iDisplayStart;
+				var iEnd = oSettings._iDisplayEnd;
+				
+				if ( oSettings.oFeatures.bServerSide )
+				{
+					iStart = 0;
+					iEnd = oSettings.aoData.length;
+				}
+				
+				for ( var j=iStart ; j<iEnd ; j++ )
+				{
+					var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ];
+					if ( aoData.nTr === null )
+					{
+						_fnCreateTr( oSettings, oSettings.aiDisplay[j] );
+					}
+
+					var nRow = aoData.nTr;
+					
+					/* Remove the old striping classes and then add the new one */
+					if ( iStripes !== 0 )
+					{
+						var sStripe = oSettings.asStripeClasses[ iRowCount % iStripes ];
+						if ( aoData._sRowStripe != sStripe )
+						{
+							$(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
+							aoData._sRowStripe = sStripe;
+						}
+					}
+					
+					/* Custom row callback function - might want to manipule the row */
+					if ( typeof oSettings.fnRowCallback == "function" )
+					{
+						nRow = oSettings.fnRowCallback.call( oSettings.oInstance, nRow, 
+							oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j );
+						if ( !nRow && !bRowError )
+						{
+							_fnLog( oSettings, 0, "A node was not returned by fnRowCallback" );
+							bRowError = true;
+						}
+					}
+					
+					anRows.push( nRow );
+					iRowCount++;
+					
+					/* If there is an open row - and it is attached to this parent - attach it on redraw */
+					if ( iOpenRows !== 0 )
+					{
+						for ( var k=0 ; k<iOpenRows ; k++ )
+						{
+							if ( nRow == oSettings.aoOpenRows[k].nParent )
+							{
+								anRows.push( oSettings.aoOpenRows[k].nTr );
+							}
+						}
+					}
+				}
+			}
+			else
+			{
+				/* Table is empty - create a row with an empty message in it */
+				anRows[ 0 ] = document.createElement( 'tr' );
+				
+				if ( typeof oSettings.asStripeClasses[0] != 'undefined' )
+				{
+					anRows[ 0 ].className = oSettings.asStripeClasses[0];
+				}
+
+				var sZero = oSettings.oLanguage.sZeroRecords.replace(
+					'_MAX_', oSettings.fnFormatNumber(oSettings.fnRecordsTotal()) );
+				if ( oSettings.iDraw == 1 && oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
+				{
+					sZero = oSettings.oLanguage.sLoadingRecords;
+				}
+				else if ( typeof oSettings.oLanguage.sEmptyTable != 'undefined' &&
+				     oSettings.fnRecordsTotal() === 0 )
+				{
+					sZero = oSettings.oLanguage.sEmptyTable;
+				}
+
+				var nTd = document.createElement( 'td' );
+				nTd.setAttribute( 'valign', "top" );
+				nTd.colSpan = _fnVisbleColumns( oSettings );
+				nTd.className = oSettings.oClasses.sRowEmpty;
+				nTd.innerHTML = sZero;
+				
+				anRows[ iRowCount ].appendChild( nTd );
+			}
+			
+			/* Callback the header and footer custom funcation if there is one */
+			if ( typeof oSettings.fnHeaderCallback == 'function' )
+			{
+				oSettings.fnHeaderCallback.call( oSettings.oInstance, $(oSettings.nTHead).children('tr')[0], 
+					_fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(),
+					oSettings.aiDisplay );
+			}
+			
+			if ( typeof oSettings.fnFooterCallback == 'function' )
+			{
+				oSettings.fnFooterCallback.call( oSettings.oInstance, $(oSettings.nTFoot).children('tr')[0], 
+					_fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(),
+					oSettings.aiDisplay );
+			}
+			
+			/* 
+			 * Need to remove any old row from the display - note we can't just empty the tbody using
+			 * $().html('') since this will unbind the jQuery event handlers (even although the node 
+			 * still exists!) - equally we can't use innerHTML, since IE throws an exception.
+			 */
+			var
+				nAddFrag = document.createDocumentFragment(),
+				nRemoveFrag = document.createDocumentFragment(),
+				nBodyPar, nTrs;
+			
+			if ( oSettings.nTBody )
+			{
+				nBodyPar = oSettings.nTBody.parentNode;
+				nRemoveFrag.appendChild( oSettings.nTBody );
+				
+				/* When doing infinite scrolling, only remove child rows when sorting, filtering or start
+				 * up. When not infinite scroll, always do it.
+				 */
+				if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete ||
+				 	oSettings.bSorted || oSettings.bFiltered )
+				{
+					nTrs = oSettings.nTBody.childNodes;
+					for ( i=nTrs.length-1 ; i>=0 ; i-- )
+					{
+						nTrs[i].parentNode.removeChild( nTrs[i] );
+					}
+				}
+				
+				/* Put the draw table into the dom */
+				for ( i=0, iLen=anRows.length ; i<iLen ; i++ )
+				{
+					nAddFrag.appendChild( anRows[i] );
+				}
+				
+				oSettings.nTBody.appendChild( nAddFrag );
+				if ( nBodyPar !== null )
+				{
+					nBodyPar.appendChild( oSettings.nTBody );
+				}
+			}
+			
+			/* Call all required callback functions for the end of a draw */
+			for ( i=oSettings.aoDrawCallback.length-1 ; i>=0 ; i-- )
+			{
+				oSettings.aoDrawCallback[i].fn.call( oSettings.oInstance, oSettings );
+			}
+			$(oSettings.oInstance).trigger('draw', oSettings);
+			
+			/* Draw is complete, sorting and filtering must be as well */
+			oSettings.bSorted = false;
+			oSettings.bFiltered = false;
+			oSettings.bDrawing = false;
+			
+			if ( oSettings.oFeatures.bServerSide )
+			{
+				_fnProcessingDisplay( oSettings, false );
+				if ( typeof oSettings._bInitComplete == 'undefined' )
+				{
+					_fnInitComplete( oSettings );
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnReDraw
+		 * Purpose:  Redraw the table - taking account of the various features which are enabled
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnReDraw( oSettings )
+		{
+			if ( oSettings.oFeatures.bSort )
+			{
+				/* Sorting will refilter and draw for us */
+				_fnSort( oSettings, oSettings.oPreviousSearch );
+			}
+			else if ( oSettings.oFeatures.bFilter )
+			{
+				/* Filtering will redraw for us */
+				_fnFilterComplete( oSettings, oSettings.oPreviousSearch );
+			}
+			else
+			{
+				_fnCalculateEnd( oSettings );
+				_fnDraw( oSettings );
+			}
+		}
+		
+		/*
+		 * Function: _fnAjaxUpdate
+		 * Purpose:  Update the table using an Ajax call
+		 * Returns:  bool: block the table drawing or not
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnAjaxUpdate( oSettings )
+		{
+			if ( oSettings.bAjaxDataGet )
+			{
+				oSettings.iDraw++;
+				_fnProcessingDisplay( oSettings, true );
+				var iColumns = oSettings.aoColumns.length;
+				var aoData = _fnAjaxParameters( oSettings );
+				_fnServerParams( oSettings, aoData );
+				
+				oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData,
+					function(json) {
+						_fnAjaxUpdateDraw( oSettings, json );
+					}, oSettings );
+				return false;
+			}
+			else
+			{
+				return true;
+			}
+		}
+		
+		/*
+		 * Function: _fnAjaxParameters
+		 * Purpose:  Build up the parameters in an object needed for a server-side processing request
+		 * Returns:  bool: block the table drawing or not
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnAjaxParameters( oSettings )
+		{
+			var iColumns = oSettings.aoColumns.length;
+			var aoData = [], mDataProp;
+			var i;
+			
+			aoData.push( { "name": "sEcho",          "value": oSettings.iDraw } );
+			aoData.push( { "name": "iColumns",       "value": iColumns } );
+			aoData.push( { "name": "sColumns",       "value": _fnColumnOrdering(oSettings) } );
+			aoData.push( { "name": "iDisplayStart",  "value": oSettings._iDisplayStart } );
+			aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ?
+				oSettings._iDisplayLength : -1 } );
+				
+			for ( i=0 ; i<iColumns ; i++ )
+			{
+			  mDataProp = oSettings.aoColumns[i].mDataProp;
+				aoData.push( { "name": "mDataProp_"+i, "value": typeof(mDataProp)=="function" ? 'function' : mDataProp } );
+			}
+			
+			/* Filtering */
+			if ( oSettings.oFeatures.bFilter !== false )
+			{
+				aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } );
+				aoData.push( { "name": "bRegex",  "value": oSettings.oPreviousSearch.bRegex } );
+				for ( i=0 ; i<iColumns ; i++ )
+				{
+					aoData.push( { "name": "sSearch_"+i,     "value": oSettings.aoPreSearchCols[i].sSearch } );
+					aoData.push( { "name": "bRegex_"+i,      "value": oSettings.aoPreSearchCols[i].bRegex } );
+					aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } );
+				}
+			}
+			
+			/* Sorting */
+			if ( oSettings.oFeatures.bSort !== false )
+			{
+				var iFixed = oSettings.aaSortingFixed !== null ? oSettings.aaSortingFixed.length : 0;
+				var iUser = oSettings.aaSorting.length;
+				aoData.push( { "name": "iSortingCols",   "value": iFixed+iUser } );
+				for ( i=0 ; i<iFixed ; i++ )
+				{
+					aoData.push( { "name": "iSortCol_"+i,  "value": oSettings.aaSortingFixed[i][0] } );
+					aoData.push( { "name": "sSortDir_"+i,  "value": oSettings.aaSortingFixed[i][1] } );
+				}
+				
+				for ( i=0 ; i<iUser ; i++ )
+				{
+					aoData.push( { "name": "iSortCol_"+(i+iFixed),  "value": oSettings.aaSorting[i][0] } );
+					aoData.push( { "name": "sSortDir_"+(i+iFixed),  "value": oSettings.aaSorting[i][1] } );
+				}
+				
+				for ( i=0 ; i<iColumns ; i++ )
+				{
+					aoData.push( { "name": "bSortable_"+i,  "value": oSettings.aoColumns[i].bSortable } );
+				}
+			}
+			
+			return aoData;
+		}
+		
+		/*
+		 * Function: _fnServerParams
+		 * Purpose:  Add Ajax parameters from plugins
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           array objects:aoData - name/value pairs to send to the server
+		 */
+		function _fnServerParams( oSettings, aoData )
+		{
+			for ( var i=0, iLen=oSettings.aoServerParams.length ; i<iLen ; i++ )
+			{
+				oSettings.aoServerParams[i].fn.call( oSettings.oInstance, aoData );
+			}
+		}
+		
+		/*
+		 * Function: _fnAjaxUpdateDraw
+		 * Purpose:  Data the data from the server (nuking the old) and redraw the table
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           object:json - json data return from the server.
+		 *             The following must be defined:
+		 *               iTotalRecords, iTotalDisplayRecords, aaData
+		 *             The following may be defined:
+		 *               sColumns
+		 */
+		function _fnAjaxUpdateDraw ( oSettings, json )
+		{
+			if ( typeof json.sEcho != 'undefined' )
+			{
+				/* Protect against old returns over-writing a new one. Possible when you get
+				 * very fast interaction, and later queires are completed much faster
+				 */
+				if ( json.sEcho*1 < oSettings.iDraw )
+				{
+					return;
+				}
+				else
+				{
+					oSettings.iDraw = json.sEcho * 1;
+				}
+			}
+			
+			if ( !oSettings.oScroll.bInfinite ||
+				   (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered)) )
+			{
+				_fnClearTable( oSettings );
+			}
+			oSettings._iRecordsTotal = json.iTotalRecords;
+			oSettings._iRecordsDisplay = json.iTotalDisplayRecords;
+			
+			/* Determine if reordering is required */
+			var sOrdering = _fnColumnOrdering(oSettings);
+			var bReOrder = (typeof json.sColumns != 'undefined' && sOrdering !== "" && json.sColumns != sOrdering );
+			if ( bReOrder )
+			{
+				var aiIndex = _fnReOrderIndex( oSettings, json.sColumns );
+			}
+
+			var fnDataSrc = _fnGetObjectDataFn( oSettings.sAjaxDataProp );
+			var aData = fnDataSrc( json );
+			
+			for ( var i=0, iLen=aData.length ; i<iLen ; i++ )
+			{
+				if ( bReOrder )
+				{
+					/* If we need to re-order, then create a new array with the correct order and add it */
+					var aDataSorted = [];
+					for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
+					{
+						aDataSorted.push( aData[i][ aiIndex[j] ] );
+					}
+					_fnAddData( oSettings, aDataSorted );
+				}
+				else
+				{
+					/* No re-order required, sever got it "right" - just straight add */
+					_fnAddData( oSettings, aData[i] );
+				}
+			}
+			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+			
+			oSettings.bAjaxDataGet = false;
+			_fnDraw( oSettings );
+			oSettings.bAjaxDataGet = true;
+			_fnProcessingDisplay( oSettings, false );
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Options (features) HTML
+		 */
+		
+		/*
+		 * Function: _fnAddOptionsHtml
+		 * Purpose:  Add the options to the page HTML for the table
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnAddOptionsHtml ( oSettings )
+		{
+			/*
+			 * Create a temporary, empty, div which we can later on replace with what we have generated
+			 * we do it this way to rendering the 'options' html offline - speed :-)
+			 */
+			var nHolding = document.createElement( 'div' );
+			oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable );
+			
+			/* 
+			 * All DataTables are wrapped in a div
+			 */
+			oSettings.nTableWrapper = document.createElement( 'div' );
+			oSettings.nTableWrapper.className = oSettings.oClasses.sWrapper;
+			if ( oSettings.sTableId !== '' )
+			{
+				oSettings.nTableWrapper.setAttribute( 'id', oSettings.sTableId+'_wrapper' );
+			}
+
+			oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
+
+			/* Track where we want to insert the option */
+			var nInsertNode = oSettings.nTableWrapper;
+			
+			/* Loop over the user set positioning and place the elements as needed */
+			var aDom = oSettings.sDom.split('');
+			var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
+			for ( var i=0 ; i<aDom.length ; i++ )
+			{
+				iPushFeature = 0;
+				cOption = aDom[i];
+				
+				if ( cOption == '<' )
+				{
+					/* New container div */
+					nNewNode = document.createElement( 'div' );
+					
+					/* Check to see if we should append an id and/or a class name to the container */
+					cNext = aDom[i+1];
+					if ( cNext == "'" || cNext == '"' )
+					{
+						sAttr = "";
+						j = 2;
+						while ( aDom[i+j] != cNext )
+						{
+							sAttr += aDom[i+j];
+							j++;
+						}
+						
+						/* Replace jQuery UI constants */
+						if ( sAttr == "H" )
+						{
+							sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix";
+						}
+						else if ( sAttr == "F" )
+						{
+							sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix";
+						}
+						
+						/* The attribute can be in the format of "#id.class", "#id" or "class" This logic
+						 * breaks the string into parts and applies them as needed
+						 */
+						if ( sAttr.indexOf('.') != -1 )
+						{
+							var aSplit = sAttr.split('.');
+							nNewNode.setAttribute('id', aSplit[0].substr(1, aSplit[0].length-1) );
+							nNewNode.className = aSplit[1];
+						}
+						else if ( sAttr.charAt(0) == "#" )
+						{
+							nNewNode.setAttribute('id', sAttr.substr(1, sAttr.length-1) );
+						}
+						else
+						{
+							nNewNode.className = sAttr;
+						}
+						
+						i += j; /* Move along the position array */
+					}
+					
+					nInsertNode.appendChild( nNewNode );
+					nInsertNode = nNewNode;
+				}
+				else if ( cOption == '>' )
+				{
+					/* End container div */
+					nInsertNode = nInsertNode.parentNode;
+				}
+				else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange )
+				{
+					/* Length */
+					nTmp = _fnFeatureHtmlLength( oSettings );
+					iPushFeature = 1;
+				}
+				else if ( cOption == 'f' && oSettings.oFeatures.bFilter )
+				{
+					/* Filter */
+					nTmp = _fnFeatureHtmlFilter( oSettings );
+					iPushFeature = 1;
+				}
+				else if ( cOption == 'r' && oSettings.oFeatures.bProcessing )
+				{
+					/* pRocessing */
+					nTmp = _fnFeatureHtmlProcessing( oSettings );
+					iPushFeature = 1;
+				}
+				else if ( cOption == 't' )
+				{
+					/* Table */
+					nTmp = _fnFeatureHtmlTable( oSettings );
+					iPushFeature = 1;
+				}
+				else if ( cOption ==  'i' && oSettings.oFeatures.bInfo )
+				{
+					/* Info */
+					nTmp = _fnFeatureHtmlInfo( oSettings );
+					iPushFeature = 1;
+				}
+				else if ( cOption == 'p' && oSettings.oFeatures.bPaginate )
+				{
+					/* Pagination */
+					nTmp = _fnFeatureHtmlPaginate( oSettings );
+					iPushFeature = 1;
+				}
+				else if ( _oExt.aoFeatures.length !== 0 )
+				{
+					/* Plug-in features */
+					var aoFeatures = _oExt.aoFeatures;
+					for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
+					{
+						if ( cOption == aoFeatures[k].cFeature )
+						{
+							nTmp = aoFeatures[k].fnInit( oSettings );
+							if ( nTmp )
+							{
+								iPushFeature = 1;
+							}
+							break;
+						}
+					}
+				}
+				
+				/* Add to the 2D features array */
+				if ( iPushFeature == 1 && nTmp !== null )
+				{
+					if ( typeof oSettings.aanFeatures[cOption] != 'object' )
+					{
+						oSettings.aanFeatures[cOption] = [];
+					}
+					oSettings.aanFeatures[cOption].push( nTmp );
+					nInsertNode.appendChild( nTmp );
+				}
+			}
+			
+			/* Built our DOM structure - replace the holding div with what we want */
+			nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding );
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Feature: Filtering
+		 */
+		
+		/*
+		 * Function: _fnFeatureHtmlTable
+		 * Purpose:  Add any control elements for the table - specifically scrolling
+		 * Returns:  node: - Node to add to the DOM
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnFeatureHtmlTable ( oSettings )
+		{
+			/* Chack if scrolling is enabled or not - if not then leave the DOM unaltered */
+			if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
+			{
+				return oSettings.nTable;
+			}
+			
+			/*
+			 * The HTML structure that we want to generate in this function is:
+			 *  div - nScroller
+			 *    div - nScrollHead
+			 *      div - nScrollHeadInner
+			 *        table - nScrollHeadTable
+			 *          thead - nThead
+			 *    div - nScrollBody
+			 *      table - oSettings.nTable
+			 *        thead - nTheadSize
+			 *        tbody - nTbody
+			 *    div - nScrollFoot
+			 *      div - nScrollFootInner
+			 *        table - nScrollFootTable
+			 *          tfoot - nTfoot
+			 */
+			var
+			 	nScroller = document.createElement('div'),
+			 	nScrollHead = document.createElement('div'),
+			 	nScrollHeadInner = document.createElement('div'),
+			 	nScrollBody = document.createElement('div'),
+			 	nScrollFoot = document.createElement('div'),
+			 	nScrollFootInner = document.createElement('div'),
+			 	nScrollHeadTable = oSettings.nTable.cloneNode(false),
+			 	nScrollFootTable = oSettings.nTable.cloneNode(false),
+				nThead = oSettings.nTable.getElementsByTagName('thead')[0],
+			 	nTfoot = oSettings.nTable.getElementsByTagName('tfoot').length === 0 ? null : 
+					oSettings.nTable.getElementsByTagName('tfoot')[0],
+				oClasses = (typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI) ?
+					_oExt.oJUIClasses : _oExt.oStdClasses;
+			
+			nScrollHead.appendChild( nScrollHeadInner );
+			nScrollFoot.appendChild( nScrollFootInner );
+			nScrollBody.appendChild( oSettings.nTable );
+			nScroller.appendChild( nScrollHead );
+			nScroller.appendChild( nScrollBody );
+			nScrollHeadInner.appendChild( nScrollHeadTable );
+			nScrollHeadTable.appendChild( nThead );
+			if ( nTfoot !== null )
+			{
+				nScroller.appendChild( nScrollFoot );
+				nScrollFootInner.appendChild( nScrollFootTable );
+				nScrollFootTable.appendChild( nTfoot );
+			}
+			
+			nScroller.className = oClasses.sScrollWrapper;
+			nScrollHead.className = oClasses.sScrollHead;
+			nScrollHeadInner.className = oClasses.sScrollHeadInner;
+			nScrollBody.className = oClasses.sScrollBody;
+			nScrollFoot.className = oClasses.sScrollFoot;
+			nScrollFootInner.className = oClasses.sScrollFootInner;
+			
+			if ( oSettings.oScroll.bAutoCss )
+			{
+				nScrollHead.style.overflow = "hidden";
+				nScrollHead.style.position = "relative";
+				nScrollFoot.style.overflow = "hidden";
+				nScrollBody.style.overflow = "auto";
+			}
+			
+			nScrollHead.style.border = "0";
+			nScrollHead.style.width = "100%";
+			nScrollFoot.style.border = "0";
+			nScrollHeadInner.style.width = "150%"; /* will be overwritten */
+			
+			/* Modify attributes to respect the clones */
+			nScrollHeadTable.removeAttribute('id');
+			nScrollHeadTable.style.marginLeft = "0";
+			oSettings.nTable.style.marginLeft = "0";
+			if ( nTfoot !== null )
+			{
+				nScrollFootTable.removeAttribute('id');
+				nScrollFootTable.style.marginLeft = "0";
+			}
+			
+			/* Move any caption elements from the body to the header */
+			var nCaptions = $(oSettings.nTable).children('caption');
+			for ( var i=0, iLen=nCaptions.length ; i<iLen ; i++ )
+			{
+				nScrollHeadTable.appendChild( nCaptions[i] );
+			}
+			
+			/*
+			 * Sizing
+			 */
+			/* When xscrolling add the width and a scroller to move the header with the body */
+			if ( oSettings.oScroll.sX !== "" )
+			{
+				nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX );
+				nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX );
+				
+				if ( nTfoot !== null )
+				{
+					nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX );	
+				}
+				
+				/* When the body is scrolled, then we also want to scroll the headers */
+				$(nScrollBody).scroll( function (e) {
+					nScrollHead.scrollLeft = this.scrollLeft;
+					
+					if ( nTfoot !== null )
+					{
+						nScrollFoot.scrollLeft = this.scrollLeft;
+					}
+				} );
+			}
+			
+			/* When yscrolling, add the height */
+			if ( oSettings.oScroll.sY !== "" )
+			{
+				nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY );
+			}
+			
+			/* Redraw - align columns across the tables */
+			oSettings.aoDrawCallback.push( {
+				"fn": _fnScrollDraw,
+				"sName": "scrolling"
+			} );
+			
+			/* Infinite scrolling event handlers */
+			if ( oSettings.oScroll.bInfinite )
+			{
+				$(nScrollBody).scroll( function() {
+					/* Use a blocker to stop scrolling from loading more data while other data is still loading */
+					if ( !oSettings.bDrawing )
+					{
+						/* Check if we should load the next data set */
+						if ( $(this).scrollTop() + $(this).height() > 
+							$(oSettings.nTable).height() - oSettings.oScroll.iLoadGap )
+						{
+							/* Only do the redraw if we have to - we might be at the end of the data */
+							if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() )
+							{
+								_fnPageChange( oSettings, 'next' );
+								_fnCalculateEnd( oSettings );
+								_fnDraw( oSettings );
+							}
+						}
+					}
+				} );
+			}
+			
+			oSettings.nScrollHead = nScrollHead;
+			oSettings.nScrollFoot = nScrollFoot;
+			
+			return nScroller;
+		}
+		
+		/*
+		 * Function: _fnScrollDraw
+		 * Purpose:  Update the various tables for resizing
+		 * Returns:  node: - Node to add to the DOM
+		 * Inputs:   object:o - dataTables settings object
+		 * Notes:    It's a bit of a pig this function, but basically the idea to:
+		 *   1. Re-create the table inside the scrolling div
+		 *   2. Take live measurements from the DOM
+		 *   3. Apply the measurements
+		 *   4. Clean up
+		 */
+		function _fnScrollDraw ( o )
+		{
+			var
+				nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0],
+				nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
+				nScrollBody = o.nTable.parentNode,
+				i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis,
+				iWidth, aApplied=[], iSanityWidth,
+				nScrollFootInner = (o.nTFoot !== null) ? o.nScrollFoot.getElementsByTagName('div')[0] : null,
+				nScrollFootTable = (o.nTFoot !== null) ? nScrollFootInner.getElementsByTagName('table')[0] : null,
+				ie67 = $.browser.msie && $.browser.version <= 7;
+			
+			/*
+			 * 1. Re-create the table inside the scrolling div
+			 */
+			
+			/* Remove the old minimised thead and tfoot elements in the inner table */
+			var nTheadSize = o.nTable.getElementsByTagName('thead');
+			if ( nTheadSize.length > 0 )
+			{
+				o.nTable.removeChild( nTheadSize[0] );
+			}
+			
+			if ( o.nTFoot !== null )
+			{
+				/* Remove the old minimised footer element in the cloned header */
+				var nTfootSize = o.nTable.getElementsByTagName('tfoot');
+				if ( nTfootSize.length > 0 )
+				{
+					o.nTable.removeChild( nTfootSize[0] );
+				}
+			}
+			
+			/* Clone the current header and footer elements and then place it into the inner table */
+			nTheadSize = o.nTHead.cloneNode(true);
+			o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] );
+			
+			if ( o.nTFoot !== null )
+			{
+				nTfootSize = o.nTFoot.cloneNode(true);
+				o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] );
+			}
+			
+			/*
+			 * 2. Take live measurements from the DOM - do not alter the DOM itself!
+			 */
+			
+			/* Remove old sizing and apply the calculated column widths
+			 * Get the unique column headers in the newly created (cloned) header. We want to apply the
+			 * calclated sizes to this header
+			 */
+			if ( o.oScroll.sX === "" )
+			{
+				nScrollBody.style.width = '100%';
+				nScrollHeadInner.parentNode.style.width = '100%';
+			}
+			
+			var nThs = _fnGetUniqueThs( o, nTheadSize );
+			for ( i=0, iLen=nThs.length ; i<iLen ; i++ )
+			{
+				iVis = _fnVisibleToColumnIndex( o, i );
+				nThs[i].style.width = o.aoColumns[iVis].sWidth;
+			}
+			
+			if ( o.nTFoot !== null )
+			{
+				_fnApplyToChildren( function(n) {
+					n.style.width = "";
+				}, nTfootSize.getElementsByTagName('tr') );
+			}
+			
+			/* Size the table as a whole */
+			iSanityWidth = $(o.nTable).outerWidth();
+			if ( o.oScroll.sX === "" )
+			{
+				/* No x scrolling */
+				o.nTable.style.width = "100%";
+				
+				/* I know this is rubbish - but IE7 will make the width of the table when 100% include
+				 * the scrollbar - which is shouldn't. When there is a scrollbar we need to take this
+				 * into account.
+				 */
+				if ( ie67 && (nScrollBody.scrollHeight > 
+					nScrollBody.offsetHeight || $(nScrollBody).css('overflow-y') == "scroll")  )
+				{
+					o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth()-o.oScroll.iBarWidth );
+				}
+			}
+			else
+			{
+				if ( o.oScroll.sXInner !== "" )
+				{
+					/* x scroll inner has been given - use it */
+					o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner);
+				}
+				else if ( iSanityWidth == $(nScrollBody).width() &&
+				   $(nScrollBody).height() < $(o.nTable).height() )
+				{
+					/* There is y-scrolling - try to take account of the y scroll bar */
+					o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth );
+					if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth )
+					{
+						/* Not possible to take account of it */
+						o.nTable.style.width = _fnStringToCss( iSanityWidth );
+					}
+				}
+				else
+				{
+					/* All else fails */
+					o.nTable.style.width = _fnStringToCss( iSanityWidth );
+				}
+			}
+			
+			/* Recalculate the sanity width - now that we've applied the required width, before it was
+			 * a temporary variable. This is required because the column width calculation is done
+			 * before this table DOM is created.
+			 */
+			iSanityWidth = $(o.nTable).outerWidth();
+			
+			/* We want the hidden header to have zero height, so remove padding and borders. Then
+			 * set the width based on the real headers
+			 */
+			anHeadToSize = o.nTHead.getElementsByTagName('tr');
+			anHeadSizers = nTheadSize.getElementsByTagName('tr');
+			
+			_fnApplyToChildren( function(nSizer, nToSize) {
+				oStyle = nSizer.style;
+				oStyle.paddingTop = "0";
+				oStyle.paddingBottom = "0";
+				oStyle.borderTopWidth = "0";
+				oStyle.borderBottomWidth = "0";
+				oStyle.height = 0;
+				
+				iWidth = $(nSizer).width();
+				nToSize.style.width = _fnStringToCss( iWidth );
+				aApplied.push( iWidth );
+			}, anHeadSizers, anHeadToSize );
+			$(anHeadSizers).height(0);
+			
+			if ( o.nTFoot !== null )
+			{
+				/* Clone the current footer and then place it into the body table as a "hidden header" */
+				anFootSizers = nTfootSize.getElementsByTagName('tr');
+				anFootToSize = o.nTFoot.getElementsByTagName('tr');
+				
+				_fnApplyToChildren( function(nSizer, nToSize) {
+					oStyle = nSizer.style;
+					oStyle.paddingTop = "0";
+					oStyle.paddingBottom = "0";
+					oStyle.borderTopWidth = "0";
+					oStyle.borderBottomWidth = "0";
+					oStyle.height = 0;
+					
+					iWidth = $(nSizer).width();
+					nToSize.style.width = _fnStringToCss( iWidth );
+					aApplied.push( iWidth );
+				}, anFootSizers, anFootToSize );
+				$(anFootSizers).height(0);
+			}
+			
+			/*
+			 * 3. Apply the measurements
+			 */
+			
+			/* "Hide" the header and footer that we used for the sizing. We want to also fix their width
+			 * to what they currently are
+			 */
+			_fnApplyToChildren( function(nSizer) {
+				nSizer.innerHTML = "";
+				nSizer.style.width = _fnStringToCss( aApplied.shift() );
+			}, anHeadSizers );
+			
+			if ( o.nTFoot !== null )
+			{
+				_fnApplyToChildren( function(nSizer) {
+					nSizer.innerHTML = "";
+					nSizer.style.width = _fnStringToCss( aApplied.shift() );
+				}, anFootSizers );
+			}
+			
+			/* Sanity check that the table is of a sensible width. If not then we are going to get
+			 * misalignment - try to prevent this by not allowing the table to shrink below its min width
+			 */
+			if ( $(o.nTable).outerWidth() < iSanityWidth )
+			{
+				/* The min width depends upon if we have a vertical scrollbar visible or not */
+				var iCorrection = ((nScrollBody.scrollHeight > nScrollBody.offsetHeight || 
+					$(nScrollBody).css('overflow-y') == "scroll")) ?
+						iSanityWidth+o.oScroll.iBarWidth : iSanityWidth;
+				
+				/* IE6/7 are a law unto themselves... */
+				if ( ie67 && (nScrollBody.scrollHeight > 
+					nScrollBody.offsetHeight || $(nScrollBody).css('overflow-y') == "scroll")  )
+				{
+					o.nTable.style.width = _fnStringToCss( iCorrection-o.oScroll.iBarWidth );
+				}
+				
+				/* Apply the calculated minimum width to the table wrappers */
+				nScrollBody.style.width = _fnStringToCss( iCorrection );
+				nScrollHeadInner.parentNode.style.width = _fnStringToCss( iCorrection );
+				
+				if ( o.nTFoot !== null )
+				{
+					nScrollFootInner.parentNode.style.width = _fnStringToCss( iCorrection );
+				}
+				
+				/* And give the user a warning that we've stopped the table getting too small */
+				if ( o.oScroll.sX === "" )
+				{
+					_fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
+						" misalignment. The table has been drawn at its minimum possible width." );
+				}
+				else if ( o.oScroll.sXInner !== "" )
+				{
+					_fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
+						" misalignment. Increase the sScrollXInner value or remove it to allow automatic"+
+						" calculation" );
+				}
+			}
+			else
+			{
+				nScrollBody.style.width = _fnStringToCss( '100%' );
+				nScrollHeadInner.parentNode.style.width = _fnStringToCss( '100%' );
+				
+				if ( o.nTFoot !== null )
+				{
+					nScrollFootInner.parentNode.style.width = _fnStringToCss( '100%' );
+				}
+			}
+			
+			
+			/*
+			 * 4. Clean up
+			 */
+			
+			if ( o.oScroll.sY === "" )
+			{
+				/* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
+				 * the scrollbar height from the visible display, rather than adding it on. We need to
+				 * set the height in order to sort this. Don't want to do it in any other browsers.
+				 */
+				if ( ie67 )
+				{
+					nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth );
+				}
+			}
+			
+			if ( o.oScroll.sY !== "" && o.oScroll.bCollapse )
+			{
+				nScrollBody.style.height = _fnStringToCss( o.oScroll.sY );
+				
+				var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ?
+				 	o.oScroll.iBarWidth : 0;
+				if ( o.nTable.offsetHeight < nScrollBody.offsetHeight )
+				{
+					nScrollBody.style.height = _fnStringToCss( $(o.nTable).height()+iExtra );
+				}
+			}
+			
+			/* Finally set the width's of the header and footer tables */
+			var iOuterWidth = $(o.nTable).outerWidth();
+			nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth );
+			nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth+o.oScroll.iBarWidth );
+			
+			if ( o.nTFoot !== null )
+			{
+				nScrollFootInner.style.width = _fnStringToCss( o.nTable.offsetWidth+o.oScroll.iBarWidth );
+				nScrollFootTable.style.width = _fnStringToCss( o.nTable.offsetWidth );
+			}
+			
+			/* If sorting or filtering has occured, jump the scrolling back to the top */
+			if ( o.bSorted || o.bFiltered )
+			{
+				nScrollBody.scrollTop = 0;
+			}
+		}
+		
+		/*
+		 * Function: _fnAdjustColumnSizing
+		 * Purpose:  Adjust the table column widths for new data
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 * Notes:    You would probably want to do a redraw after calling this function!
+		 */
+		function _fnAdjustColumnSizing ( oSettings )
+		{
+			/* Not interested in doing column width calculation if autowidth is disabled */
+			if ( oSettings.oFeatures.bAutoWidth === false )
+			{
+				return false;
+			}
+			
+			_fnCalculateColumnWidths( oSettings );
+			for ( var i=0 , iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+			{
+				oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth;
+			}
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Feature: Filtering
+		 */
+		
+		/*
+		 * Function: _fnFeatureHtmlFilter
+		 * Purpose:  Generate the node required for filtering text
+		 * Returns:  node
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnFeatureHtmlFilter ( oSettings )
+		{
+			var sSearchStr = oSettings.oLanguage.sSearch;
+			sSearchStr = (sSearchStr.indexOf('_INPUT_') !== -1) ?
+			  sSearchStr.replace('_INPUT_', '<input type="text" />') :
+			  sSearchStr==="" ? '<input type="text" />' : sSearchStr+' <input type="text" />';
+			
+			var nFilter = document.createElement( 'div' );
+			nFilter.className = oSettings.oClasses.sFilter;
+			nFilter.innerHTML = '<label>'+sSearchStr+'</label>';
+			if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.f == "undefined" )
+			{
+				nFilter.setAttribute( 'id', oSettings.sTableId+'_filter' );
+			}
+			
+			var jqFilter = $("input", nFilter);
+			jqFilter.val( oSettings.oPreviousSearch.sSearch.replace('"','&quot;') );
+			jqFilter.bind( 'keyup.DT', function(e) {
+				/* Update all other filter input elements for the new display */
+				var n = oSettings.aanFeatures.f;
+				for ( var i=0, iLen=n.length ; i<iLen ; i++ )
+				{
+					if ( n[i] != $(this).parents('div.dataTables_filter')[0] )
+					{
+						$('input', n[i]).val( this.value );
+					}
+				}
+				
+				/* Now do the filter */
+				if ( this.value != oSettings.oPreviousSearch.sSearch )
+				{
+					_fnFilterComplete( oSettings, { 
+						"sSearch": this.value, 
+						"bRegex":  oSettings.oPreviousSearch.bRegex,
+						"bSmart":  oSettings.oPreviousSearch.bSmart 
+					} );
+				}
+			} );
+			
+			jqFilter.bind( 'keypress.DT', function(e) {
+				/* Prevent default */
+				if ( e.keyCode == 13 )
+				{
+					return false;
+				}
+			} );
+			
+			return nFilter;
+		}
+		
+		/*
+		 * Function: _fnFilterComplete
+		 * Purpose:  Filter the table using both the global filter and column based filtering
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           object:oSearch: search information
+		 *           int:iForce - optional - force a research of the master array (1) or not (undefined or 0)
+		 */
+		function _fnFilterComplete ( oSettings, oInput, iForce )
+		{
+			/* Filter on everything */
+			_fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart );
+			
+			/* Now do the individual column filter */
+			for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
+			{
+				_fnFilterColumn( oSettings, oSettings.aoPreSearchCols[i].sSearch, i, 
+					oSettings.aoPreSearchCols[i].bRegex, oSettings.aoPreSearchCols[i].bSmart );
+			}
+			
+			/* Custom filtering */
+			if ( _oExt.afnFiltering.length !== 0 )
+			{
+				_fnFilterCustom( oSettings );
+			}
+			
+			/* Tell the draw function we have been filtering */
+			oSettings.bFiltered = true;
+			$(oSettings.oInstance).trigger('filter', oSettings);
+			
+			/* Redraw the table */
+			oSettings._iDisplayStart = 0;
+			_fnCalculateEnd( oSettings );
+			_fnDraw( oSettings );
+			
+			/* Rebuild search array 'offline' */
+			_fnBuildSearchArray( oSettings, 0 );
+		}
+		
+		/*
+		 * Function: _fnFilterCustom
+		 * Purpose:  Apply custom filtering functions
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnFilterCustom( oSettings )
+		{
+			var afnFilters = _oExt.afnFiltering;
+			for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ )
+			{
+				var iCorrector = 0;
+				for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ )
+				{
+					var iDisIndex = oSettings.aiDisplay[j-iCorrector];
+					
+					/* Check if we should use this row based on the filtering function */
+					if ( !afnFilters[i]( oSettings, _fnGetRowData( oSettings, iDisIndex, 'filter' ), iDisIndex ) )
+					{
+						oSettings.aiDisplay.splice( j-iCorrector, 1 );
+						iCorrector++;
+					}
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnFilterColumn
+		 * Purpose:  Filter the table on a per-column basis
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           string:sInput - string to filter on
+		 *           int:iColumn - column to filter
+		 *           bool:bRegex - treat search string as a regular expression or not
+		 *           bool:bSmart - use smart filtering or not
+		 */
+		function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart )
+		{
+			if ( sInput === "" )
+			{
+				return;
+			}
+			
+			var iIndexCorrector = 0;
+			var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart );
+			
+			for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- )
+			{
+				var sData = _fnDataToSearch( _fnGetCellData( oSettings, oSettings.aiDisplay[i], iColumn, 'filter' ),
+					oSettings.aoColumns[iColumn].sType );
+				if ( ! rpSearch.test( sData ) )
+				{
+					oSettings.aiDisplay.splice( i, 1 );
+					iIndexCorrector++;
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnFilter
+		 * Purpose:  Filter the data table based on user input and draw the table
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           string:sInput - string to filter on
+		 *           int:iForce - optional - force a research of the master array (1) or not (undefined or 0)
+		 *           bool:bRegex - treat as a regular expression or not
+		 *           bool:bSmart - perform smart filtering or not
+		 */
+		function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart )
+		{
+			var i;
+			var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart );
+			
+			/* Check if we are forcing or not - optional parameter */
+			if ( typeof iForce == 'undefined' || iForce === null )
+			{
+				iForce = 0;
+			}
+			
+			/* Need to take account of custom filtering functions - always filter */
+			if ( _oExt.afnFiltering.length !== 0 )
+			{
+				iForce = 1;
+			}
+			
+			/*
+			 * If the input is blank - we want the full data set
+			 */
+			if ( sInput.length <= 0 )
+			{
+				oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
+				oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+			}
+			else
+			{
+				/*
+				 * We are starting a new search or the new search string is smaller 
+				 * then the old one (i.e. delete). Search from the master array
+			 	 */
+				if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length ||
+					   oSettings.oPreviousSearch.sSearch.length > sInput.length || iForce == 1 ||
+					   sInput.indexOf(oSettings.oPreviousSearch.sSearch) !== 0 )
+				{
+					/* Nuke the old display array - we are going to rebuild it */
+					oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
+					
+					/* Force a rebuild of the search array */
+					_fnBuildSearchArray( oSettings, 1 );
+					
+					/* Search through all records to populate the search array
+					 * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 
+					 * mapping
+					 */
+					for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ )
+					{
+						if ( rpSearch.test(oSettings.asDataSearch[i]) )
+						{
+							oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] );
+						}
+					}
+			  }
+			  else
+				{
+			  	/* Using old search array - refine it - do it this way for speed
+			  	 * Don't have to search the whole master array again
+					 */
+			  	var iIndexCorrector = 0;
+			  	
+			  	/* Search the current results */
+			  	for ( i=0 ; i<oSettings.asDataSearch.length ; i++ )
+					{
+			  		if ( ! rpSearch.test(oSettings.asDataSearch[i]) )
+						{
+			  			oSettings.aiDisplay.splice( i-iIndexCorrector, 1 );
+			  			iIndexCorrector++;
+			  		}
+			  	}
+			  }
+			}
+			oSettings.oPreviousSearch.sSearch = sInput;
+			oSettings.oPreviousSearch.bRegex = bRegex;
+			oSettings.oPreviousSearch.bSmart = bSmart;
+		}
+		
+		/*
+		 * Function: _fnBuildSearchArray
+		 * Purpose:  Create an array which can be quickly search through
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iMaster - use the master data array - optional
+		 */
+		function _fnBuildSearchArray ( oSettings, iMaster )
+		{
+			if ( !oSettings.oFeatures.bServerSide )
+			{
+				/* Clear out the old data */
+				oSettings.asDataSearch.splice( 0, oSettings.asDataSearch.length );
+				
+				var aArray = (typeof iMaster != 'undefined' && iMaster == 1) ?
+				 	oSettings.aiDisplayMaster : oSettings.aiDisplay;
+				
+				for ( var i=0, iLen=aArray.length ; i<iLen ; i++ )
+				{
+					oSettings.asDataSearch[i] = _fnBuildSearchRow( oSettings,
+						_fnGetRowData( oSettings, aArray[i], 'filter' ) );
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnBuildSearchRow
+		 * Purpose:  Create a searchable string from a single data row
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           array:aData - Row data array to use for the data to search
+		 */
+		function _fnBuildSearchRow( oSettings, aData )
+		{
+			var sSearch = '';
+			if ( typeof oSettings.__nTmpFilter == 'undefined' ) {
+				oSettings.__nTmpFilter = document.createElement('div');
+			}
+			var nTmp = oSettings.__nTmpFilter;
+			
+			for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
+			{
+				if ( oSettings.aoColumns[j].bSearchable )
+				{
+					var sData = aData[j];
+					sSearch += _fnDataToSearch( sData, oSettings.aoColumns[j].sType )+'  ';
+				}
+			}
+			
+			/* If it looks like there is an HTML entity in the string, attempt to decode it */
+			if ( sSearch.indexOf('&') !== -1 )
+			{
+				nTmp.innerHTML = sSearch;
+				sSearch = nTmp.textContent ? nTmp.textContent : nTmp.innerText;
+				
+				/* IE and Opera appear to put an newline where there is a <br> tag - remove it */
+				sSearch = sSearch.replace(/\n/g," ").replace(/\r/g,"");
+			}
+			
+			return sSearch;
+		}
+		
+		/*
+		 * Function: _fnFilterCreateSearch
+		 * Purpose:  Build a regular expression object suitable for searching a table
+		 * Returns:  RegExp: - constructed object
+		 * Inputs:   string:sSearch - string to search for
+		 *           bool:bRegex - treat as a regular expression or not
+		 *           bool:bSmart - perform smart filtering or not
+		 */
+		function _fnFilterCreateSearch( sSearch, bRegex, bSmart )
+		{
+			var asSearch, sRegExpString;
+			
+			if ( bSmart )
+			{
+				/* Generate the regular expression to use. Something along the lines of:
+				 * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$
+				 */
+				asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' );
+				sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$';
+				return new RegExp( sRegExpString, "i" );
+			}
+			else
+			{
+				sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch );
+				return new RegExp( sSearch, "i" );
+			}
+		}
+		
+		/*
+		 * Function: _fnDataToSearch
+		 * Purpose:  Convert raw data into something that the user can search on
+		 * Returns:  string: - search string
+		 * Inputs:   string:sData - data to be modified
+		 *           string:sType - data type
+		 */
+		function _fnDataToSearch ( sData, sType )
+		{
+			if ( typeof _oExt.ofnSearch[sType] == "function" )
+			{
+				return _oExt.ofnSearch[sType]( sData );
+			}
+			else if ( sType == "html" )
+			{
+				return sData.replace(/\n/g," ").replace( /<.*?>/g, "" );
+			}
+			else if ( typeof sData == "string" )
+			{
+				return sData.replace(/\n/g," ");
+			}
+			else if ( sData === null )
+			{
+				return '';
+			}
+			return sData;
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Feature: Sorting
+		 */
+		
+		/*
+	 	 * Function: _fnSort
+		 * Purpose:  Change the order of the table
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           bool:bApplyClasses - optional - should we apply classes or not
+		 * Notes:    We always sort the master array and then apply a filter again
+		 *   if it is needed. This probably isn't optimal - but atm I can't think
+		 *   of any other way which is (each has disadvantages). we want to sort aiDisplayMaster - 
+		 *   but according to aoData[]._aData
+		 */
+		function _fnSort ( oSettings, bApplyClasses )
+		{
+			var
+				iDataSort, iDataType,
+				i, iLen, j, jLen,
+				aaSort = [],
+			 	aiOrig = [],
+				oSort = _oExt.oSort,
+				aoData = oSettings.aoData,
+				aoColumns = oSettings.aoColumns;
+			
+			/* No sorting required if server-side or no sorting array */
+			if ( !oSettings.oFeatures.bServerSide && 
+				(oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
+			{
+				if ( oSettings.aaSortingFixed !== null )
+				{
+					aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
+				}
+				else
+				{
+					aaSort = oSettings.aaSorting.slice();
+				}
+				
+				/* If there is a sorting data type, and a fuction belonging to it, then we need to
+				 * get the data from the developer's function and apply it for this column
+				 */
+				for ( i=0 ; i<aaSort.length ; i++ )
+				{
+					var iColumn = aaSort[i][0];
+					var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
+					var sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
+					if ( typeof _oExt.afnSortData[sDataType] != 'undefined' )
+					{
+						var aData = _oExt.afnSortData[sDataType]( oSettings, iColumn, iVisColumn );
+						for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
+						{
+							_fnSetCellData( oSettings, j, iColumn, aData[j] );
+						}
+					}
+				}
+				
+				/* Create a value - key array of the current row positions such that we can use their
+				 * current position during the sort, if values match, in order to perform stable sorting
+				 */
+				for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
+				{
+					aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
+				}
+				
+				/* Do the sort - here we want multi-column sorting based on a given data source (column)
+				 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
+				 * follow on it's own, but this is what we want (example two column sorting):
+				 *  fnLocalSorting = function(a,b){
+				 *  	var iTest;
+				 *  	iTest = oSort['string-asc']('data11', 'data12');
+				 *  	if (iTest !== 0)
+				 *  		return iTest;
+				 *    iTest = oSort['numeric-desc']('data21', 'data22');
+				 *    if (iTest !== 0)
+				 *  		return iTest;
+				 *  	return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
+				 *  }
+				 * Basically we have a test for each sorting column, if the data in that column is equal,
+				 * test the next column. If all columns match, then we use a numeric sort on the row 
+				 * positions in the original data array to provide a stable sort.
+				 */
+				var iSortLen = aaSort.length;
+				oSettings.aiDisplayMaster.sort( function ( a, b ) {
+					var iTest, iDataSort, sDataType;
+					for ( i=0 ; i<iSortLen ; i++ )
+					{
+						iDataSort = aoColumns[ aaSort[i][0] ].iDataSort;
+						sDataType = aoColumns[ iDataSort ].sType;
+						iTest = oSort[ (sDataType?sDataType:'string')+"-"+aaSort[i][1] ](
+							_fnGetCellData( oSettings, a, iDataSort, 'sort' ),
+							_fnGetCellData( oSettings, b, iDataSort, 'sort' )
+						);
+						
+						if ( iTest !== 0 )
+						{
+							return iTest;
+						}
+					}
+					
+					return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
+				} );
+			}
+			
+			/* Alter the sorting classes to take account of the changes */
+			if ( (typeof bApplyClasses == 'undefined' || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
+			{
+				_fnSortingClasses( oSettings );
+			}
+			
+			/* Tell the draw function that we have sorted the data */
+			oSettings.bSorted = true;
+			$(oSettings.oInstance).trigger('sort', oSettings);
+			
+			/* Copy the master data into the draw array and re-draw */
+			if ( oSettings.oFeatures.bFilter )
+			{
+				/* _fnFilter() will redraw the table for us */
+				_fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
+			}
+			else
+			{
+				oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+				oSettings._iDisplayStart = 0; /* reset display back to page 0 */
+				_fnCalculateEnd( oSettings );
+				_fnDraw( oSettings );
+			}
+		}
+		
+		/*
+		 * Function: _fnSortAttachListener
+		 * Purpose:  Attach a sort handler (click) to a node
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           node:nNode - node to attach the handler to
+		 *           int:iDataIndex - column sorting index
+		 *           function:fnCallback - callback function - optional
+		 */
+		function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
+		{
+			$(nNode).bind( 'click.DT', function (e) {
+				/* If the column is not sortable - don't to anything */
+				if ( oSettings.aoColumns[iDataIndex].bSortable === false )
+				{
+					return;
+				}
+				
+				/*
+				 * This is a little bit odd I admit... I declare a temporary function inside the scope of
+				 * _fnBuildHead and the click handler in order that the code presented here can be used 
+				 * twice - once for when bProcessing is enabled, and another time for when it is 
+				 * disabled, as we need to perform slightly different actions.
+				 *   Basically the issue here is that the Javascript engine in modern browsers don't 
+				 * appear to allow the rendering engine to update the display while it is still excuting
+				 * it's thread (well - it does but only after long intervals). This means that the 
+				 * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
+				 * I force an execution break by using setTimeout - but this breaks the expected 
+				 * thread continuation for the end-developer's point of view (their code would execute
+				 * too early), so we on;y do it when we absolutely have to.
+				 */
+				var fnInnerSorting = function () {
+					var iColumn, iNextSort;
+					
+					/* If the shift key is pressed then we are multipe column sorting */
+					if ( e.shiftKey )
+					{
+						/* Are we already doing some kind of sort on this column? */
+						var bFound = false;
+						for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
+						{
+							if ( oSettings.aaSorting[i][0] == iDataIndex )
+							{
+								bFound = true;
+								iColumn = oSettings.aaSorting[i][0];
+								iNextSort = oSettings.aaSorting[i][2]+1;
+								
+								if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' )
+								{
+									/* Reached the end of the sorting options, remove from multi-col sort */
+									oSettings.aaSorting.splice( i, 1 );
+								}
+								else
+								{
+									/* Move onto next sorting direction */
+									oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
+									oSettings.aaSorting[i][2] = iNextSort;
+								}
+								break;
+							}
+						}
+						
+						/* No sort yet - add it in */
+						if ( bFound === false )
+						{
+							oSettings.aaSorting.push( [ iDataIndex, 
+								oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
+						}
+					}
+					else
+					{
+						/* If no shift key then single column sort */
+						if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
+						{
+							iColumn = oSettings.aaSorting[0][0];
+							iNextSort = oSettings.aaSorting[0][2]+1;
+							if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' )
+							{
+								iNextSort = 0;
+							}
+							oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
+							oSettings.aaSorting[0][2] = iNextSort;
+						}
+						else
+						{
+							oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
+							oSettings.aaSorting.push( [ iDataIndex, 
+								oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
+						}
+					}
+					
+					/* Run the sort */
+					_fnSort( oSettings );
+				}; /* /fnInnerSorting */
+				
+				if ( !oSettings.oFeatures.bProcessing )
+				{
+					fnInnerSorting();
+				}
+				else
+				{
+					_fnProcessingDisplay( oSettings, true );
+					setTimeout( function() {
+						fnInnerSorting();
+						if ( !oSettings.oFeatures.bServerSide )
+						{
+							_fnProcessingDisplay( oSettings, false );
+						}
+					}, 0 );
+				}
+				
+				/* Call the user specified callback function - used for async user interaction */
+				if ( typeof fnCallback == 'function' )
+				{
+					fnCallback( oSettings );
+				}
+			} );
+		}
+		
+		/*
+		 * Function: _fnSortingClasses
+		 * Purpose:  Set the sortting classes on the header
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 * Notes:    It is safe to call this function when bSort and bSortClasses are false
+		 */
+		function _fnSortingClasses( oSettings )
+		{
+			var i, iLen, j, jLen, iFound;
+			var aaSort, sClass;
+			var iColumns = oSettings.aoColumns.length;
+			var oClasses = oSettings.oClasses;
+			
+			for ( i=0 ; i<iColumns ; i++ )
+			{
+				if ( oSettings.aoColumns[i].bSortable )
+				{
+					$(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
+						" "+ oSettings.aoColumns[i].sSortingClass );
+				}
+			}
+			
+			if ( oSettings.aaSortingFixed !== null )
+			{
+				aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
+			}
+			else
+			{
+				aaSort = oSettings.aaSorting.slice();
+			}
+			
+			/* Apply the required classes to the header */
+			for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+			{
+				if ( oSettings.aoColumns[i].bSortable )
+				{
+					sClass = oSettings.aoColumns[i].sSortingClass;
+					iFound = -1;
+					for ( j=0 ; j<aaSort.length ; j++ )
+					{
+						if ( aaSort[j][0] == i )
+						{
+							sClass = ( aaSort[j][1] == "asc" ) ?
+								oClasses.sSortAsc : oClasses.sSortDesc;
+							iFound = j;
+							break;
+						}
+					}
+					$(oSettings.aoColumns[i].nTh).addClass( sClass );
+					
+					if ( oSettings.bJUI )
+					{
+						/* jQuery UI uses extra markup */
+						var jqSpan = $("span", oSettings.aoColumns[i].nTh);
+						jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+ 
+							oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed );
+						
+						var sSpanClass;
+						if ( iFound == -1 )
+						{
+						 	sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
+						}
+						else if ( aaSort[iFound][1] == "asc" )
+						{
+							sSpanClass = oClasses.sSortJUIAsc;
+						}
+						else
+						{
+							sSpanClass = oClasses.sSortJUIDesc;
+						}
+						
+						jqSpan.addClass( sSpanClass );
+					}
+				}
+				else
+				{
+					/* No sorting on this column, so add the base class. This will have been assigned by
+					 * _fnAddColumn
+					 */
+					$(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass );
+				}
+			}
+			
+			/* 
+			 * Apply the required classes to the table body
+			 * Note that this is given as a feature switch since it can significantly slow down a sort
+			 * on large data sets (adding and removing of classes is always slow at the best of times..)
+			 * Further to this, note that this code is admitadly fairly ugly. It could be made a lot 
+			 * simpiler using jQuery selectors and add/removeClass, but that is significantly slower
+			 * (on the order of 5 times slower) - hence the direct DOM manipulation here.
+			 * Note that for defered drawing we do use jQuery - the reason being that taking the first
+			 * row found to see if the whole column needs processed can miss classes since the first
+			 * column might be new.
+			 */
+			sClass = oClasses.sSortColumn;
+			
+			if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses )
+			{
+				var nTds = _fnGetTdNodes( oSettings );
+
+				/* Remove the old classes */
+				if ( oSettings.oFeatures.bDeferRender )
+				{
+					$(nTds).removeClass(sClass+'1 '+sClass+'2 '+sClass+'3');
+				}
+				else if ( nTds.length >= iColumns )
+				{
+					for ( i=0 ; i<iColumns ; i++ )
+					{
+						if ( nTds[i].className.indexOf(sClass+"1") != -1 )
+						{
+							for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
+							{
+								nTds[(iColumns*j)+i].className = 
+									$.trim( nTds[(iColumns*j)+i].className.replace( sClass+"1", "" ) );
+							}
+						}
+						else if ( nTds[i].className.indexOf(sClass+"2") != -1 )
+						{
+							for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
+							{
+								nTds[(iColumns*j)+i].className = 
+									$.trim( nTds[(iColumns*j)+i].className.replace( sClass+"2", "" ) );
+							}
+						}
+						else if ( nTds[i].className.indexOf(sClass+"3") != -1 )
+						{
+							for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
+							{
+								nTds[(iColumns*j)+i].className = 
+									$.trim( nTds[(iColumns*j)+i].className.replace( " "+sClass+"3", "" ) );
+							}
+						}
+					}
+				}
+				
+				/* Add the new classes to the table */
+				var iClass = 1, iTargetCol;
+				for ( i=0 ; i<aaSort.length ; i++ )
+				{
+					iTargetCol = parseInt( aaSort[i][0], 10 );
+					for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
+					{
+						nTds[(iColumns*j)+iTargetCol].className += " "+sClass+iClass;
+					}
+					
+					if ( iClass < 3 )
+					{
+						iClass++;
+					}
+				}
+			}
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Feature: Pagination. Note that most of the paging logic is done in 
+		 * _oExt.oPagination
+		 */
+		
+		/*
+		 * Function: _fnFeatureHtmlPaginate
+		 * Purpose:  Generate the node required for default pagination
+		 * Returns:  node
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnFeatureHtmlPaginate ( oSettings )
+		{
+			if ( oSettings.oScroll.bInfinite )
+			{
+				return null;
+			}
+			
+			var nPaginate = document.createElement( 'div' );
+			nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType;
+			
+			_oExt.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate, 
+				function( oSettings ) {
+					_fnCalculateEnd( oSettings );
+					_fnDraw( oSettings );
+				}
+			);
+			
+			/* Add a draw callback for the pagination on first instance, to update the paging display */
+			if ( typeof oSettings.aanFeatures.p == "undefined" )
+			{
+				oSettings.aoDrawCallback.push( {
+					"fn": function( oSettings ) {
+						_oExt.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) {
+							_fnCalculateEnd( oSettings );
+							_fnDraw( oSettings );
+						} );
+					},
+					"sName": "pagination"
+				} );
+			}
+			return nPaginate;
+		}
+		
+		/*
+		 * Function: _fnPageChange
+		 * Purpose:  Alter the display settings to change the page
+		 * Returns:  bool:true - page has changed, false - no change (no effect) eg 'first' on page 1
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           string:sAction - paging action to take: "first", "previous", "next" or "last"
+		 */
+		function _fnPageChange ( oSettings, sAction )
+		{
+			var iOldStart = oSettings._iDisplayStart;
+			
+			if ( sAction == "first" )
+			{
+				oSettings._iDisplayStart = 0;
+			}
+			else if ( sAction == "previous" )
+			{
+				oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
+					oSettings._iDisplayStart - oSettings._iDisplayLength :
+					0;
+				
+				/* Correct for underrun */
+				if ( oSettings._iDisplayStart < 0 )
+				{
+				  oSettings._iDisplayStart = 0;
+				}
+			}
+			else if ( sAction == "next" )
+			{
+				if ( oSettings._iDisplayLength >= 0 )
+				{
+					/* Make sure we are not over running the display array */
+					if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() )
+					{
+						oSettings._iDisplayStart += oSettings._iDisplayLength;
+					}
+				}
+				else
+				{
+					oSettings._iDisplayStart = 0;
+				}
+			}
+			else if ( sAction == "last" )
+			{
+				if ( oSettings._iDisplayLength >= 0 )
+				{
+					var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1;
+					oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength;
+				}
+				else
+				{
+					oSettings._iDisplayStart = 0;
+				}
+			}
+			else
+			{
+				_fnLog( oSettings, 0, "Unknown paging action: "+sAction );
+			}
+			$(oSettings.oInstance).trigger('page', oSettings);
+			
+			return iOldStart != oSettings._iDisplayStart;
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Feature: HTML info
+		 */
+		
+		/*
+		 * Function: _fnFeatureHtmlInfo
+		 * Purpose:  Generate the node required for the info display
+		 * Returns:  node
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnFeatureHtmlInfo ( oSettings )
+		{
+			var nInfo = document.createElement( 'div' );
+			nInfo.className = oSettings.oClasses.sInfo;
+			
+			/* Actions that are to be taken once only for this feature */
+			if ( typeof oSettings.aanFeatures.i == "undefined" )
+			{
+				/* Add draw callback */
+				oSettings.aoDrawCallback.push( {
+					"fn": _fnUpdateInfo,
+					"sName": "information"
+				} );
+				
+				/* Add id */
+				if ( oSettings.sTableId !== '' )
+				{
+					nInfo.setAttribute( 'id', oSettings.sTableId+'_info' );
+				}
+			}
+			
+			return nInfo;
+		}
+		
+		/*
+		 * Function: _fnUpdateInfo
+		 * Purpose:  Update the information elements in the display
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnUpdateInfo ( oSettings )
+		{
+			/* Show information about the table */
+			if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 )
+			{
+				return;
+			}
+			
+			var
+				iStart = oSettings._iDisplayStart+1, iEnd = oSettings.fnDisplayEnd(),
+				iMax = oSettings.fnRecordsTotal(), iTotal = oSettings.fnRecordsDisplay(),
+				sStart = oSettings.fnFormatNumber( iStart ), sEnd = oSettings.fnFormatNumber( iEnd ),
+				sMax = oSettings.fnFormatNumber( iMax ), sTotal = oSettings.fnFormatNumber( iTotal ),
+				sOut;
+			
+			/* When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
+			 * internally
+			 */
+			if ( oSettings.oScroll.bInfinite )
+			{
+				sStart = oSettings.fnFormatNumber( 1 );
+			}
+			
+			if ( oSettings.fnRecordsDisplay() === 0 && 
+				   oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() )
+			{
+				/* Empty record set */
+				sOut = oSettings.oLanguage.sInfoEmpty+ oSettings.oLanguage.sInfoPostFix;
+			}
+			else if ( oSettings.fnRecordsDisplay() === 0 )
+			{
+				/* Rmpty record set after filtering */
+				sOut = oSettings.oLanguage.sInfoEmpty +' '+ 
+					oSettings.oLanguage.sInfoFiltered.replace('_MAX_', sMax)+
+						oSettings.oLanguage.sInfoPostFix;
+			}
+			else if ( oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() )
+			{
+				/* Normal record set */
+				sOut = oSettings.oLanguage.sInfo.
+						replace('_START_', sStart).
+						replace('_END_',   sEnd).
+						replace('_TOTAL_', sTotal)+ 
+					oSettings.oLanguage.sInfoPostFix;
+			}
+			else
+			{
+				/* Record set after filtering */
+				sOut = oSettings.oLanguage.sInfo.
+						replace('_START_', sStart).
+						replace('_END_',   sEnd).
+						replace('_TOTAL_', sTotal) +' '+ 
+					oSettings.oLanguage.sInfoFiltered.replace('_MAX_', 
+						oSettings.fnFormatNumber(oSettings.fnRecordsTotal()))+ 
+					oSettings.oLanguage.sInfoPostFix;
+			}
+			
+			if ( oSettings.oLanguage.fnInfoCallback !== null )
+			{
+				sOut = oSettings.oLanguage.fnInfoCallback( oSettings, iStart, iEnd, iMax, iTotal, sOut );
+			}
+			
+			var n = oSettings.aanFeatures.i;
+			for ( var i=0, iLen=n.length ; i<iLen ; i++ )
+			{
+				$(n[i]).html( sOut );
+			}
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Feature: Length change
+		 */
+		
+		/*
+		 * Function: _fnFeatureHtmlLength
+		 * Purpose:  Generate the node required for user display length changing
+		 * Returns:  node
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnFeatureHtmlLength ( oSettings )
+		{
+			if ( oSettings.oScroll.bInfinite )
+			{
+				return null;
+			}
+			
+			/* This can be overruled by not using the _MENU_ var/macro in the language variable */
+			var sName = (oSettings.sTableId === "") ? "" : 'name="'+oSettings.sTableId+'_length"';
+			var sStdMenu = '<select size="1" '+sName+'>';
+			var i, iLen;
+			
+			if ( oSettings.aLengthMenu.length == 2 && typeof oSettings.aLengthMenu[0] == 'object' && 
+					typeof oSettings.aLengthMenu[1] == 'object' )
+			{
+				for ( i=0, iLen=oSettings.aLengthMenu[0].length ; i<iLen ; i++ )
+				{
+					sStdMenu += '<option value="'+oSettings.aLengthMenu[0][i]+'">'+
+						oSettings.aLengthMenu[1][i]+'</option>';
+				}
+			}
+			else
+			{
+				for ( i=0, iLen=oSettings.aLengthMenu.length ; i<iLen ; i++ )
+				{
+					sStdMenu += '<option value="'+oSettings.aLengthMenu[i]+'">'+
+						oSettings.aLengthMenu[i]+'</option>';
+				}
+			}
+			sStdMenu += '</select>';
+			
+			var nLength = document.createElement( 'div' );
+			if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.l == "undefined" )
+			{
+				nLength.setAttribute( 'id', oSettings.sTableId+'_length' );
+			}
+			nLength.className = oSettings.oClasses.sLength;
+			nLength.innerHTML = '<label>'+oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu )+'</label>';
+			
+			/*
+			 * Set the length to the current display length - thanks to Andrea Pavlovic for this fix,
+			 * and Stefan Skopnik for fixing the fix!
+			 */
+			$('select option[value="'+oSettings._iDisplayLength+'"]',nLength).attr("selected",true);
+			
+			$('select', nLength).bind( 'change.DT', function(e) {
+				var iVal = $(this).val();
+				
+				/* Update all other length options for the new display */
+				var n = oSettings.aanFeatures.l;
+				for ( i=0, iLen=n.length ; i<iLen ; i++ )
+				{
+					if ( n[i] != this.parentNode )
+					{
+						$('select', n[i]).val( iVal );
+					}
+				}
+				
+				/* Redraw the table */
+				oSettings._iDisplayLength = parseInt(iVal, 10);
+				_fnCalculateEnd( oSettings );
+				
+				/* If we have space to show extra rows (backing up from the end point - then do so */
+				if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() )
+				{
+					oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength;
+					if ( oSettings._iDisplayStart < 0 )
+					{
+						oSettings._iDisplayStart = 0;
+					}
+				}
+				
+				if ( oSettings._iDisplayLength == -1 )
+				{
+					oSettings._iDisplayStart = 0;
+				}
+				
+				_fnDraw( oSettings );
+			} );
+			
+			return nLength;
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Feature: Processing incidator
+		 */
+		
+		/*
+		 * Function: _fnFeatureHtmlProcessing
+		 * Purpose:  Generate the node required for the processing node
+		 * Returns:  node
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnFeatureHtmlProcessing ( oSettings )
+		{
+			var nProcessing = document.createElement( 'div' );
+			
+			if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.r == "undefined" )
+			{
+				nProcessing.setAttribute( 'id', oSettings.sTableId+'_processing' );
+			}
+			nProcessing.innerHTML = oSettings.oLanguage.sProcessing;
+			nProcessing.className = oSettings.oClasses.sProcessing;
+			oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable );
+			
+			return nProcessing;
+		}
+		
+		/*
+		 * Function: _fnProcessingDisplay
+		 * Purpose:  Display or hide the processing indicator
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           bool:
+		 *   true - show the processing indicator
+		 *   false - don't show
+		 */
+		function _fnProcessingDisplay ( oSettings, bShow )
+		{
+			if ( oSettings.oFeatures.bProcessing )
+			{
+				var an = oSettings.aanFeatures.r;
+				for ( var i=0, iLen=an.length ; i<iLen ; i++ )
+				{
+					an[i].style.visibility = bShow ? "visible" : "hidden";
+				}
+			}
+		}
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Support functions
+		 */
+		
+		/*
+		 * Function: _fnVisibleToColumnIndex
+		 * Purpose:  Covert the index of a visible column to the index in the data array (take account
+		 *   of hidden columns)
+		 * Returns:  int:i - the data index
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnVisibleToColumnIndex( oSettings, iMatch )
+		{
+			var iColumn = -1;
+			
+			for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
+			{
+				if ( oSettings.aoColumns[i].bVisible === true )
+				{
+					iColumn++;
+				}
+				
+				if ( iColumn == iMatch )
+				{
+					return i;
+				}
+			}
+			
+			return null;
+		}
+		
+		/*
+		 * Function: _fnColumnIndexToVisible
+		 * Purpose:  Covert the index of an index in the data array and convert it to the visible
+		 *   column index (take account of hidden columns)
+		 * Returns:  int:i - the data index
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnColumnIndexToVisible( oSettings, iMatch )
+		{
+			var iVisible = -1;
+			for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
+			{
+				if ( oSettings.aoColumns[i].bVisible === true )
+				{
+					iVisible++;
+				}
+				
+				if ( i == iMatch )
+				{
+					return oSettings.aoColumns[i].bVisible === true ? iVisible : null;
+				}
+			}
+			
+			return null;
+		}
+		
+		
+		/*
+		 * Function: _fnNodeToDataIndex
+		 * Purpose:  Take a TR element and convert it to an index in aoData
+		 * Returns:  int:i - index if found, null if not
+		 * Inputs:   object:s - dataTables settings object
+		 *           node:n - the TR element to find
+		 */
+		function _fnNodeToDataIndex( s, n )
+		{
+			var i, iLen;
+			
+			/* Optimisation - see if the nodes which are currently visible match, since that is
+			 * the most likely node to be asked for (a selector or event for example)
+			 */
+			for ( i=s._iDisplayStart, iLen=s._iDisplayEnd ; i<iLen ; i++ )
+			{
+				if ( s.aoData[ s.aiDisplay[i] ].nTr == n )
+				{
+					return s.aiDisplay[i];
+				}
+			}
+			
+			/* Otherwise we are in for a slog through the whole data cache */
+			for ( i=0, iLen=s.aoData.length ; i<iLen ; i++ )
+			{
+				if ( s.aoData[i].nTr == n )
+				{
+					return i;
+				}
+			}
+			return null;
+		}
+		
+		/*
+		 * Function: _fnVisbleColumns
+		 * Purpose:  Get the number of visible columns
+		 * Returns:  int:i - the number of visible columns
+		 * Inputs:   object:oS - dataTables settings object
+		 */
+		function _fnVisbleColumns( oS )
+		{
+			var iVis = 0;
+			for ( var i=0 ; i<oS.aoColumns.length ; i++ )
+			{
+				if ( oS.aoColumns[i].bVisible === true )
+				{
+					iVis++;
+				}
+			}
+			return iVis;
+		}
+		
+		/*
+		 * Function: _fnCalculateEnd
+		 * Purpose:  Rcalculate the end point based on the start point
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnCalculateEnd( oSettings )
+		{
+			if ( oSettings.oFeatures.bPaginate === false )
+			{
+				oSettings._iDisplayEnd = oSettings.aiDisplay.length;
+			}
+			else
+			{
+				/* Set the end point of the display - based on how many elements there are
+				 * still to display
+				 */
+				if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length ||
+					   oSettings._iDisplayLength == -1 )
+				{
+					oSettings._iDisplayEnd = oSettings.aiDisplay.length;
+				}
+				else
+				{
+					oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength;
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnConvertToWidth
+		 * Purpose:  Convert a CSS unit width to pixels (e.g. 2em)
+		 * Returns:  int:iWidth - width in pixels
+		 * Inputs:   string:sWidth - width to be converted
+		 *           node:nParent - parent to get the with for (required for
+		 *             relative widths) - optional
+		 */
+		function _fnConvertToWidth ( sWidth, nParent )
+		{
+			if ( !sWidth || sWidth === null || sWidth === '' )
+			{
+				return 0;
+			}
+			
+			if ( typeof nParent == "undefined" )
+			{
+				nParent = document.getElementsByTagName('body')[0];
+			}
+			
+			var iWidth;
+			var nTmp = document.createElement( "div" );
+			nTmp.style.width = _fnStringToCss( sWidth );
+			
+			nParent.appendChild( nTmp );
+			iWidth = nTmp.offsetWidth;
+			nParent.removeChild( nTmp );
+			
+			return ( iWidth );
+		}
+		
+		/*
+		 * Function: _fnCalculateColumnWidths
+		 * Purpose:  Calculate the width of columns for the table
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnCalculateColumnWidths ( oSettings )
+		{
+			var iTableWidth = oSettings.nTable.offsetWidth;
+			var iUserInputs = 0;
+			var iTmpWidth;
+			var iVisibleColumns = 0;
+			var iColums = oSettings.aoColumns.length;
+			var i, iIndex, iCorrector, iWidth;
+			var oHeaders = $('th', oSettings.nTHead);
+			
+			/* Convert any user input sizes into pixel sizes */
+			for ( i=0 ; i<iColums ; i++ )
+			{
+				if ( oSettings.aoColumns[i].bVisible )
+				{
+					iVisibleColumns++;
+					
+					if ( oSettings.aoColumns[i].sWidth !== null )
+					{
+						iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig, 
+							oSettings.nTable.parentNode );
+						if ( iTmpWidth !== null )
+						{
+							oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
+						}
+							
+						iUserInputs++;
+					}
+				}
+			}
+			
+			/* If the number of columns in the DOM equals the number that we have to process in 
+			 * DataTables, then we can use the offsets that are created by the web-browser. No custom 
+			 * sizes can be set in order for this to happen, nor scrolling used
+			 */
+			if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
+				oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
+			{
+				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+				{
+					iTmpWidth = $(oHeaders[i]).width();
+					if ( iTmpWidth !== null )
+					{
+						oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
+					}
+				}
+			}
+			else
+			{
+				/* Otherwise we are going to have to do some calculations to get the width of each column.
+				 * Construct a 1 row table with the widest node in the data, and any user defined widths,
+				 * then insert it into the DOM and allow the browser to do all the hard work of
+				 * calculating table widths.
+				 */
+				var
+					nCalcTmp = oSettings.nTable.cloneNode( false ),
+					nTheadClone = oSettings.nTHead.cloneNode(true),
+					nBody = document.createElement( 'tbody' ),
+					nTr = document.createElement( 'tr' ),
+					nDivSizing;
+				
+				nCalcTmp.removeAttribute( "id" );
+				nCalcTmp.appendChild( nTheadClone );
+				if ( oSettings.nTFoot !== null )
+				{
+					nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
+					_fnApplyToChildren( function(n) {
+						n.style.width = "";
+					}, nCalcTmp.getElementsByTagName('tr') );
+				}
+				
+				nCalcTmp.appendChild( nBody );
+				nBody.appendChild( nTr );
+				
+				/* Remove any sizing that was previously applied by the styles */
+				var jqColSizing = $('thead th', nCalcTmp);
+				if ( jqColSizing.length === 0 )
+				{
+					jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
+				}
+
+				/* Apply custom sizing to the cloned header */
+				var nThs = _fnGetUniqueThs( oSettings, nTheadClone );
+				iCorrector = 0;
+				for ( i=0 ; i<iColums ; i++ )
+				{
+					var oColumn = oSettings.aoColumns[i];
+					if ( oColumn.bVisible && oColumn.sWidthOrig !== null && oColumn.sWidthOrig !== "" )
+					{
+						nThs[i-iCorrector].style.width = _fnStringToCss( oColumn.sWidthOrig );
+					}
+					else if ( oColumn.bVisible )
+					{
+						nThs[i-iCorrector].style.width = "";
+					}
+					else
+					{
+						iCorrector++;
+					}
+				}
+
+				/* Find the biggest td for each column and put it into the table */
+				for ( i=0 ; i<iColums ; i++ )
+				{
+					if ( oSettings.aoColumns[i].bVisible )
+					{
+						var nTd = _fnGetWidestNode( oSettings, i );
+						if ( nTd !== null )
+						{
+							nTd = nTd.cloneNode(true);
+							if ( oSettings.aoColumns[i].sContentPadding !== "" )
+							{
+								nTd.innerHTML += oSettings.aoColumns[i].sContentPadding;
+							}
+							nTr.appendChild( nTd );
+						}
+					}
+				}
+				
+				/* Build the table and 'display' it */
+				var nWrapper = oSettings.nTable.parentNode;
+				nWrapper.appendChild( nCalcTmp );
+				
+				/* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
+				 * when not scrolling leave the table width as it is. This results in slightly different,
+				 * but I think correct behaviour
+				 */
+				if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
+				{
+					nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
+				}
+				else if ( oSettings.oScroll.sX !== "" )
+				{
+					nCalcTmp.style.width = "";
+					if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
+					{
+						nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
+					}
+				}
+				else if ( oSettings.oScroll.sY !== "" )
+				{
+					nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
+				}
+				nCalcTmp.style.visibility = "hidden";
+				
+				/* Scrolling considerations */
+				_fnScrollingWidthAdjust( oSettings, nCalcTmp );
+				
+				/* Read the width's calculated by the browser and store them for use by the caller. We
+				 * first of all try to use the elements in the body, but it is possible that there are
+				 * no elements there, under which circumstances we use the header elements
+				 */
+				var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
+				if ( oNodes.length === 0 )
+				{
+					oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
+				}
+
+				/* Browsers need a bit of a hand when a width is assigned to any columns when 
+				 * x-scrolling as they tend to collapse the table to the min-width, even if
+				 * we sent the column widths. So we need to keep track of what the table width
+				 * should be by summing the user given values, and the automatic values
+				 */
+				if ( oSettings.oScroll.sX !== "" )
+				{
+					var iTotal = 0;
+					iCorrector = 0;
+					for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+					{
+						if ( oSettings.aoColumns[i].bVisible )
+						{
+							if ( oSettings.aoColumns[i].sWidthOrig === null )
+							{
+								iTotal += $(oNodes[iCorrector]).outerWidth();
+							}
+							else
+							{
+								iTotal += parseInt(oSettings.aoColumns[i].sWidth.replace('px',''), 10) +
+									($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
+							}
+							iCorrector++;
+						}
+					}
+				
+					nCalcTmp.style.width = _fnStringToCss( iTotal );
+					oSettings.nTable.style.width = _fnStringToCss( iTotal );
+				}
+
+				iCorrector = 0;
+				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+				{
+					if ( oSettings.aoColumns[i].bVisible )
+					{
+						iWidth = $(oNodes[iCorrector]).width();
+						if ( iWidth !== null && iWidth > 0 )
+						{
+							oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
+						}
+						iCorrector++;
+					}
+				}
+				
+				oSettings.nTable.style.width = _fnStringToCss( $(nCalcTmp).outerWidth() );
+				nCalcTmp.parentNode.removeChild( nCalcTmp );
+			}
+		}
+		
+		/*
+		 * Function: _fnScrollingWidthAdjust
+		 * Purpose:  Adjust a table's width to take account of scrolling
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           node:n - table node
+		 */
+		function _fnScrollingWidthAdjust ( oSettings, n )
+		{
+			if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
+			{
+				/* When y-scrolling only, we want to remove the width of the scroll bar so the table
+				 * + scroll bar will fit into the area avaialble.
+				 */
+				var iOrigWidth = $(n).width();
+				n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
+			}
+			else if ( oSettings.oScroll.sX !== "" )
+			{
+				/* When x-scrolling both ways, fix the table at it's current size, without adjusting */
+				n.style.width = _fnStringToCss( $(n).outerWidth() );
+			}
+		}
+		
+		/*
+		 * Function: _fnGetWidestNode
+		 * Purpose:  Get the widest node
+		 * Returns:  string: - max strlens for each column
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iCol - column of interest
+		 */
+		function _fnGetWidestNode( oSettings, iCol )
+		{
+			var iMaxIndex = _fnGetMaxLenString( oSettings, iCol );
+			if ( iMaxIndex < 0 )
+			{
+				return null;
+			}
+
+			if ( oSettings.aoData[iMaxIndex].nTr === null )
+			{
+				var n = document.createElement('td');
+				n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' );
+				return n;
+			}
+			return _fnGetTdNodes(oSettings, iMaxIndex)[iCol];
+		}
+		
+		/*
+		 * Function: _fnGetMaxLenString
+		 * Purpose:  Get the maximum strlen for each data column
+		 * Returns:  string: - max strlens for each column
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iCol - column of interest
+		 */
+		function _fnGetMaxLenString( oSettings, iCol )
+		{
+			var iMax = -1;
+			var iMaxIndex = -1;
+			
+			for ( var i=0 ; i<oSettings.aoData.length ; i++ )
+			{
+				var s = _fnGetCellData( oSettings, i, iCol, 'display' )+"";
+				s = s.replace( /<.*?>/g, "" );
+				if ( s.length > iMax )
+				{
+					iMax = s.length;
+					iMaxIndex = i;
+				}
+			}
+			
+			return iMaxIndex;
+		}
+		
+		/*
+		 * Function: _fnStringToCss
+		 * Purpose:  Append a CSS unit (only if required) to a string
+		 * Returns:  0 if match, 1 if length is different, 2 if no match
+		 * Inputs:   array:aArray1 - first array
+		 *           array:aArray2 - second array
+		 */
+		function _fnStringToCss( s )
+		{
+			if ( s === null )
+			{
+				return "0px";
+			}
+			
+			if ( typeof s == 'number' )
+			{
+				if ( s < 0 )
+				{
+					return "0px";
+				}
+				return s+"px";
+			}
+			
+			/* Check if the last character is not 0-9 */
+			var c = s.charCodeAt( s.length-1 );
+			if (c < 0x30 || c > 0x39)
+			{
+				return s;
+			}
+			return s+"px";
+		}
+		
+		/*
+		 * Function: _fnArrayCmp
+		 * Purpose:  Compare two arrays
+		 * Returns:  0 if match, 1 if length is different, 2 if no match
+		 * Inputs:   array:aArray1 - first array
+		 *           array:aArray2 - second array
+		 */
+		function _fnArrayCmp( aArray1, aArray2 )
+		{
+			if ( aArray1.length != aArray2.length )
+			{
+				return 1;
+			}
+			
+			for ( var i=0 ; i<aArray1.length ; i++ )
+			{
+				if ( aArray1[i] != aArray2[i] )
+				{
+					return 2;
+				}
+			}
+			
+			return 0;
+		}
+		
+		/*
+		 * Function: _fnDetectType
+		 * Purpose:  Get the sort type based on an input string
+		 * Returns:  string: - type (defaults to 'string' if no type can be detected)
+		 * Inputs:   string:sData - data we wish to know the type of
+		 * Notes:    This function makes use of the DataTables plugin objct _oExt 
+		 *   (.aTypes) such that new types can easily be added.
+		 */
+		function _fnDetectType( sData )
+		{
+			var aTypes = _oExt.aTypes;
+			var iLen = aTypes.length;
+			
+			for ( var i=0 ; i<iLen ; i++ )
+			{
+				var sType = aTypes[i]( sData );
+				if ( sType !== null )
+				{
+					return sType;
+				}
+			}
+			
+			return 'string';
+		}
+		
+		/*
+		 * Function: _fnSettingsFromNode
+		 * Purpose:  Return the settings object for a particular table
+		 * Returns:  object: Settings object - or null if not found
+		 * Inputs:   node:nTable - table we are using as a dataTable
+		 */
+		function _fnSettingsFromNode ( nTable )
+		{
+			for ( var i=0 ; i<_aoSettings.length ; i++ )
+			{
+				if ( _aoSettings[i].nTable == nTable )
+				{
+					return _aoSettings[i];
+				}
+			}
+			
+			return null;
+		}
+		
+		/*
+		 * Function: _fnGetDataMaster
+		 * Purpose:  Return an array with the full table data
+		 * Returns:  array array:aData - Master data array
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnGetDataMaster ( oSettings )
+		{
+			var aData = [];
+			var iLen = oSettings.aoData.length;
+			for ( var i=0 ; i<iLen; i++ )
+			{
+				aData.push( oSettings.aoData[i]._aData );
+			}
+			return aData;
+		}
+		
+		/*
+		 * Function: _fnGetTrNodes
+		 * Purpose:  Return an array with the TR nodes for the table
+		 * Returns:  array: - TR array
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnGetTrNodes ( oSettings )
+		{
+			var aNodes = [];
+			for ( var i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+			{
+				if ( oSettings.aoData[i].nTr !== null )
+				{
+					aNodes.push( oSettings.aoData[i].nTr );
+				}
+			}
+			return aNodes;
+		}
+		
+		/*
+		 * Function: _fnGetTdNodes
+		 * Purpose:  Return an flat array with all TD nodes for the table, or row
+		 * Returns:  array: - TD array
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iIndividualRow - aoData index to get the nodes for - optional if not
+		 *             given then the return array will contain all nodes for the table
+		 */
+		function _fnGetTdNodes ( oSettings, iIndividualRow )
+		{
+			var anReturn = [];
+			var iCorrector;
+			var anTds;
+			var iRow, iRows=oSettings.aoData.length,
+				iColumn, iColumns, oData, sNodeName, iStart=0, iEnd=iRows;
+			
+			/* Allow the collection to be limited to just one row */
+			if ( typeof iIndividualRow != 'undefined' )
+			{
+				iStart = iIndividualRow;
+				iEnd = iIndividualRow+1;
+			}
+
+			for ( iRow=iStart ; iRow<iEnd ; iRow++ )
+			{
+				oData = oSettings.aoData[iRow];
+				if ( oData.nTr !== null )
+				{
+					/* get the TD child nodes - taking into account text etc nodes */
+					anTds = [];
+					for ( iColumn=0, iColumns=oData.nTr.childNodes.length ; iColumn<iColumns ; iColumn++ )
+					{
+						sNodeName = oData.nTr.childNodes[iColumn].nodeName.toLowerCase();
+						if ( sNodeName == 'td' || sNodeName == 'th' )
+						{
+							anTds.push( oData.nTr.childNodes[iColumn] );
+						}
+					}
+
+					iCorrector = 0;
+					for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
+					{
+						if ( oSettings.aoColumns[iColumn].bVisible )
+						{
+							anReturn.push( anTds[iColumn-iCorrector] );
+						}
+						else
+						{
+							anReturn.push( oData._anHidden[iColumn] );
+							iCorrector++;
+						}
+					}
+				}
+			}
+
+			return anReturn;
+		}
+		
+		/*
+		 * Function: _fnEscapeRegex
+		 * Purpose:  scape a string stuch that it can be used in a regular expression
+		 * Returns:  string: - escaped string
+		 * Inputs:   string:sVal - string to escape
+		 */
+		function _fnEscapeRegex ( sVal )
+		{
+			var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^' ];
+		  var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' );
+		  return sVal.replace(reReplace, '\\$1');
+		}
+		
+		/*
+		 * Function: _fnDeleteIndex
+		 * Purpose:  Take an array of integers (index array) and remove a target integer (value - not 
+		 *             the key!)
+		 * Returns:  -
+		 * Inputs:   a:array int - Index array to target
+		 *           int:iTarget - value to find
+		 */
+		function _fnDeleteIndex( a, iTarget )
+		{
+			var iTargetIndex = -1;
+			
+			for ( var i=0, iLen=a.length ; i<iLen ; i++ )
+			{
+				if ( a[i] == iTarget )
+				{
+					iTargetIndex = i;
+				}
+				else if ( a[i] > iTarget )
+				{
+					a[i]--;
+				}
+			}
+			
+			if ( iTargetIndex != -1 )
+			{
+				a.splice( iTargetIndex, 1 );
+			}
+		}
+		
+		/*
+		 * Function: _fnReOrderIndex
+		 * Purpose:  Figure out how to reorder a display list
+		 * Returns:  array int:aiReturn - index list for reordering
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnReOrderIndex ( oSettings, sColumns )
+		{
+			var aColumns = sColumns.split(',');
+			var aiReturn = [];
+			
+			for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+			{
+				for ( var j=0 ; j<iLen ; j++ )
+				{
+					if ( oSettings.aoColumns[i].sName == aColumns[j] )
+					{
+						aiReturn.push( j );
+						break;
+					}
+				}
+			}
+			
+			return aiReturn;
+		}
+		
+		/*
+		 * Function: _fnColumnOrdering
+		 * Purpose:  Get the column ordering that DataTables expects
+		 * Returns:  string: - comma separated list of names
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnColumnOrdering ( oSettings )
+		{
+			var sNames = '';
+			for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+			{
+				sNames += oSettings.aoColumns[i].sName+',';
+			}
+			if ( sNames.length == iLen )
+			{
+				return "";
+			}
+			return sNames.slice(0, -1);
+		}
+		
+		/*
+		 * Function: _fnLog
+		 * Purpose:  Log an error message
+		 * Returns:  -
+		 * Inputs:   int:iLevel - log error messages, or display them to the user
+		 *           string:sMesg - error message
+		 */
+		function _fnLog( oSettings, iLevel, sMesg )
+		{
+			var sAlert = oSettings.sTableId === "" ?
+			 	"DataTables warning: " +sMesg :
+			 	"DataTables warning (table id = '"+oSettings.sTableId+"'): " +sMesg;
+			
+			if ( iLevel === 0 )
+			{
+				if ( _oExt.sErrMode == 'alert' )
+				{
+					alert( sAlert );
+				}
+				else
+				{
+					throw sAlert;
+				}
+				return;
+			}
+			else if ( typeof console != 'undefined' && typeof console.log != 'undefined' )
+			{
+				console.log( sAlert );
+			}
+		}
+		
+		/*
+		 * Function: _fnClearTable
+		 * Purpose:  Nuke the table
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnClearTable( oSettings )
+		{
+			oSettings.aoData.splice( 0, oSettings.aoData.length );
+			oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length );
+			oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length );
+			_fnCalculateEnd( oSettings );
+		}
+		
+		/*
+		 * Function: _fnSaveState
+		 * Purpose:  Save the state of a table in a cookie such that the page can be reloaded
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 */
+		function _fnSaveState ( oSettings )
+		{
+			if ( !oSettings.oFeatures.bStateSave || typeof oSettings.bDestroying != 'undefined' )
+			{
+				return;
+			}
+			
+			/* Store the interesting variables */
+			var i, iLen, sTmp;
+			var sValue = "{";
+			sValue += '"iCreate":'+ new Date().getTime()+',';
+			sValue += '"iStart":'+ (oSettings.oScroll.bInfinite ? 0 : oSettings._iDisplayStart)+',';
+			sValue += '"iEnd":'+ (oSettings.oScroll.bInfinite ? oSettings._iDisplayLength : oSettings._iDisplayEnd)+',';
+			sValue += '"iLength":'+ oSettings._iDisplayLength+',';
+			sValue += '"sFilter":"'+ encodeURIComponent(oSettings.oPreviousSearch.sSearch)+'",';
+			sValue += '"sFilterEsc":'+ !oSettings.oPreviousSearch.bRegex+',';
+			
+			sValue += '"aaSorting":[ ';
+			for ( i=0 ; i<oSettings.aaSorting.length ; i++ )
+			{
+				sValue += '['+oSettings.aaSorting[i][0]+',"'+oSettings.aaSorting[i][1]+'"],';
+			}
+			sValue = sValue.substring(0, sValue.length-1);
+			sValue += "],";
+			
+			sValue += '"aaSearchCols":[ ';
+			for ( i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
+			{
+				sValue += '["'+encodeURIComponent(oSettings.aoPreSearchCols[i].sSearch)+
+					'",'+!oSettings.aoPreSearchCols[i].bRegex+'],';
+			}
+			sValue = sValue.substring(0, sValue.length-1);
+			sValue += "],";
+			
+			sValue += '"abVisCols":[ ';
+			for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+			{
+				sValue += oSettings.aoColumns[i].bVisible+",";
+			}
+			sValue = sValue.substring(0, sValue.length-1);
+			sValue += "]";
+			
+			/* Save state from any plug-ins */
+			for ( i=0, iLen=oSettings.aoStateSave.length ; i<iLen ; i++ )
+			{
+				sTmp = oSettings.aoStateSave[i].fn( oSettings, sValue );
+				if ( sTmp !== "" )
+				{
+					sValue = sTmp;
+				}
+			}
+			
+			sValue += "}";
+			
+			_fnCreateCookie( oSettings.sCookiePrefix+oSettings.sInstance, sValue, 
+				oSettings.iCookieDuration, oSettings.sCookiePrefix, oSettings.fnCookieCallback );
+		}
+		
+		/*
+		 * Function: _fnLoadState
+		 * Purpose:  Attempt to load a saved table state from a cookie
+		 * Returns:  -
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           object:oInit - DataTables init object so we can override settings
+		 */
+		function _fnLoadState ( oSettings, oInit )
+		{
+			if ( !oSettings.oFeatures.bStateSave )
+			{
+				return;
+			}
+			
+			var oData, i, iLen;
+			var sData = _fnReadCookie( oSettings.sCookiePrefix+oSettings.sInstance );
+			if ( sData !== null && sData !== '' )
+			{
+				/* Try/catch the JSON eval - if it is bad then we ignore it - note that 1.7.0 and before
+				 * incorrectly used single quotes for some strings - hence the replace below
+				 */
+				try
+				{
+					oData = (typeof $.parseJSON == 'function') ? 
+						$.parseJSON( sData.replace(/'/g, '"') ) : eval( '('+sData+')' );
+				}
+				catch( e )
+				{
+					return;
+				}
+				
+				/* Allow custom and plug-in manipulation functions to alter the data set which was
+				 * saved, and also reject any saved state by returning false
+				 */
+				for ( i=0, iLen=oSettings.aoStateLoad.length ; i<iLen ; i++ )
+				{
+					if ( !oSettings.aoStateLoad[i].fn( oSettings, oData ) )
+					{
+						return;
+					}
+				}
+				
+				/* Store the saved state so it might be accessed at any time (particualrly a plug-in */
+				oSettings.oLoadedState = $.extend( true, {}, oData );
+				
+				/* Restore key features */
+				oSettings._iDisplayStart = oData.iStart;
+				oSettings.iInitDisplayStart = oData.iStart;
+				oSettings._iDisplayEnd = oData.iEnd;
+				oSettings._iDisplayLength = oData.iLength;
+				oSettings.oPreviousSearch.sSearch = decodeURIComponent(oData.sFilter);
+				oSettings.aaSorting = oData.aaSorting.slice();
+				oSettings.saved_aaSorting = oData.aaSorting.slice();
+				
+				/*
+				 * Search filtering - global reference added in 1.4.1
+				 * Note that we use a 'not' for the value of the regular expression indicator to maintain
+				 * compatibility with pre 1.7 versions, where this was basically inverted. Added in 1.7.0
+				 */
+				if ( typeof oData.sFilterEsc != 'undefined' )
+				{
+					oSettings.oPreviousSearch.bRegex = !oData.sFilterEsc;
+				}
+				
+				/* Column filtering - added in 1.5.0 beta 6 */
+				if ( typeof oData.aaSearchCols != 'undefined' )
+				{
+					for ( i=0 ; i<oData.aaSearchCols.length ; i++ )
+					{
+						oSettings.aoPreSearchCols[i] = {
+							"sSearch": decodeURIComponent(oData.aaSearchCols[i][0]),
+							"bRegex": !oData.aaSearchCols[i][1]
+						};
+					}
+				}
+				
+				/* Column visibility state - added in 1.5.0 beta 10 */
+				if ( typeof oData.abVisCols != 'undefined' )
+				{
+					/* Pass back visibiliy settings to the init handler, but to do not here override
+					 * the init object that the user might have passed in
+					 */
+					oInit.saved_aoColumns = [];
+					for ( i=0 ; i<oData.abVisCols.length ; i++ )
+					{
+						oInit.saved_aoColumns[i] = {};
+						oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
+					}
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnCreateCookie
+		 * Purpose:  Create a new cookie with a value to store the state of a table
+		 * Returns:  -
+		 * Inputs:   string:sName - name of the cookie to create
+		 *           string:sValue - the value the cookie should take
+		 *           int:iSecs - duration of the cookie
+		 *           string:sBaseName - sName is made up of the base + file name - this is the base
+		 *           function:fnCallback - User definable function to modify the cookie
+		 */
+		function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback )
+		{
+			var date = new Date();
+			date.setTime( date.getTime()+(iSecs*1000) );
+			
+			/* 
+			 * Shocking but true - it would appear IE has major issues with having the path not having
+			 * a trailing slash on it. We need the cookie to be available based on the path, so we
+			 * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
+			 * patch to use at least some of the path
+			 */
+			var aParts = window.location.pathname.split('/');
+			var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase();
+			var sFullCookie, oData;
+			
+			if ( fnCallback !== null )
+			{
+				oData = (typeof $.parseJSON == 'function') ? 
+					$.parseJSON( sValue ) : eval( '('+sValue+')' );
+				sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(),
+					aParts.join('/')+"/" );
+			}
+			else
+			{
+				sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
+					"; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/";
+			}
+			
+			/* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
+			 * belonging to DataTables. This is FAR from bullet proof
+			 */
+			var sOldName="", iOldTime=9999999999999;
+			var iLength = _fnReadCookie( sNameFile )!==null ? document.cookie.length : 
+				sFullCookie.length + document.cookie.length;
+			
+			if ( iLength+10 > 4096 ) /* Magic 10 for padding */
+			{
+				var aCookies =document.cookie.split(';');
+				for ( var i=0, iLen=aCookies.length ; i<iLen ; i++ )
+				{
+					if ( aCookies[i].indexOf( sBaseName ) != -1 )
+					{
+						/* It's a DataTables cookie, so eval it and check the time stamp */
+						var aSplitCookie = aCookies[i].split('=');
+						try { oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' ); }
+						catch( e ) { continue; }
+						
+						if ( typeof oData.iCreate != 'undefined' && oData.iCreate < iOldTime )
+						{
+							sOldName = aSplitCookie[0];
+							iOldTime = oData.iCreate;
+						}
+					}
+				}
+				
+				if ( sOldName !== "" )
+				{
+					document.cookie = sOldName+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
+						aParts.join('/') + "/";
+				}
+			}
+			
+			document.cookie = sFullCookie;
+		}
+		
+		/*
+		 * Function: _fnReadCookie
+		 * Purpose:  Read an old cookie to get a cookie with an old table state
+		 * Returns:  string: - contents of the cookie - or null if no cookie with that name found
+		 * Inputs:   string:sName - name of the cookie to read
+		 */
+		function _fnReadCookie ( sName )
+		{
+			var
+				aParts = window.location.pathname.split('/'),
+				sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=',
+			 	sCookieContents = document.cookie.split(';');
+			
+			for( var i=0 ; i<sCookieContents.length ; i++ )
+			{
+				var c = sCookieContents[i];
+				
+				while (c.charAt(0)==' ')
+				{
+					c = c.substring(1,c.length);
+				}
+				
+				if (c.indexOf(sNameEQ) === 0)
+				{
+					return decodeURIComponent( c.substring(sNameEQ.length,c.length) );
+				}
+			}
+			return null;
+		}
+		
+		/*
+		 * Function: _fnDetectHeader
+		 * Purpose:  Use the DOM source to create up an array of header cells. The idea here is to
+		 *           create a layout grid (array) of rows x columns, which contains a reference
+		 *           to the cell that that point in the grid (regardless of col/rowspan), such that
+		 *           any column / row could be removed and the new grid constructed
+		 * Returns:  void
+		 * Outputs:  array object:aLayout - Array to store the calculated layout in
+		 * Inputs:   node:nThead - The header/footer element for the table
+		 */
+		function _fnDetectHeader ( aLayout, nThead )
+		{
+			var nTrs = $(nThead).children('tr');
+			var nCell;
+			var i, j, k, l, iLen, jLen, iColShifted;
+			var fnShiftCol = function ( a, i, j ) {
+				while ( typeof a[i][j] != 'undefined' ) {
+					j++;
+				}
+				return j;
+			};
+
+			aLayout.splice( 0, aLayout.length );
+			
+			/* We know how many rows there are in the layout - so prep it */
+			for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
+			{
+				aLayout.push( [] );
+			}
+			
+			/* Calculate a layout array */
+			for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
+			{
+				var iColumn = 0;
+				
+				/* For every cell in the row... */
+				for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ )
+				{
+					nCell = nTrs[i].childNodes[j];
+
+					if ( nCell.nodeName.toUpperCase() == "TD" ||
+					     nCell.nodeName.toUpperCase() == "TH" )
+					{
+						/* Get the col and rowspan attributes from the DOM and sanitise them */
+						var iColspan = nCell.getAttribute('colspan') * 1;
+						var iRowspan = nCell.getAttribute('rowspan') * 1;
+						iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
+						iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
+
+						/* There might be colspan cells already in this row, so shift our target 
+						 * accordingly
+						 */
+						iColShifted = fnShiftCol( aLayout, i, iColumn );
+						
+						/* If there is col / rowspan, copy the information into the layout grid */
+						for ( l=0 ; l<iColspan ; l++ )
+						{
+							for ( k=0 ; k<iRowspan ; k++ )
+							{
+								aLayout[i+k][iColShifted+l] = {
+									"cell": nCell,
+									"unique": iColspan == 1 ? true : false
+								};
+								aLayout[i+k].nTr = nTrs[i];
+							}
+						}
+					}
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnGetUniqueThs
+		 * Purpose:  Get an array of unique th elements, one for each column
+		 * Returns:  array node:aReturn - list of unique ths
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           node:nHeader - automatically detect the layout from this node - optional
+		 *           array object:aLayout - thead/tfoot layout from _fnDetectHeader - optional
+		 */
+		function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
+		{
+			var aReturn = [];
+			if ( typeof aLayout == 'undefined' )
+			{
+				aLayout = oSettings.aoHeader;
+				if ( typeof nHeader != 'undefined' )
+				{
+					aLayout = [];
+					_fnDetectHeader( aLayout, nHeader );
+				}
+			}
+
+			for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
+			{
+				for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
+				{
+					if ( aLayout[i][j].unique && 
+						 (typeof aReturn[j] == 'undefined' || !oSettings.bSortCellsTop) )
+					{
+						aReturn[j] = aLayout[i][j].cell;
+					}
+				}
+			}
+			
+			return aReturn;
+		}
+		
+		/*
+		 * Function: _fnScrollBarWidth
+		 * Purpose:  Get the width of a scroll bar in this browser being used
+		 * Returns:  int: - width in pixels
+		 * Inputs:   -
+		 * Notes:    All credit for this function belongs to Alexandre Gomes. Thanks for sharing!
+		 *   http://www.alexandre-gomes.com/?p=115
+		 */
+		function _fnScrollBarWidth ()
+		{  
+			var inner = document.createElement('p');
+			var style = inner.style;
+			style.width = "100%";
+			style.height = "200px";
+			style.padding = "0px";
+			
+			var outer = document.createElement('div');
+			style = outer.style;
+			style.position = "absolute";
+			style.top = "0px";
+			style.left = "0px";
+			style.visibility = "hidden";
+			style.width = "200px";
+			style.height = "150px";
+			style.padding = "0px";
+			style.overflow = "hidden";
+			outer.appendChild(inner);
+			
+			document.body.appendChild(outer);
+			var w1 = inner.offsetWidth;
+			outer.style.overflow = 'scroll';
+			var w2 = inner.offsetWidth;
+			if ( w1 == w2 )
+			{
+				w2 = outer.clientWidth;
+			}
+			
+			document.body.removeChild(outer);
+			return (w1 - w2);  
+		}
+		
+		/*
+		 * Function: _fnApplyToChildren
+		 * Purpose:  Apply a given function to the display child nodes of an element array (typically
+		 *   TD children of TR rows
+		 * Returns:  - (done by reference)
+		 * Inputs:   function:fn - Method to apply to the objects
+		 *           array nodes:an1 - List of elements to look through for display children
+		 *           array nodes:an2 - Another list (identical structure to the first) - optional
+		 */
+		function _fnApplyToChildren( fn, an1, an2 )
+		{
+			for ( var i=0, iLen=an1.length ; i<iLen ; i++ )
+			{
+				for ( var j=0, jLen=an1[i].childNodes.length ; j<jLen ; j++ )
+				{
+					if ( an1[i].childNodes[j].nodeType == 1 )
+					{
+						if ( typeof an2 != 'undefined' )
+						{
+							fn( an1[i].childNodes[j], an2[i].childNodes[j] );
+						}
+						else
+						{
+							fn( an1[i].childNodes[j] );
+						}
+					}
+				}
+			}
+		}
+		
+		/*
+		 * Function: _fnMap
+		 * Purpose:  See if a property is defined on one object, if so assign it to the other object
+		 * Returns:  - (done by reference)
+		 * Inputs:   object:oRet - target object
+		 *           object:oSrc - source object
+		 *           string:sName - property
+		 *           string:sMappedName - name to map too - optional, sName used if not given
+		 */
+		function _fnMap( oRet, oSrc, sName, sMappedName )
+		{
+			if ( typeof sMappedName == 'undefined' )
+			{
+				sMappedName = sName;
+			}
+			if ( typeof oSrc[sName] != 'undefined' )
+			{
+				oRet[sMappedName] = oSrc[sName];
+			}
+		}
+		
+		/*
+		 * Function: _fnGetRowData
+		 * Purpose:  Get an array of data for a given row from the internal data cache
+		 * Returns:  array: - Data array
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iRow - aoData row id
+		 *           string:sSpecific - data get type ('type' 'filter' 'sort')
+		 */
+		function _fnGetRowData( oSettings, iRow, sSpecific )
+		{
+			var out = [];
+			for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+			{
+				out.push( _fnGetCellData( oSettings, iRow, i, sSpecific ) );
+			}
+			return out;
+		}
+		
+		/*
+		 * Function: _fnGetCellData
+		 * Purpose:  Get the data for a given cell from the internal cache, taking into account data mapping
+		 * Returns:  *: - Cell data
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iRow - aoData row id
+		 *           int:iCol - Column index
+		 *           string:sSpecific - data get type ('display', 'type' 'filter' 'sort')
+		 */
+		function _fnGetCellData( oSettings, iRow, iCol, sSpecific )
+		{
+			var sData;
+			var oCol = oSettings.aoColumns[iCol];
+			var oData = oSettings.aoData[iRow]._aData;
+
+			if ( (sData=oCol.fnGetData( oData )) === undefined )
+			{
+				if ( oSettings.iDrawError != oSettings.iDraw && oCol.sDefaultContent === null )
+				{
+					_fnLog( oSettings, 0, "Requested unknown parameter '"+oCol.mDataProp+
+						"' from the data source for row "+iRow );
+					oSettings.iDrawError = oSettings.iDraw;
+				}
+				return oCol.sDefaultContent;
+			}
+
+			/* When the data source is null, we can use default column data */
+			if ( sData === null && oCol.sDefaultContent !== null )
+			{
+				sData = oCol.sDefaultContent;
+			}
+			else if ( typeof sData == 'function' )
+			{
+				/* If the data source is a function, then we run it and use the return */
+				return sData();
+			}
+
+			if ( sSpecific == 'display' && sData === null )
+			{
+				return '';
+			}
+			return sData;
+		}
+		
+		/*
+		 * Function: _fnSetCellData
+		 * Purpose:  Set the value for a specific cell, into the internal data cache
+		 * Returns:  *: - Cell data
+		 * Inputs:   object:oSettings - dataTables settings object
+		 *           int:iRow - aoData row id
+		 *           int:iCol - Column index
+		 *           *:val - Value to set
+		 */
+		function _fnSetCellData( oSettings, iRow, iCol, val )
+		{
+			var oCol = oSettings.aoColumns[iCol];
+			var oData = oSettings.aoData[iRow]._aData;
+
+			oCol.fnSetData( oData, val );
+		}
+		
+		/*
+		 * Function: _fnGetObjectDataFn
+		 * Purpose:  Return a function that can be used to get data from a source object, taking
+		 *           into account the ability to use nested objects as a source
+		 * Returns:  function: - Data get function
+		 * Inputs:   string|int|function:mSource - The data source for the object
+		 */
+		function _fnGetObjectDataFn( mSource )
+		{
+			if ( mSource === null )
+			{
+				/* Give an empty string for rendering / sorting etc */
+				return function (data) {
+					return null;
+				};
+			}
+			else if ( typeof mSource == 'function' )
+			{
+			    return function (data) {
+			        return mSource( data );
+			    };
+			}
+			else if ( typeof mSource == 'string' && mSource.indexOf('.') != -1 )
+			{
+				/* If there is a . in the source string then the data source is in a nested object
+				 * we provide two 'quick' functions for the look up to speed up the most common
+				 * operation, and a generalised one for when it is needed
+				 */
+				var a = mSource.split('.');
+				if ( a.length == 2 )
+				{
+					return function (data) {
+						return data[ a[0] ][ a[1] ];
+					};
+				}
+				else if ( a.length == 3 )
+				{
+					return function (data) {
+						return data[ a[0] ][ a[1] ][ a[2] ];
+					};
+				}
+				else
+				{
+					return function (data) {
+						for ( var i=0, iLen=a.length ; i<iLen ; i++ )
+						{
+							data = data[ a[i] ];
+						}
+						return data;
+					};
+				}
+			}
+			else
+			{
+				/* Array or flat object mapping */
+				return function (data) {
+					return data[mSource];	
+				};
+			}
+		}
+		
+		/*
+		 * Function: _fnSetObjectDataFn
+		 * Purpose:  Return a function that can be used to set data from a source object, taking
+		 *           into account the ability to use nested objects as a source
+		 * Returns:  function: - Data set function
+		 * Inputs:   string|int|function:mSource - The data source for the object
+		 */
+		function _fnSetObjectDataFn( mSource )
+		{
+			if ( mSource === null )
+			{
+				/* Nothing to do when the data source is null */
+				return function (data, val) {};
+			}
+			else if ( typeof mSource == 'function' )
+			{
+			    return function (data, val) {
+			        return mSource( data, val );
+			    };
+			}
+			else if ( typeof mSource == 'string' && mSource.indexOf('.') != -1 )
+			{
+				/* Like the get, we need to get data from a nested object. Again two fast lookup
+				 * functions are provided, and a generalised one.
+				 */
+				var a = mSource.split('.');
+				if ( a.length == 2 )
+				{
+					return function (data, val) {
+						data[ a[0] ][ a[1] ] = val;
+					};
+				}
+				else if ( a.length == 3 )
+				{
+					return function (data, val) {
+						data[ a[0] ][ a[1] ][ a[2] ] = val;
+					};
+				}
+				else
+				{
+					return function (data, val) {
+						for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
+						{
+							data = data[ a[i] ];
+						}
+						data[ a[a.length-1] ] = val;
+					};
+				}
+			}
+			else
+			{
+				/* Array or flat object mapping */
+				return function (data, val) {
+					data[mSource] = val;	
+				};
+			}
+		}
+
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - API
+		 * I'm not happy with this solution... - To be fixed in 2.0
+		 */
+		this.oApi._fnExternApiFunc = _fnExternApiFunc;
+		this.oApi._fnInitialise = _fnInitialise;
+		this.oApi._fnInitComplete = _fnInitComplete;
+		this.oApi._fnLanguageProcess = _fnLanguageProcess;
+		this.oApi._fnAddColumn = _fnAddColumn;
+		this.oApi._fnColumnOptions = _fnColumnOptions;
+		this.oApi._fnAddData = _fnAddData;
+		this.oApi._fnCreateTr = _fnCreateTr;
+		this.oApi._fnGatherData = _fnGatherData;
+		this.oApi._fnBuildHead = _fnBuildHead;
+		this.oApi._fnDrawHead = _fnDrawHead;
+		this.oApi._fnDraw = _fnDraw;
+		this.oApi._fnReDraw = _fnReDraw;
+		this.oApi._fnAjaxUpdate = _fnAjaxUpdate;
+		this.oApi._fnAjaxParameters = _fnAjaxParameters;
+		this.oApi._fnAjaxUpdateDraw = _fnAjaxUpdateDraw;
+		this.oApi._fnServerParams = _fnServerParams;
+		this.oApi._fnAddOptionsHtml = _fnAddOptionsHtml;
+		this.oApi._fnFeatureHtmlTable = _fnFeatureHtmlTable;
+		this.oApi._fnScrollDraw = _fnScrollDraw;
+		this.oApi._fnAdjustColumnSizing = _fnAdjustColumnSizing;
+		this.oApi._fnFeatureHtmlFilter = _fnFeatureHtmlFilter;
+		this.oApi._fnFilterComplete = _fnFilterComplete;
+		this.oApi._fnFilterCustom = _fnFilterCustom;
+		this.oApi._fnFilterColumn = _fnFilterColumn;
+		this.oApi._fnFilter = _fnFilter;
+		this.oApi._fnBuildSearchArray = _fnBuildSearchArray;
+		this.oApi._fnBuildSearchRow = _fnBuildSearchRow;
+		this.oApi._fnFilterCreateSearch = _fnFilterCreateSearch;
+		this.oApi._fnDataToSearch = _fnDataToSearch;
+		this.oApi._fnSort = _fnSort;
+		this.oApi._fnSortAttachListener = _fnSortAttachListener;
+		this.oApi._fnSortingClasses = _fnSortingClasses;
+		this.oApi._fnFeatureHtmlPaginate = _fnFeatureHtmlPaginate;
+		this.oApi._fnPageChange = _fnPageChange;
+		this.oApi._fnFeatureHtmlInfo = _fnFeatureHtmlInfo;
+		this.oApi._fnUpdateInfo = _fnUpdateInfo;
+		this.oApi._fnFeatureHtmlLength = _fnFeatureHtmlLength;
+		this.oApi._fnFeatureHtmlProcessing = _fnFeatureHtmlProcessing;
+		this.oApi._fnProcessingDisplay = _fnProcessingDisplay;
+		this.oApi._fnVisibleToColumnIndex = _fnVisibleToColumnIndex;
+		this.oApi._fnColumnIndexToVisible = _fnColumnIndexToVisible;
+		this.oApi._fnNodeToDataIndex = _fnNodeToDataIndex;
+		this.oApi._fnVisbleColumns = _fnVisbleColumns;
+		this.oApi._fnCalculateEnd = _fnCalculateEnd;
+		this.oApi._fnConvertToWidth = _fnConvertToWidth;
+		this.oApi._fnCalculateColumnWidths = _fnCalculateColumnWidths;
+		this.oApi._fnScrollingWidthAdjust = _fnScrollingWidthAdjust;
+		this.oApi._fnGetWidestNode = _fnGetWidestNode;
+		this.oApi._fnGetMaxLenString = _fnGetMaxLenString;
+		this.oApi._fnStringToCss = _fnStringToCss;
+		this.oApi._fnArrayCmp = _fnArrayCmp;
+		this.oApi._fnDetectType = _fnDetectType;
+		this.oApi._fnSettingsFromNode = _fnSettingsFromNode;
+		this.oApi._fnGetDataMaster = _fnGetDataMaster;
+		this.oApi._fnGetTrNodes = _fnGetTrNodes;
+		this.oApi._fnGetTdNodes = _fnGetTdNodes;
+		this.oApi._fnEscapeRegex = _fnEscapeRegex;
+		this.oApi._fnDeleteIndex = _fnDeleteIndex;
+		this.oApi._fnReOrderIndex = _fnReOrderIndex;
+		this.oApi._fnColumnOrdering = _fnColumnOrdering;
+		this.oApi._fnLog = _fnLog;
+		this.oApi._fnClearTable = _fnClearTable;
+		this.oApi._fnSaveState = _fnSaveState;
+		this.oApi._fnLoadState = _fnLoadState;
+		this.oApi._fnCreateCookie = _fnCreateCookie;
+		this.oApi._fnReadCookie = _fnReadCookie;
+		this.oApi._fnDetectHeader = _fnDetectHeader;
+		this.oApi._fnGetUniqueThs = _fnGetUniqueThs;
+		this.oApi._fnScrollBarWidth = _fnScrollBarWidth;
+		this.oApi._fnApplyToChildren = _fnApplyToChildren;
+		this.oApi._fnMap = _fnMap;
+		this.oApi._fnGetRowData = _fnGetRowData;
+		this.oApi._fnGetCellData = _fnGetCellData;
+		this.oApi._fnSetCellData = _fnSetCellData;
+		this.oApi._fnGetObjectDataFn = _fnGetObjectDataFn;
+		this.oApi._fnSetObjectDataFn = _fnSetObjectDataFn;
+		
+		
+		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+		 * Section - Constructor
+		 */
+		
+		/* Want to be able to reference "this" inside the this.each function */
+		var _that = this;
+		return this.each(function()
+		{
+			var i=0, iLen, j, jLen, k, kLen;
+			
+			/* Check to see if we are re-initialising a table */
+			for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ )
+			{
+				/* Base check on table node */
+				if ( _aoSettings[i].nTable == this )
+				{
+					if ( typeof oInit == 'undefined' || 
+					   ( typeof oInit.bRetrieve != 'undefined' && oInit.bRetrieve === true ) )
+					{
+						return _aoSettings[i].oInstance;
+					}
+					else if ( typeof oInit.bDestroy != 'undefined' && oInit.bDestroy === true )
+					{
+						_aoSettings[i].oInstance.fnDestroy();
+						break;
+					}
+					else
+					{
+						_fnLog( _aoSettings[i], 0, "Cannot reinitialise DataTable.\n\n"+
+							"To retrieve the DataTables object for this table, please pass either no arguments "+
+							"to the dataTable() function, or set bRetrieve to true. Alternatively, to destory "+
+							"the old table and create a new one, set bDestroy to true (note that a lot of "+
+							"changes to the configuration can be made through the API which is usually much "+
+							"faster)." );
+						return;
+					}
+				}
+				
+				/* If the element we are initialising has the same ID as a table which was previously
+				 * initialised, but the table nodes don't match (from before) then we destory the old
+				 * instance by simply deleting it. This is under the assumption that the table has been
+				 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
+				 */
+				if ( _aoSettings[i].sTableId !== "" && _aoSettings[i].sTableId == this.getAttribute('id') )
+				{
+					_aoSettings.splice( i, 1 );
+					break;
+				}
+			}
+			
+			/* Make a complete and independent copy of the settings object */
+			var oSettings = new classSettings();
+			_aoSettings.push( oSettings );
+			
+			var bInitHandedOff = false;
+			var bUsePassedData = false;
+			
+			/* Set the id */
+			var sId = this.getAttribute( 'id' );
+			if ( sId !== null )
+			{
+				oSettings.sTableId = sId;
+				oSettings.sInstance = sId;
+			}
+			else
+			{
+				oSettings.sInstance = _oExt._oExternConfig.iNextUnique ++;
+			}
+			
+			/* Sanity check */
+			if ( this.nodeName.toLowerCase() != 'table' )
+			{
+				_fnLog( oSettings, 0, "Attempted to initialise DataTables on a node which is not a "+
+					"table: "+this.nodeName );
+				return;
+			}
+			
+			/* Set the table node */
+			oSettings.nTable = this;
+			
+			/* Keep a reference to the 'this' instance for the table. Note that if this table is being
+			 * created with others, we retrieve a unique instance to ease API access.
+			 */
+			oSettings.oInstance = _that.length == 1 ? _that : $(this).dataTable();
+			
+			/* Bind the API functions to the settings, so we can perform actions whenever oSettings is
+			 * available
+			 */
+			oSettings.oApi = _that.oApi;
+			
+			/* State the table's width for if a destroy is called at a later time */
+			oSettings.sDestroyWidth = $(this).width();
+			
+			/* Store the features that we have available */
+			if ( typeof oInit != 'undefined' && oInit !== null )
+			{
+				oSettings.oInit = oInit;
+				_fnMap( oSettings.oFeatures, oInit, "bPaginate" );
+				_fnMap( oSettings.oFeatures, oInit, "bLengthChange" );
+				_fnMap( oSettings.oFeatures, oInit, "bFilter" );
+				_fnMap( oSettings.oFeatures, oInit, "bSort" );
+				_fnMap( oSettings.oFeatures, oInit, "bInfo" );
+				_fnMap( oSettings.oFeatures, oInit, "bProcessing" );
+				_fnMap( oSettings.oFeatures, oInit, "bAutoWidth" );
+				_fnMap( oSettings.oFeatures, oInit, "bSortClasses" );
+				_fnMap( oSettings.oFeatures, oInit, "bServerSide" );
+				_fnMap( oSettings.oFeatures, oInit, "bDeferRender" );
+				_fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" );
+				_fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" );
+				_fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" );
+				_fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" );
+				_fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" );
+				_fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" );
+				_fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" );
+				_fnMap( oSettings, oInit, "asStripClasses", "asStripeClasses" ); // legacy
+				_fnMap( oSettings, oInit, "asStripeClasses" );
+				_fnMap( oSettings, oInit, "fnPreDrawCallback" );
+				_fnMap( oSettings, oInit, "fnRowCallback" );
+				_fnMap( oSettings, oInit, "fnHeaderCallback" );
+				_fnMap( oSettings, oInit, "fnFooterCallback" );
+				_fnMap( oSettings, oInit, "fnCookieCallback" );
+				_fnMap( oSettings, oInit, "fnInitComplete" );
+				_fnMap( oSettings, oInit, "fnServerData" );
+				_fnMap( oSettings, oInit, "fnFormatNumber" );
+				_fnMap( oSettings, oInit, "aaSorting" );
+				_fnMap( oSettings, oInit, "aaSortingFixed" );
+				_fnMap( oSettings, oInit, "aLengthMenu" );
+				_fnMap( oSettings, oInit, "sPaginationType" );
+				_fnMap( oSettings, oInit, "sAjaxSource" );
+				_fnMap( oSettings, oInit, "sAjaxDataProp" );
+				_fnMap( oSettings, oInit, "iCookieDuration" );
+				_fnMap( oSettings, oInit, "sCookiePrefix" );
+				_fnMap( oSettings, oInit, "sDom" );
+				_fnMap( oSettings, oInit, "bSortCellsTop" );
+				_fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" );
+				_fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" );
+				_fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" );
+				_fnMap( oSettings, oInit, "bJQueryUI", "bJUI" );
+				_fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
+				
+				/* Callback functions which are array driven */
+				if ( typeof oInit.fnDrawCallback == 'function' )
+				{
+					oSettings.aoDrawCallback.push( {
+						"fn": oInit.fnDrawCallback,
+						"sName": "user"
+					} );
+				}
+				
+				/* Ajax additional variables are array driven */
+				if ( typeof oInit.fnServerParams == 'function' )
+				{
+					oSettings.aoServerParams.push( {
+						"fn": oInit.fnServerParams,
+						"sName": "user"
+					} );
+				}
+				
+				if ( typeof oInit.fnStateSaveCallback == 'function' )
+				{
+					oSettings.aoStateSave.push( {
+						"fn": oInit.fnStateSaveCallback,
+						"sName": "user"
+					} );
+				}
+				
+				if ( typeof oInit.fnStateLoadCallback == 'function' )
+				{
+					oSettings.aoStateLoad.push( {
+						"fn": oInit.fnStateLoadCallback,
+						"sName": "user"
+					} );
+				}
+				
+				if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort &&
+					   oSettings.oFeatures.bSortClasses )
+				{
+					/* Enable sort classes for server-side processing. Safe to do it here, since server-side
+					 * processing must be enabled by the developer
+					 */
+					oSettings.aoDrawCallback.push( {
+						"fn": _fnSortingClasses,
+						"sName": "server_side_sort_classes"
+					} );
+				}
+				else if ( oSettings.oFeatures.bDeferRender )
+				{
+					oSettings.aoDrawCallback.push( {
+						"fn": _fnSortingClasses,
+						"sName": "defer_sort_classes"
+					} );
+				}
+				
+				if ( typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI )
+				{
+					/* Use the JUI classes object for display. You could clone the oStdClasses object if 
+					 * you want to have multiple tables with multiple independent classes 
+					 */
+					oSettings.oClasses = _oExt.oJUIClasses;
+					
+					if ( typeof oInit.sDom == 'undefined' )
+					{
+						/* Set the DOM to use a layout suitable for jQuery UI's theming */
+						oSettings.sDom = '<"H"lfr>t<"F"ip>';
+					}
+				}
+				
+				/* Calculate the scroll bar width and cache it for use later on */
+				if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
+				{
+					oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
+				}
+				
+				if ( typeof oInit.iDisplayStart != 'undefined' && 
+				     typeof oSettings.iInitDisplayStart == 'undefined' )
+				{
+					/* Display start point, taking into account the save saving */
+					oSettings.iInitDisplayStart = oInit.iDisplayStart;
+					oSettings._iDisplayStart = oInit.iDisplayStart;
+				}
+				
+				/* Must be done after everything which can be overridden by a cookie! */
+				if ( typeof oInit.bStateSave != 'undefined' )
+				{
+					oSettings.oFeatures.bStateSave = oInit.bStateSave;
+					_fnLoadState( oSettings, oInit );
+					oSettings.aoDrawCallback.push( {
+						"fn": _fnSaveState,
+						"sName": "state_save"
+					} );
+				}
+
+				if ( typeof oInit.iDeferLoading != 'undefined' )
+				{
+					oSettings.bDeferLoading = true;
+					oSettings._iRecordsTotal = oInit.iDeferLoading;
+					oSettings._iRecordsDisplay = oInit.iDeferLoading;
+				}
+				
+				if ( typeof oInit.aaData != 'undefined' )
+				{
+					bUsePassedData = true;
+				}
+				
+				/* Backwards compatability */
+				/* aoColumns / aoData - remove at some point... */
+				if ( typeof oInit != 'undefined' && typeof oInit.aoData != 'undefined' )
+				{
+					oInit.aoColumns = oInit.aoData;
+				}
+				
+				/* Language definitions */
+				if ( typeof oInit.oLanguage != 'undefined' )
+				{
+					if ( typeof oInit.oLanguage.sUrl != 'undefined' && oInit.oLanguage.sUrl !== "" )
+					{
+						/* Get the language definitions from a file */
+						oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
+						$.getJSON( oSettings.oLanguage.sUrl, null, function( json ) { 
+							_fnLanguageProcess( oSettings, json, true ); } );
+						bInitHandedOff = true;
+					}
+					else
+					{
+						_fnLanguageProcess( oSettings, oInit.oLanguage, false );
+					}
+				}
+				/* Warning: The _fnLanguageProcess function is async to the remainder of this function due
+				 * to the XHR. We use _bInitialised in _fnLanguageProcess() to check this the processing 
+				 * below is complete. The reason for spliting it like this is optimisation - we can fire
+				 * off the XHR (if needed) and then continue processing the data.
+				 */
+			}
+			else
+			{
+				/* Create a dummy object for quick manipulation later on. */
+				oInit = {};
+			}
+			
+			/*
+			 * Stripes
+			 * Add the stripe classes now that we know which classes to apply - unless overruled
+			 */
+			if ( typeof oInit.asStripClasses == 'undefined' && 
+			     typeof oInit.asStripeClasses == 'undefined' )
+			{
+				oSettings.asStripeClasses.push( oSettings.oClasses.sStripeOdd );
+				oSettings.asStripeClasses.push( oSettings.oClasses.sStripeEven );
+			}
+			
+			/* Remove row stripe classes if they are already on the table row */
+			var bStripeRemove = false;
+			var anRows = $(this).children('tbody').children('tr');
+			for ( i=0, iLen=oSettings.asStripeClasses.length ; i<iLen ; i++ )
+			{
+				if ( anRows.filter(":lt(2)").hasClass( oSettings.asStripeClasses[i]) )
+				{
+					bStripeRemove = true;
+					break;
+				}
+			}
+					
+			if ( bStripeRemove )
+			{
+				/* Store the classes which we are about to remove so they can be readded on destory */
+				oSettings.asDestroyStripes = [ '', '' ];
+				if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripeOdd) )
+				{
+					oSettings.asDestroyStripes[0] += oSettings.oClasses.sStripeOdd+" ";
+				}
+				if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripeEven) )
+				{
+					oSettings.asDestroyStripes[0] += oSettings.oClasses.sStripeEven;
+				}
+				if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripeOdd) )
+				{
+					oSettings.asDestroyStripes[1] += oSettings.oClasses.sStripeOdd+" ";
+				}
+				if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripeEven) )
+				{
+					oSettings.asDestroyStripes[1] += oSettings.oClasses.sStripeEven;
+				}
+				
+				anRows.removeClass( oSettings.asStripeClasses.join(' ') );
+			}
+			
+			/*
+			 * Columns
+			 * See if we should load columns automatically or use defined ones
+			 */
+			var anThs = [];
+			var aoColumnsInit;
+			var nThead = this.getElementsByTagName('thead');
+			if ( nThead.length !== 0 )
+			{
+				_fnDetectHeader( oSettings.aoHeader, nThead[0] );
+				anThs = _fnGetUniqueThs( oSettings );
+			}
+			
+			/* If not given a column array, generate one with nulls */
+			if ( typeof oInit.aoColumns == 'undefined' )
+			{
+				aoColumnsInit = [];
+				for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
+				{
+					aoColumnsInit.push( null );
+				}
+			}
+			else
+			{
+				aoColumnsInit = oInit.aoColumns;
+			}
+			
+			/* Add the columns */
+			for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
+			{
+				/* Check if we have column visibilty state to restore */
+				if ( typeof oInit.saved_aoColumns != 'undefined' && oInit.saved_aoColumns.length == iLen )
+				{
+					if ( aoColumnsInit[i] === null )
+					{
+						aoColumnsInit[i] = {};
+					}
+					aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
+				}
+				
+				_fnAddColumn( oSettings, anThs ? anThs[i] : null );
+			}
+			
+			/* Add options from column definations */
+			if ( typeof oInit.aoColumnDefs != 'undefined' )
+			{
+				/* Loop over the column defs array - loop in reverse so first instace has priority */
+				for ( i=oInit.aoColumnDefs.length-1 ; i>=0 ; i-- )
+				{
+					/* Each column def can target multiple columns, as it is an array */
+					var aTargets = oInit.aoColumnDefs[i].aTargets;
+					if ( !$.isArray( aTargets ) )
+					{
+						_fnLog( oSettings, 1, 'aTargets must be an array of targets, not a '+(typeof aTargets) );
+					}
+					for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
+					{
+						if ( typeof aTargets[j] == 'number' && aTargets[j] >= 0 )
+						{
+							/* 0+ integer, left to right column counting. We add columns which are unknown
+							 * automatically. Is this the right behaviour for this? We should at least
+							 * log it in future. We cannot do this for the negative or class targets, only here.
+							 */
+							while( oSettings.aoColumns.length <= aTargets[j] )
+							{
+								_fnAddColumn( oSettings );
+							}
+							_fnColumnOptions( oSettings, aTargets[j], oInit.aoColumnDefs[i] );
+						}
+						else if ( typeof aTargets[j] == 'number' && aTargets[j] < 0 )
+						{
+							/* Negative integer, right to left column counting */
+							_fnColumnOptions( oSettings, oSettings.aoColumns.length+aTargets[j], 
+								oInit.aoColumnDefs[i] );
+						}
+						else if ( typeof aTargets[j] == 'string' )
+						{
+							/* Class name matching on TH element */
+							for ( k=0, kLen=oSettings.aoColumns.length ; k<kLen ; k++ )
+							{
+								if ( aTargets[j] == "_all" ||
+								     $(oSettings.aoColumns[k].nTh).hasClass( aTargets[j] ) )
+								{
+									_fnColumnOptions( oSettings, k, oInit.aoColumnDefs[i] );
+								}
+							}
+						}
+					}
+				}
+			}
+			
+			/* Add options from column array - after the defs array so this has priority */
+			if ( typeof aoColumnsInit != 'undefined' )
+			{
+				for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
+				{
+					_fnColumnOptions( oSettings, i, aoColumnsInit[i] );
+				}
+			}
+			
+			/*
+			 * Sorting
+			 * Check the aaSorting array
+			 */
+			for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
+			{
+				if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length )
+				{
+					oSettings.aaSorting[i][0] = 0;
+				}
+				var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ];
+				
+				/* Add a default sorting index */
+				if ( typeof oSettings.aaSorting[i][2] == 'undefined' )
+				{
+					oSettings.aaSorting[i][2] = 0;
+				}
+				
+				/* If aaSorting is not defined, then we use the first indicator in asSorting */
+				if ( typeof oInit.aaSorting == "undefined" && 
+						 typeof oSettings.saved_aaSorting == "undefined" )
+				{
+					oSettings.aaSorting[i][1] = oColumn.asSorting[0];
+				}
+				
+				/* Set the current sorting index based on aoColumns.asSorting */
+				for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ )
+				{
+					if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] )
+					{
+						oSettings.aaSorting[i][2] = j;
+						break;
+					}
+				}
+			}
+				
+			/* Do a first pass on the sorting classes (allows any size changes to be taken into
+			 * account, and also will apply sorting disabled classes if disabled
+			 */
+			_fnSortingClasses( oSettings );
+			
+			/*
+			 * Final init
+			 * Cache the header, body and footer as required, creating them if needed
+			 */
+			var thead = $(this).children('thead');
+			if ( thead.length === 0 )
+			{
+				thead = [ document.createElement( 'thead' ) ];
+				this.appendChild( thead[0] );
+			}
+			oSettings.nTHead = thead[0];
+			
+			var tbody = $(this).children('tbody');
+			if ( tbody.length === 0 )
+			{
+				tbody = [ document.createElement( 'tbody' ) ];
+				this.appendChild( tbody[0] );
+			}
+			oSettings.nTBody = tbody[0];
+			
+			var tfoot = $(this).children('tfoot');
+			if ( tfoot.length > 0 )
+			{
+				oSettings.nTFoot = tfoot[0];
+				_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
+			}
+			
+			/* Check if there is data passing into the constructor */
+			if ( bUsePassedData )
+			{
+				for ( i=0 ; i<oInit.aaData.length ; i++ )
+				{
+					_fnAddData( oSettings, oInit.aaData[ i ] );
+				}
+			}
+			else
+			{
+				/* Grab the data from the page */
+				_fnGatherData( oSettings );
+			}
+			
+			/* Copy the data index array */
+			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+			
+			/* Initialisation complete - table can be drawn */
+			oSettings.bInitialised = true;
+			
+			/* Check if we need to initialise the table (it might not have been handed off to the
+			 * language processor)
+			 */
+			if ( bInitHandedOff === false )
+			{
+				_fnInitialise( oSettings );
+			}
+		});
+	};
+})(jQuery, window, document);
diff --git a/plugins/DataTables/media/js/jquery.dataTables.min.js b/plugins/DataTables/media/js/jquery.dataTables.min.js
new file mode 100644
index 0000000..4280c6d
--- /dev/null
+++ b/plugins/DataTables/media/js/jquery.dataTables.min.js
@@ -0,0 +1,151 @@
+/*
+ * File:        jquery.dataTables.min.js
+ * Version:     1.8.2
+ * Author:      Allan Jardine (www.sprymedia.co.uk)
+ * Info:        www.datatables.net
+ * 
+ * Copyright 2008-2011 Allan Jardine, all rights reserved.
+ *
+ * This source file is free software, under either the GPL v2 license or a
+ * BSD style license, as supplied with this software.
+ * 
+ * This source file 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 license files for details.
+ */
+(function(i,za,p){i.fn.dataTableSettings=[];var D=i.fn.dataTableSettings;i.fn.dataTableExt={};var n=i.fn.dataTableExt;n.sVersion="1.8.2";n.sErrMode="alert";n.iApiIndex=0;n.oApi={};n.afnFiltering=[];n.aoFeatures=[];n.ofnSearch={};n.afnSortData=[];n.oStdClasses={sPagePrevEnabled:"paginate_enabled_previous",sPagePrevDisabled:"paginate_disabled_previous",sPageNextEnabled:"paginate_enabled_next",sPageNextDisabled:"paginate_disabled_next",sPageJUINext:"",sPageJUIPrev:"",sPageButton:"paginate_button",sPageButtonActive:"paginate_active",
+sPageButtonStaticDisabled:"paginate_button paginate_button_disabled",sPageFirst:"first",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",
+sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:""};n.oJUIClasses={sPagePrevEnabled:"fg-button ui-button ui-state-default ui-corner-left",
+sPagePrevDisabled:"fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",sPageNextEnabled:"fg-button ui-button ui-state-default ui-corner-right",sPageNextDisabled:"fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",sPageJUINext:"ui-icon ui-icon-circle-arrow-e",sPageJUIPrev:"ui-icon ui-icon-circle-arrow-w",sPageButton:"fg-button ui-button ui-state-default",sPageButtonActive:"fg-button ui-button ui-state-default ui-state-disabled",sPageButtonStaticDisabled:"fg-button ui-button ui-state-default ui-state-disabled",
+sPageFirst:"first ui-corner-tl ui-corner-bl",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last ui-corner-tr ui-corner-br",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"ui-state-default",sSortDesc:"ui-state-default",sSortable:"ui-state-default",
+sSortableAsc:"ui-state-default",sSortableDesc:"ui-state-default",sSortableNone:"ui-state-default",sSortColumn:"sorting_",sSortJUIAsc:"css_right ui-icon ui-icon-triangle-1-n",sSortJUIDesc:"css_right ui-icon ui-icon-triangle-1-s",sSortJUI:"css_right ui-icon ui-icon-carat-2-n-s",sSortJUIAscAllowed:"css_right ui-icon ui-icon-carat-1-n",sSortJUIDescAllowed:"css_right ui-icon ui-icon-carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollWrapper:"dataTables_scroll",
+sScrollHead:"dataTables_scrollHead ui-state-default",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot ui-state-default",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:"ui-state-default"};n.oPagination={two_button:{fnInit:function(g,l,s){var t,w,y;if(g.bJUI){t=p.createElement("a");w=p.createElement("a");y=p.createElement("span");y.className=g.oClasses.sPageJUINext;w.appendChild(y);y=p.createElement("span");y.className=g.oClasses.sPageJUIPrev;
+t.appendChild(y)}else{t=p.createElement("div");w=p.createElement("div")}t.className=g.oClasses.sPagePrevDisabled;w.className=g.oClasses.sPageNextDisabled;t.title=g.oLanguage.oPaginate.sPrevious;w.title=g.oLanguage.oPaginate.sNext;l.appendChild(t);l.appendChild(w);i(t).bind("click.DT",function(){g.oApi._fnPageChange(g,"previous")&&s(g)});i(w).bind("click.DT",function(){g.oApi._fnPageChange(g,"next")&&s(g)});i(t).bind("selectstart.DT",function(){return false});i(w).bind("selectstart.DT",function(){return false});
+if(g.sTableId!==""&&typeof g.aanFeatures.p=="undefined"){l.setAttribute("id",g.sTableId+"_paginate");t.setAttribute("id",g.sTableId+"_previous");w.setAttribute("id",g.sTableId+"_next")}},fnUpdate:function(g){if(g.aanFeatures.p)for(var l=g.aanFeatures.p,s=0,t=l.length;s<t;s++)if(l[s].childNodes.length!==0){l[s].childNodes[0].className=g._iDisplayStart===0?g.oClasses.sPagePrevDisabled:g.oClasses.sPagePrevEnabled;l[s].childNodes[1].className=g.fnDisplayEnd()==g.fnRecordsDisplay()?g.oClasses.sPageNextDisabled:
+g.oClasses.sPageNextEnabled}}},iFullNumbersShowPages:5,full_numbers:{fnInit:function(g,l,s){var t=p.createElement("span"),w=p.createElement("span"),y=p.createElement("span"),F=p.createElement("span"),x=p.createElement("span");t.innerHTML=g.oLanguage.oPaginate.sFirst;w.innerHTML=g.oLanguage.oPaginate.sPrevious;F.innerHTML=g.oLanguage.oPaginate.sNext;x.innerHTML=g.oLanguage.oPaginate.sLast;var v=g.oClasses;t.className=v.sPageButton+" "+v.sPageFirst;w.className=v.sPageButton+" "+v.sPagePrevious;F.className=
+v.sPageButton+" "+v.sPageNext;x.className=v.sPageButton+" "+v.sPageLast;l.appendChild(t);l.appendChild(w);l.appendChild(y);l.appendChild(F);l.appendChild(x);i(t).bind("click.DT",function(){g.oApi._fnPageChange(g,"first")&&s(g)});i(w).bind("click.DT",function(){g.oApi._fnPageChange(g,"previous")&&s(g)});i(F).bind("click.DT",function(){g.oApi._fnPageChange(g,"next")&&s(g)});i(x).bind("click.DT",function(){g.oApi._fnPageChange(g,"last")&&s(g)});i("span",l).bind("mousedown.DT",function(){return false}).bind("selectstart.DT",
+function(){return false});if(g.sTableId!==""&&typeof g.aanFeatures.p=="undefined"){l.setAttribute("id",g.sTableId+"_paginate");t.setAttribute("id",g.sTableId+"_first");w.setAttribute("id",g.sTableId+"_previous");F.setAttribute("id",g.sTableId+"_next");x.setAttribute("id",g.sTableId+"_last")}},fnUpdate:function(g,l){if(g.aanFeatures.p){var s=n.oPagination.iFullNumbersShowPages,t=Math.floor(s/2),w=Math.ceil(g.fnRecordsDisplay()/g._iDisplayLength),y=Math.ceil(g._iDisplayStart/g._iDisplayLength)+1,F=
+"",x,v=g.oClasses;if(w<s){t=1;x=w}else if(y<=t){t=1;x=s}else if(y>=w-t){t=w-s+1;x=w}else{t=y-Math.ceil(s/2)+1;x=t+s-1}for(s=t;s<=x;s++)F+=y!=s?'<span class="'+v.sPageButton+'">'+s+"</span>":'<span class="'+v.sPageButtonActive+'">'+s+"</span>";x=g.aanFeatures.p;var z,$=function(M){g._iDisplayStart=(this.innerHTML*1-1)*g._iDisplayLength;l(g);M.preventDefault()},X=function(){return false};s=0;for(t=x.length;s<t;s++)if(x[s].childNodes.length!==0){z=i("span:eq(2)",x[s]);z.html(F);i("span",z).bind("click.DT",
+$).bind("mousedown.DT",X).bind("selectstart.DT",X);z=x[s].getElementsByTagName("span");z=[z[0],z[1],z[z.length-2],z[z.length-1]];i(z).removeClass(v.sPageButton+" "+v.sPageButtonActive+" "+v.sPageButtonStaticDisabled);if(y==1){z[0].className+=" "+v.sPageButtonStaticDisabled;z[1].className+=" "+v.sPageButtonStaticDisabled}else{z[0].className+=" "+v.sPageButton;z[1].className+=" "+v.sPageButton}if(w===0||y==w||g._iDisplayLength==-1){z[2].className+=" "+v.sPageButtonStaticDisabled;z[3].className+=" "+
+v.sPageButtonStaticDisabled}else{z[2].className+=" "+v.sPageButton;z[3].className+=" "+v.sPageButton}}}}}};n.oSort={"string-asc":function(g,l){if(typeof g!="string")g="";if(typeof l!="string")l="";g=g.toLowerCase();l=l.toLowerCase();return g<l?-1:g>l?1:0},"string-desc":function(g,l){if(typeof g!="string")g="";if(typeof l!="string")l="";g=g.toLowerCase();l=l.toLowerCase();return g<l?1:g>l?-1:0},"html-asc":function(g,l){g=g.replace(/<.*?>/g,"").toLowerCase();l=l.replace(/<.*?>/g,"").toLowerCase();return g<
+l?-1:g>l?1:0},"html-desc":function(g,l){g=g.replace(/<.*?>/g,"").toLowerCase();l=l.replace(/<.*?>/g,"").toLowerCase();return g<l?1:g>l?-1:0},"date-asc":function(g,l){g=Date.parse(g);l=Date.parse(l);if(isNaN(g)||g==="")g=Date.parse("01/01/1970 00:00:00");if(isNaN(l)||l==="")l=Date.parse("01/01/1970 00:00:00");return g-l},"date-desc":function(g,l){g=Date.parse(g);l=Date.parse(l);if(isNaN(g)||g==="")g=Date.parse("01/01/1970 00:00:00");if(isNaN(l)||l==="")l=Date.parse("01/01/1970 00:00:00");return l-
+g},"numeric-asc":function(g,l){return(g=="-"||g===""?0:g*1)-(l=="-"||l===""?0:l*1)},"numeric-desc":function(g,l){return(l=="-"||l===""?0:l*1)-(g=="-"||g===""?0:g*1)}};n.aTypes=[function(g){if(typeof g=="number")return"numeric";else if(typeof g!="string")return null;var l,s=false;l=g.charAt(0);if("0123456789-".indexOf(l)==-1)return null;for(var t=1;t<g.length;t++){l=g.charAt(t);if("0123456789.".indexOf(l)==-1)return null;if(l=="."){if(s)return null;s=true}}return"numeric"},function(g){var l=Date.parse(g);
+if(l!==null&&!isNaN(l)||typeof g=="string"&&g.length===0)return"date";return null},function(g){if(typeof g=="string"&&g.indexOf("<")!=-1&&g.indexOf(">")!=-1)return"html";return null}];n.fnVersionCheck=function(g){var l=function(x,v){for(;x.length<v;)x+="0";return x},s=n.sVersion.split(".");g=g.split(".");for(var t="",w="",y=0,F=g.length;y<F;y++){t+=l(s[y],3);w+=l(g[y],3)}return parseInt(t,10)>=parseInt(w,10)};n._oExternConfig={iNextUnique:0};i.fn.dataTable=function(g){function l(){this.fnRecordsTotal=
+function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsTotal,10):this.aiDisplayMaster.length};this.fnRecordsDisplay=function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsDisplay,10):this.aiDisplay.length};this.fnDisplayEnd=function(){return this.oFeatures.bServerSide?this.oFeatures.bPaginate===false||this._iDisplayLength==-1?this._iDisplayStart+this.aiDisplay.length:Math.min(this._iDisplayStart+this._iDisplayLength,this._iRecordsDisplay):this._iDisplayEnd};this.sInstance=
+this.oInstance=null;this.oFeatures={bPaginate:true,bLengthChange:true,bFilter:true,bSort:true,bInfo:true,bAutoWidth:true,bProcessing:false,bSortClasses:true,bStateSave:false,bServerSide:false,bDeferRender:false};this.oScroll={sX:"",sXInner:"",sY:"",bCollapse:false,bInfinite:false,iLoadGap:100,iBarWidth:0,bAutoCss:true};this.aanFeatures=[];this.oLanguage={sProcessing:"Processing...",sLengthMenu:"Show _MENU_ entries",sZeroRecords:"No matching records found",sEmptyTable:"No data available in table",
+sLoadingRecords:"Loading...",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sInfoThousands:",",sSearch:"Search:",sUrl:"",oPaginate:{sFirst:"First",sPrevious:"Previous",sNext:"Next",sLast:"Last"},fnInfoCallback:null};this.aoData=[];this.aiDisplay=[];this.aiDisplayMaster=[];this.aoColumns=[];this.aoHeader=[];this.aoFooter=[];this.iNextId=0;this.asDataSearch=[];this.oPreviousSearch={sSearch:"",
+bRegex:false,bSmart:true};this.aoPreSearchCols=[];this.aaSorting=[[0,"asc",0]];this.aaSortingFixed=null;this.asStripeClasses=[];this.asDestroyStripes=[];this.sDestroyWidth=0;this.fnFooterCallback=this.fnHeaderCallback=this.fnRowCallback=null;this.aoDrawCallback=[];this.fnInitComplete=this.fnPreDrawCallback=null;this.sTableId="";this.nTableWrapper=this.nTBody=this.nTFoot=this.nTHead=this.nTable=null;this.bInitialised=this.bDeferLoading=false;this.aoOpenRows=[];this.sDom="lfrtip";this.sPaginationType=
+"two_button";this.iCookieDuration=7200;this.sCookiePrefix="SpryMedia_DataTables_";this.fnCookieCallback=null;this.aoStateSave=[];this.aoStateLoad=[];this.sAjaxSource=this.oLoadedState=null;this.sAjaxDataProp="aaData";this.bAjaxDataGet=true;this.jqXHR=null;this.fnServerData=function(a,b,c,d){d.jqXHR=i.ajax({url:a,data:b,success:function(f){i(d.oInstance).trigger("xhr",d);c(f)},dataType:"json",cache:false,error:function(f,e){e=="parsererror"&&alert("DataTables warning: JSON data from server could not be parsed. This is caused by a JSON formatting error.")}})};
+this.aoServerParams=[];this.fnFormatNumber=function(a){if(a<1E3)return a;else{var b=a+"";a=b.split("");var c="";b=b.length;for(var d=0;d<b;d++){if(d%3===0&&d!==0)c=this.oLanguage.sInfoThousands+c;c=a[b-d-1]+c}}return c};this.aLengthMenu=[10,25,50,100];this.bDrawing=this.iDraw=0;this.iDrawError=-1;this._iDisplayLength=10;this._iDisplayStart=0;this._iDisplayEnd=10;this._iRecordsDisplay=this._iRecordsTotal=0;this.bJUI=false;this.oClasses=n.oStdClasses;this.bSortCellsTop=this.bSorted=this.bFiltered=false;
+this.oInit=null;this.aoDestroyCallback=[]}function s(a){return function(){var b=[A(this[n.iApiIndex])].concat(Array.prototype.slice.call(arguments));return n.oApi[a].apply(this,b)}}function t(a){var b,c,d=a.iInitDisplayStart;if(a.bInitialised===false)setTimeout(function(){t(a)},200);else{Aa(a);X(a);M(a,a.aoHeader);a.nTFoot&&M(a,a.aoFooter);K(a,true);a.oFeatures.bAutoWidth&&ga(a);b=0;for(c=a.aoColumns.length;b<c;b++)if(a.aoColumns[b].sWidth!==null)a.aoColumns[b].nTh.style.width=q(a.aoColumns[b].sWidth);
+if(a.oFeatures.bSort)R(a);else if(a.oFeatures.bFilter)N(a,a.oPreviousSearch);else{a.aiDisplay=a.aiDisplayMaster.slice();E(a);C(a)}if(a.sAjaxSource!==null&&!a.oFeatures.bServerSide){c=[];ha(a,c);a.fnServerData.call(a.oInstance,a.sAjaxSource,c,function(f){var e=f;if(a.sAjaxDataProp!=="")e=aa(a.sAjaxDataProp)(f);for(b=0;b<e.length;b++)v(a,e[b]);a.iInitDisplayStart=d;if(a.oFeatures.bSort)R(a);else{a.aiDisplay=a.aiDisplayMaster.slice();E(a);C(a)}K(a,false);w(a,f)},a)}else if(!a.oFeatures.bServerSide){K(a,
+false);w(a)}}}function w(a,b){a._bInitComplete=true;if(typeof a.fnInitComplete=="function")typeof b!="undefined"?a.fnInitComplete.call(a.oInstance,a,b):a.fnInitComplete.call(a.oInstance,a)}function y(a,b,c){a.oLanguage=i.extend(true,a.oLanguage,b);typeof b.sEmptyTable=="undefined"&&typeof b.sZeroRecords!="undefined"&&o(a.oLanguage,b,"sZeroRecords","sEmptyTable");typeof b.sLoadingRecords=="undefined"&&typeof b.sZeroRecords!="undefined"&&o(a.oLanguage,b,"sZeroRecords","sLoadingRecords");c&&t(a)}function F(a,
+b){var c=a.aoColumns.length;b={sType:null,_bAutoType:true,bVisible:true,bSearchable:true,bSortable:true,asSorting:["asc","desc"],sSortingClass:a.oClasses.sSortable,sSortingClassJUI:a.oClasses.sSortJUI,sTitle:b?b.innerHTML:"",sName:"",sWidth:null,sWidthOrig:null,sClass:null,fnRender:null,bUseRendered:true,iDataSort:c,mDataProp:c,fnGetData:null,fnSetData:null,sSortDataType:"std",sDefaultContent:null,sContentPadding:"",nTh:b?b:p.createElement("th"),nTf:null};a.aoColumns.push(b);if(typeof a.aoPreSearchCols[c]==
+"undefined"||a.aoPreSearchCols[c]===null)a.aoPreSearchCols[c]={sSearch:"",bRegex:false,bSmart:true};else{if(typeof a.aoPreSearchCols[c].bRegex=="undefined")a.aoPreSearchCols[c].bRegex=true;if(typeof a.aoPreSearchCols[c].bSmart=="undefined")a.aoPreSearchCols[c].bSmart=true}x(a,c,null)}function x(a,b,c){b=a.aoColumns[b];if(typeof c!="undefined"&&c!==null){if(typeof c.sType!="undefined"){b.sType=c.sType;b._bAutoType=false}o(b,c,"bVisible");o(b,c,"bSearchable");o(b,c,"bSortable");o(b,c,"sTitle");o(b,
+c,"sName");o(b,c,"sWidth");o(b,c,"sWidth","sWidthOrig");o(b,c,"sClass");o(b,c,"fnRender");o(b,c,"bUseRendered");o(b,c,"iDataSort");o(b,c,"mDataProp");o(b,c,"asSorting");o(b,c,"sSortDataType");o(b,c,"sDefaultContent");o(b,c,"sContentPadding")}b.fnGetData=aa(b.mDataProp);b.fnSetData=Ba(b.mDataProp);if(!a.oFeatures.bSort)b.bSortable=false;if(!b.bSortable||i.inArray("asc",b.asSorting)==-1&&i.inArray("desc",b.asSorting)==-1){b.sSortingClass=a.oClasses.sSortableNone;b.sSortingClassJUI=""}else if(b.bSortable||
+i.inArray("asc",b.asSorting)==-1&&i.inArray("desc",b.asSorting)==-1){b.sSortingClass=a.oClasses.sSortable;b.sSortingClassJUI=a.oClasses.sSortJUI}else if(i.inArray("asc",b.asSorting)!=-1&&i.inArray("desc",b.asSorting)==-1){b.sSortingClass=a.oClasses.sSortableAsc;b.sSortingClassJUI=a.oClasses.sSortJUIAscAllowed}else if(i.inArray("asc",b.asSorting)==-1&&i.inArray("desc",b.asSorting)!=-1){b.sSortingClass=a.oClasses.sSortableDesc;b.sSortingClassJUI=a.oClasses.sSortJUIDescAllowed}}function v(a,b){var c;
+c=i.isArray(b)?b.slice():i.extend(true,{},b);b=a.aoData.length;var d={nTr:null,_iId:a.iNextId++,_aData:c,_anHidden:[],_sRowStripe:""};a.aoData.push(d);for(var f,e=0,h=a.aoColumns.length;e<h;e++){c=a.aoColumns[e];typeof c.fnRender=="function"&&c.bUseRendered&&c.mDataProp!==null&&O(a,b,e,c.fnRender({iDataRow:b,iDataColumn:e,aData:d._aData,oSettings:a}));if(c._bAutoType&&c.sType!="string"){f=G(a,b,e,"type");if(f!==null&&f!==""){f=ia(f);if(c.sType===null)c.sType=f;else if(c.sType!=f&&c.sType!="html")c.sType=
+"string"}}}a.aiDisplayMaster.push(b);a.oFeatures.bDeferRender||z(a,b);return b}function z(a,b){var c=a.aoData[b],d;if(c.nTr===null){c.nTr=p.createElement("tr");typeof c._aData.DT_RowId!="undefined"&&c.nTr.setAttribute("id",c._aData.DT_RowId);typeof c._aData.DT_RowClass!="undefined"&&i(c.nTr).addClass(c._aData.DT_RowClass);for(var f=0,e=a.aoColumns.length;f<e;f++){var h=a.aoColumns[f];d=p.createElement("td");d.innerHTML=typeof h.fnRender=="function"&&(!h.bUseRendered||h.mDataProp===null)?h.fnRender({iDataRow:b,
+iDataColumn:f,aData:c._aData,oSettings:a}):G(a,b,f,"display");if(h.sClass!==null)d.className=h.sClass;if(h.bVisible){c.nTr.appendChild(d);c._anHidden[f]=null}else c._anHidden[f]=d}}}function $(a){var b,c,d,f,e,h,j,k,m;if(a.bDeferLoading||a.sAjaxSource===null){j=a.nTBody.childNodes;b=0;for(c=j.length;b<c;b++)if(j[b].nodeName.toUpperCase()=="TR"){k=a.aoData.length;a.aoData.push({nTr:j[b],_iId:a.iNextId++,_aData:[],_anHidden:[],_sRowStripe:""});a.aiDisplayMaster.push(k);h=j[b].childNodes;d=e=0;for(f=
+h.length;d<f;d++){m=h[d].nodeName.toUpperCase();if(m=="TD"||m=="TH"){O(a,k,e,i.trim(h[d].innerHTML));e++}}}}j=ba(a);h=[];b=0;for(c=j.length;b<c;b++){d=0;for(f=j[b].childNodes.length;d<f;d++){e=j[b].childNodes[d];m=e.nodeName.toUpperCase();if(m=="TD"||m=="TH")h.push(e)}}h.length!=j.length*a.aoColumns.length&&J(a,1,"Unexpected number of TD elements. Expected "+j.length*a.aoColumns.length+" and got "+h.length+". DataTables does not support rowspan / colspan in the table body, and there must be one cell for each row/column combination.");
+d=0;for(f=a.aoColumns.length;d<f;d++){if(a.aoColumns[d].sTitle===null)a.aoColumns[d].sTitle=a.aoColumns[d].nTh.innerHTML;j=a.aoColumns[d]._bAutoType;m=typeof a.aoColumns[d].fnRender=="function";e=a.aoColumns[d].sClass!==null;k=a.aoColumns[d].bVisible;var u,r;if(j||m||e||!k){b=0;for(c=a.aoData.length;b<c;b++){u=h[b*f+d];if(j&&a.aoColumns[d].sType!="string"){r=G(a,b,d,"type");if(r!==""){r=ia(r);if(a.aoColumns[d].sType===null)a.aoColumns[d].sType=r;else if(a.aoColumns[d].sType!=r&&a.aoColumns[d].sType!=
+"html")a.aoColumns[d].sType="string"}}if(m){r=a.aoColumns[d].fnRender({iDataRow:b,iDataColumn:d,aData:a.aoData[b]._aData,oSettings:a});u.innerHTML=r;a.aoColumns[d].bUseRendered&&O(a,b,d,r)}if(e)u.className+=" "+a.aoColumns[d].sClass;if(k)a.aoData[b]._anHidden[d]=null;else{a.aoData[b]._anHidden[d]=u;u.parentNode.removeChild(u)}}}}}function X(a){var b,c,d;a.nTHead.getElementsByTagName("tr");if(a.nTHead.getElementsByTagName("th").length!==0){b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;
+a.aoColumns[b].sClass!==null&&i(c).addClass(a.aoColumns[b].sClass);if(a.aoColumns[b].sTitle!=c.innerHTML)c.innerHTML=a.aoColumns[b].sTitle}}else{var f=p.createElement("tr");b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;c.innerHTML=a.aoColumns[b].sTitle;a.aoColumns[b].sClass!==null&&i(c).addClass(a.aoColumns[b].sClass);f.appendChild(c)}i(a.nTHead).html("")[0].appendChild(f);Y(a.aoHeader,a.nTHead)}if(a.bJUI){b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;f=p.createElement("div");
+f.className=a.oClasses.sSortJUIWrapper;i(c).contents().appendTo(f);var e=p.createElement("span");e.className=a.oClasses.sSortIcon;f.appendChild(e);c.appendChild(f)}}d=function(){this.onselectstart=function(){return false};return false};if(a.oFeatures.bSort)for(b=0;b<a.aoColumns.length;b++)if(a.aoColumns[b].bSortable!==false){ja(a,a.aoColumns[b].nTh,b);i(a.aoColumns[b].nTh).bind("mousedown.DT",d)}else i(a.aoColumns[b].nTh).addClass(a.oClasses.sSortableNone);a.oClasses.sFooterTH!==""&&i(a.nTFoot).children("tr").children("th").addClass(a.oClasses.sFooterTH);
+if(a.nTFoot!==null){c=S(a,null,a.aoFooter);b=0;for(d=a.aoColumns.length;b<d;b++)if(typeof c[b]!="undefined")a.aoColumns[b].nTf=c[b]}}function M(a,b,c){var d,f,e,h=[],j=[],k=a.aoColumns.length;if(typeof c=="undefined")c=false;d=0;for(f=b.length;d<f;d++){h[d]=b[d].slice();h[d].nTr=b[d].nTr;for(e=k-1;e>=0;e--)!a.aoColumns[e].bVisible&&!c&&h[d].splice(e,1);j.push([])}d=0;for(f=h.length;d<f;d++){if(h[d].nTr){a=0;for(e=h[d].nTr.childNodes.length;a<e;a++)h[d].nTr.removeChild(h[d].nTr.childNodes[0])}e=0;
+for(b=h[d].length;e<b;e++){k=c=1;if(typeof j[d][e]=="undefined"){h[d].nTr.appendChild(h[d][e].cell);for(j[d][e]=1;typeof h[d+c]!="undefined"&&h[d][e].cell==h[d+c][e].cell;){j[d+c][e]=1;c++}for(;typeof h[d][e+k]!="undefined"&&h[d][e].cell==h[d][e+k].cell;){for(a=0;a<c;a++)j[d+a][e+k]=1;k++}h[d][e].cell.rowSpan=c;h[d][e].cell.colSpan=k}}}}function C(a){var b,c,d=[],f=0,e=false;b=a.asStripeClasses.length;c=a.aoOpenRows.length;if(!(a.fnPreDrawCallback!==null&&a.fnPreDrawCallback.call(a.oInstance,a)===
+false)){a.bDrawing=true;if(typeof a.iInitDisplayStart!="undefined"&&a.iInitDisplayStart!=-1){a._iDisplayStart=a.oFeatures.bServerSide?a.iInitDisplayStart:a.iInitDisplayStart>=a.fnRecordsDisplay()?0:a.iInitDisplayStart;a.iInitDisplayStart=-1;E(a)}if(a.bDeferLoading){a.bDeferLoading=false;a.iDraw++}else if(a.oFeatures.bServerSide){if(!a.bDestroying&&!Ca(a))return}else a.iDraw++;if(a.aiDisplay.length!==0){var h=a._iDisplayStart,j=a._iDisplayEnd;if(a.oFeatures.bServerSide){h=0;j=a.aoData.length}for(h=
+h;h<j;h++){var k=a.aoData[a.aiDisplay[h]];k.nTr===null&&z(a,a.aiDisplay[h]);var m=k.nTr;if(b!==0){var u=a.asStripeClasses[f%b];if(k._sRowStripe!=u){i(m).removeClass(k._sRowStripe).addClass(u);k._sRowStripe=u}}if(typeof a.fnRowCallback=="function"){m=a.fnRowCallback.call(a.oInstance,m,a.aoData[a.aiDisplay[h]]._aData,f,h);if(!m&&!e){J(a,0,"A node was not returned by fnRowCallback");e=true}}d.push(m);f++;if(c!==0)for(k=0;k<c;k++)m==a.aoOpenRows[k].nParent&&d.push(a.aoOpenRows[k].nTr)}}else{d[0]=p.createElement("tr");
+if(typeof a.asStripeClasses[0]!="undefined")d[0].className=a.asStripeClasses[0];e=a.oLanguage.sZeroRecords.replace("_MAX_",a.fnFormatNumber(a.fnRecordsTotal()));if(a.iDraw==1&&a.sAjaxSource!==null&&!a.oFeatures.bServerSide)e=a.oLanguage.sLoadingRecords;else if(typeof a.oLanguage.sEmptyTable!="undefined"&&a.fnRecordsTotal()===0)e=a.oLanguage.sEmptyTable;b=p.createElement("td");b.setAttribute("valign","top");b.colSpan=Z(a);b.className=a.oClasses.sRowEmpty;b.innerHTML=e;d[f].appendChild(b)}typeof a.fnHeaderCallback==
+"function"&&a.fnHeaderCallback.call(a.oInstance,i(a.nTHead).children("tr")[0],ca(a),a._iDisplayStart,a.fnDisplayEnd(),a.aiDisplay);typeof a.fnFooterCallback=="function"&&a.fnFooterCallback.call(a.oInstance,i(a.nTFoot).children("tr")[0],ca(a),a._iDisplayStart,a.fnDisplayEnd(),a.aiDisplay);f=p.createDocumentFragment();b=p.createDocumentFragment();if(a.nTBody){e=a.nTBody.parentNode;b.appendChild(a.nTBody);if(!a.oScroll.bInfinite||!a._bInitComplete||a.bSorted||a.bFiltered){c=a.nTBody.childNodes;for(b=
+c.length-1;b>=0;b--)c[b].parentNode.removeChild(c[b])}b=0;for(c=d.length;b<c;b++)f.appendChild(d[b]);a.nTBody.appendChild(f);e!==null&&e.appendChild(a.nTBody)}for(b=a.aoDrawCallback.length-1;b>=0;b--)a.aoDrawCallback[b].fn.call(a.oInstance,a);i(a.oInstance).trigger("draw",a);a.bSorted=false;a.bFiltered=false;a.bDrawing=false;if(a.oFeatures.bServerSide){K(a,false);typeof a._bInitComplete=="undefined"&&w(a)}}}function da(a){if(a.oFeatures.bSort)R(a,a.oPreviousSearch);else if(a.oFeatures.bFilter)N(a,
+a.oPreviousSearch);else{E(a);C(a)}}function Ca(a){if(a.bAjaxDataGet){a.iDraw++;K(a,true);var b=Da(a);ha(a,b);a.fnServerData.call(a.oInstance,a.sAjaxSource,b,function(c){Ea(a,c)},a);return false}else return true}function Da(a){var b=a.aoColumns.length,c=[],d,f;c.push({name:"sEcho",value:a.iDraw});c.push({name:"iColumns",value:b});c.push({name:"sColumns",value:ka(a)});c.push({name:"iDisplayStart",value:a._iDisplayStart});c.push({name:"iDisplayLength",value:a.oFeatures.bPaginate!==false?a._iDisplayLength:
+-1});for(f=0;f<b;f++){d=a.aoColumns[f].mDataProp;c.push({name:"mDataProp_"+f,value:typeof d=="function"?"function":d})}if(a.oFeatures.bFilter!==false){c.push({name:"sSearch",value:a.oPreviousSearch.sSearch});c.push({name:"bRegex",value:a.oPreviousSearch.bRegex});for(f=0;f<b;f++){c.push({name:"sSearch_"+f,value:a.aoPreSearchCols[f].sSearch});c.push({name:"bRegex_"+f,value:a.aoPreSearchCols[f].bRegex});c.push({name:"bSearchable_"+f,value:a.aoColumns[f].bSearchable})}}if(a.oFeatures.bSort!==false){d=
+a.aaSortingFixed!==null?a.aaSortingFixed.length:0;var e=a.aaSorting.length;c.push({name:"iSortingCols",value:d+e});for(f=0;f<d;f++){c.push({name:"iSortCol_"+f,value:a.aaSortingFixed[f][0]});c.push({name:"sSortDir_"+f,value:a.aaSortingFixed[f][1]})}for(f=0;f<e;f++){c.push({name:"iSortCol_"+(f+d),value:a.aaSorting[f][0]});c.push({name:"sSortDir_"+(f+d),value:a.aaSorting[f][1]})}for(f=0;f<b;f++)c.push({name:"bSortable_"+f,value:a.aoColumns[f].bSortable})}return c}function ha(a,b){for(var c=0,d=a.aoServerParams.length;c<
+d;c++)a.aoServerParams[c].fn.call(a.oInstance,b)}function Ea(a,b){if(typeof b.sEcho!="undefined")if(b.sEcho*1<a.iDraw)return;else a.iDraw=b.sEcho*1;if(!a.oScroll.bInfinite||a.oScroll.bInfinite&&(a.bSorted||a.bFiltered))la(a);a._iRecordsTotal=b.iTotalRecords;a._iRecordsDisplay=b.iTotalDisplayRecords;var c=ka(a);if(c=typeof b.sColumns!="undefined"&&c!==""&&b.sColumns!=c)var d=Fa(a,b.sColumns);b=aa(a.sAjaxDataProp)(b);for(var f=0,e=b.length;f<e;f++)if(c){for(var h=[],j=0,k=a.aoColumns.length;j<k;j++)h.push(b[f][d[j]]);
+v(a,h)}else v(a,b[f]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=false;C(a);a.bAjaxDataGet=true;K(a,false)}function Aa(a){var b=p.createElement("div");a.nTable.parentNode.insertBefore(b,a.nTable);a.nTableWrapper=p.createElement("div");a.nTableWrapper.className=a.oClasses.sWrapper;a.sTableId!==""&&a.nTableWrapper.setAttribute("id",a.sTableId+"_wrapper");a.nTableReinsertBefore=a.nTable.nextSibling;for(var c=a.nTableWrapper,d=a.sDom.split(""),f,e,h,j,k,m,u,r=0;r<d.length;r++){e=0;h=d[r];if(h==
+"<"){j=p.createElement("div");k=d[r+1];if(k=="'"||k=='"'){m="";for(u=2;d[r+u]!=k;){m+=d[r+u];u++}if(m=="H")m="fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix";else if(m=="F")m="fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix";if(m.indexOf(".")!=-1){k=m.split(".");j.setAttribute("id",k[0].substr(1,k[0].length-1));j.className=k[1]}else if(m.charAt(0)=="#")j.setAttribute("id",m.substr(1,m.length-1));else j.className=m;r+=u}c.appendChild(j);
+c=j}else if(h==">")c=c.parentNode;else if(h=="l"&&a.oFeatures.bPaginate&&a.oFeatures.bLengthChange){f=Ga(a);e=1}else if(h=="f"&&a.oFeatures.bFilter){f=Ha(a);e=1}else if(h=="r"&&a.oFeatures.bProcessing){f=Ia(a);e=1}else if(h=="t"){f=Ja(a);e=1}else if(h=="i"&&a.oFeatures.bInfo){f=Ka(a);e=1}else if(h=="p"&&a.oFeatures.bPaginate){f=La(a);e=1}else if(n.aoFeatures.length!==0){j=n.aoFeatures;u=0;for(k=j.length;u<k;u++)if(h==j[u].cFeature){if(f=j[u].fnInit(a))e=1;break}}if(e==1&&f!==null){if(typeof a.aanFeatures[h]!=
+"object")a.aanFeatures[h]=[];a.aanFeatures[h].push(f);c.appendChild(f)}}b.parentNode.replaceChild(a.nTableWrapper,b)}function Ja(a){if(a.oScroll.sX===""&&a.oScroll.sY==="")return a.nTable;var b=p.createElement("div"),c=p.createElement("div"),d=p.createElement("div"),f=p.createElement("div"),e=p.createElement("div"),h=p.createElement("div"),j=a.nTable.cloneNode(false),k=a.nTable.cloneNode(false),m=a.nTable.getElementsByTagName("thead")[0],u=a.nTable.getElementsByTagName("tfoot").length===0?null:a.nTable.getElementsByTagName("tfoot")[0],
+r=typeof g.bJQueryUI!="undefined"&&g.bJQueryUI?n.oJUIClasses:n.oStdClasses;c.appendChild(d);e.appendChild(h);f.appendChild(a.nTable);b.appendChild(c);b.appendChild(f);d.appendChild(j);j.appendChild(m);if(u!==null){b.appendChild(e);h.appendChild(k);k.appendChild(u)}b.className=r.sScrollWrapper;c.className=r.sScrollHead;d.className=r.sScrollHeadInner;f.className=r.sScrollBody;e.className=r.sScrollFoot;h.className=r.sScrollFootInner;if(a.oScroll.bAutoCss){c.style.overflow="hidden";c.style.position="relative";
+e.style.overflow="hidden";f.style.overflow="auto"}c.style.border="0";c.style.width="100%";e.style.border="0";d.style.width="150%";j.removeAttribute("id");j.style.marginLeft="0";a.nTable.style.marginLeft="0";if(u!==null){k.removeAttribute("id");k.style.marginLeft="0"}d=i(a.nTable).children("caption");h=0;for(k=d.length;h<k;h++)j.appendChild(d[h]);if(a.oScroll.sX!==""){c.style.width=q(a.oScroll.sX);f.style.width=q(a.oScroll.sX);if(u!==null)e.style.width=q(a.oScroll.sX);i(f).scroll(function(){c.scrollLeft=
+this.scrollLeft;if(u!==null)e.scrollLeft=this.scrollLeft})}if(a.oScroll.sY!=="")f.style.height=q(a.oScroll.sY);a.aoDrawCallback.push({fn:Ma,sName:"scrolling"});a.oScroll.bInfinite&&i(f).scroll(function(){if(!a.bDrawing)if(i(this).scrollTop()+i(this).height()>i(a.nTable).height()-a.oScroll.iLoadGap)if(a.fnDisplayEnd()<a.fnRecordsDisplay()){ma(a,"next");E(a);C(a)}});a.nScrollHead=c;a.nScrollFoot=e;return b}function Ma(a){var b=a.nScrollHead.getElementsByTagName("div")[0],c=b.getElementsByTagName("table")[0],
+d=a.nTable.parentNode,f,e,h,j,k,m,u,r,H=[],L=a.nTFoot!==null?a.nScrollFoot.getElementsByTagName("div")[0]:null,T=a.nTFoot!==null?L.getElementsByTagName("table")[0]:null,B=i.browser.msie&&i.browser.version<=7;h=a.nTable.getElementsByTagName("thead");h.length>0&&a.nTable.removeChild(h[0]);if(a.nTFoot!==null){k=a.nTable.getElementsByTagName("tfoot");k.length>0&&a.nTable.removeChild(k[0])}h=a.nTHead.cloneNode(true);a.nTable.insertBefore(h,a.nTable.childNodes[0]);if(a.nTFoot!==null){k=a.nTFoot.cloneNode(true);
+a.nTable.insertBefore(k,a.nTable.childNodes[1])}if(a.oScroll.sX===""){d.style.width="100%";b.parentNode.style.width="100%"}var U=S(a,h);f=0;for(e=U.length;f<e;f++){u=Na(a,f);U[f].style.width=a.aoColumns[u].sWidth}a.nTFoot!==null&&P(function(I){I.style.width=""},k.getElementsByTagName("tr"));f=i(a.nTable).outerWidth();if(a.oScroll.sX===""){a.nTable.style.width="100%";if(B&&(d.scrollHeight>d.offsetHeight||i(d).css("overflow-y")=="scroll"))a.nTable.style.width=q(i(a.nTable).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sXInner!==
+"")a.nTable.style.width=q(a.oScroll.sXInner);else if(f==i(d).width()&&i(d).height()<i(a.nTable).height()){a.nTable.style.width=q(f-a.oScroll.iBarWidth);if(i(a.nTable).outerWidth()>f-a.oScroll.iBarWidth)a.nTable.style.width=q(f)}else a.nTable.style.width=q(f);f=i(a.nTable).outerWidth();e=a.nTHead.getElementsByTagName("tr");h=h.getElementsByTagName("tr");P(function(I,na){m=I.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth="0";m.borderBottomWidth="0";m.height=0;r=i(I).width();na.style.width=
+q(r);H.push(r)},h,e);i(h).height(0);if(a.nTFoot!==null){j=k.getElementsByTagName("tr");k=a.nTFoot.getElementsByTagName("tr");P(function(I,na){m=I.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth="0";m.borderBottomWidth="0";m.height=0;r=i(I).width();na.style.width=q(r);H.push(r)},j,k);i(j).height(0)}P(function(I){I.innerHTML="";I.style.width=q(H.shift())},h);a.nTFoot!==null&&P(function(I){I.innerHTML="";I.style.width=q(H.shift())},j);if(i(a.nTable).outerWidth()<f){j=d.scrollHeight>d.offsetHeight||
+i(d).css("overflow-y")=="scroll"?f+a.oScroll.iBarWidth:f;if(B&&(d.scrollHeight>d.offsetHeight||i(d).css("overflow-y")=="scroll"))a.nTable.style.width=q(j-a.oScroll.iBarWidth);d.style.width=q(j);b.parentNode.style.width=q(j);if(a.nTFoot!==null)L.parentNode.style.width=q(j);if(a.oScroll.sX==="")J(a,1,"The table cannot fit into the current element which will cause column misalignment. The table has been drawn at its minimum possible width.");else a.oScroll.sXInner!==""&&J(a,1,"The table cannot fit into the current element which will cause column misalignment. Increase the sScrollXInner value or remove it to allow automatic calculation")}else{d.style.width=
+q("100%");b.parentNode.style.width=q("100%");if(a.nTFoot!==null)L.parentNode.style.width=q("100%")}if(a.oScroll.sY==="")if(B)d.style.height=q(a.nTable.offsetHeight+a.oScroll.iBarWidth);if(a.oScroll.sY!==""&&a.oScroll.bCollapse){d.style.height=q(a.oScroll.sY);B=a.oScroll.sX!==""&&a.nTable.offsetWidth>d.offsetWidth?a.oScroll.iBarWidth:0;if(a.nTable.offsetHeight<d.offsetHeight)d.style.height=q(i(a.nTable).height()+B)}B=i(a.nTable).outerWidth();c.style.width=q(B);b.style.width=q(B+a.oScroll.iBarWidth);
+if(a.nTFoot!==null){L.style.width=q(a.nTable.offsetWidth+a.oScroll.iBarWidth);T.style.width=q(a.nTable.offsetWidth)}if(a.bSorted||a.bFiltered)d.scrollTop=0}function ea(a){if(a.oFeatures.bAutoWidth===false)return false;ga(a);for(var b=0,c=a.aoColumns.length;b<c;b++)a.aoColumns[b].nTh.style.width=a.aoColumns[b].sWidth}function Ha(a){var b=a.oLanguage.sSearch;b=b.indexOf("_INPUT_")!==-1?b.replace("_INPUT_",'<input type="text" />'):b===""?'<input type="text" />':b+' <input type="text" />';var c=p.createElement("div");
+c.className=a.oClasses.sFilter;c.innerHTML="<label>"+b+"</label>";a.sTableId!==""&&typeof a.aanFeatures.f=="undefined"&&c.setAttribute("id",a.sTableId+"_filter");b=i("input",c);b.val(a.oPreviousSearch.sSearch.replace('"',"&quot;"));b.bind("keyup.DT",function(){for(var d=a.aanFeatures.f,f=0,e=d.length;f<e;f++)d[f]!=i(this).parents("div.dataTables_filter")[0]&&i("input",d[f]).val(this.value);this.value!=a.oPreviousSearch.sSearch&&N(a,{sSearch:this.value,bRegex:a.oPreviousSearch.bRegex,bSmart:a.oPreviousSearch.bSmart})});
+b.bind("keypress.DT",function(d){if(d.keyCode==13)return false});return c}function N(a,b,c){Oa(a,b.sSearch,c,b.bRegex,b.bSmart);for(b=0;b<a.aoPreSearchCols.length;b++)Pa(a,a.aoPreSearchCols[b].sSearch,b,a.aoPreSearchCols[b].bRegex,a.aoPreSearchCols[b].bSmart);n.afnFiltering.length!==0&&Qa(a);a.bFiltered=true;i(a.oInstance).trigger("filter",a);a._iDisplayStart=0;E(a);C(a);oa(a,0)}function Qa(a){for(var b=n.afnFiltering,c=0,d=b.length;c<d;c++)for(var f=0,e=0,h=a.aiDisplay.length;e<h;e++){var j=a.aiDisplay[e-
+f];if(!b[c](a,fa(a,j,"filter"),j)){a.aiDisplay.splice(e-f,1);f++}}}function Pa(a,b,c,d,f){if(b!==""){var e=0;b=pa(b,d,f);for(d=a.aiDisplay.length-1;d>=0;d--){f=qa(G(a,a.aiDisplay[d],c,"filter"),a.aoColumns[c].sType);if(!b.test(f)){a.aiDisplay.splice(d,1);e++}}}}function Oa(a,b,c,d,f){var e=pa(b,d,f);if(typeof c=="undefined"||c===null)c=0;if(n.afnFiltering.length!==0)c=1;if(b.length<=0){a.aiDisplay.splice(0,a.aiDisplay.length);a.aiDisplay=a.aiDisplayMaster.slice()}else if(a.aiDisplay.length==a.aiDisplayMaster.length||
+a.oPreviousSearch.sSearch.length>b.length||c==1||b.indexOf(a.oPreviousSearch.sSearch)!==0){a.aiDisplay.splice(0,a.aiDisplay.length);oa(a,1);for(c=0;c<a.aiDisplayMaster.length;c++)e.test(a.asDataSearch[c])&&a.aiDisplay.push(a.aiDisplayMaster[c])}else{var h=0;for(c=0;c<a.asDataSearch.length;c++)if(!e.test(a.asDataSearch[c])){a.aiDisplay.splice(c-h,1);h++}}a.oPreviousSearch.sSearch=b;a.oPreviousSearch.bRegex=d;a.oPreviousSearch.bSmart=f}function oa(a,b){if(!a.oFeatures.bServerSide){a.asDataSearch.splice(0,
+a.asDataSearch.length);b=typeof b!="undefined"&&b==1?a.aiDisplayMaster:a.aiDisplay;for(var c=0,d=b.length;c<d;c++)a.asDataSearch[c]=ra(a,fa(a,b[c],"filter"))}}function ra(a,b){var c="";if(typeof a.__nTmpFilter=="undefined")a.__nTmpFilter=p.createElement("div");for(var d=a.__nTmpFilter,f=0,e=a.aoColumns.length;f<e;f++)if(a.aoColumns[f].bSearchable)c+=qa(b[f],a.aoColumns[f].sType)+"  ";if(c.indexOf("&")!==-1){d.innerHTML=c;c=d.textContent?d.textContent:d.innerText;c=c.replace(/\n/g," ").replace(/\r/g,
+"")}return c}function pa(a,b,c){if(c){a=b?a.split(" "):sa(a).split(" ");a="^(?=.*?"+a.join(")(?=.*?")+").*$";return new RegExp(a,"i")}else{a=b?a:sa(a);return new RegExp(a,"i")}}function qa(a,b){if(typeof n.ofnSearch[b]=="function")return n.ofnSearch[b](a);else if(b=="html")return a.replace(/\n/g," ").replace(/<.*?>/g,"");else if(typeof a=="string")return a.replace(/\n/g," ");else if(a===null)return"";return a}function R(a,b){var c,d,f,e,h=[],j=[],k=n.oSort;d=a.aoData;var m=a.aoColumns;if(!a.oFeatures.bServerSide&&
+(a.aaSorting.length!==0||a.aaSortingFixed!==null)){h=a.aaSortingFixed!==null?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(c=0;c<h.length;c++){var u=h[c][0];f=ta(a,u);e=a.aoColumns[u].sSortDataType;if(typeof n.afnSortData[e]!="undefined"){var r=n.afnSortData[e](a,u,f);f=0;for(e=d.length;f<e;f++)O(a,f,u,r[f])}}c=0;for(d=a.aiDisplayMaster.length;c<d;c++)j[a.aiDisplayMaster[c]]=c;var H=h.length;a.aiDisplayMaster.sort(function(L,T){var B,U;for(c=0;c<H;c++){B=m[h[c][0]].iDataSort;U=m[B].sType;
+B=k[(U?U:"string")+"-"+h[c][1]](G(a,L,B,"sort"),G(a,T,B,"sort"));if(B!==0)return B}return k["numeric-asc"](j[L],j[T])})}if((typeof b=="undefined"||b)&&!a.oFeatures.bDeferRender)V(a);a.bSorted=true;i(a.oInstance).trigger("sort",a);if(a.oFeatures.bFilter)N(a,a.oPreviousSearch,1);else{a.aiDisplay=a.aiDisplayMaster.slice();a._iDisplayStart=0;E(a);C(a)}}function ja(a,b,c,d){i(b).bind("click.DT",function(f){if(a.aoColumns[c].bSortable!==false){var e=function(){var h,j;if(f.shiftKey){for(var k=false,m=0;m<
+a.aaSorting.length;m++)if(a.aaSorting[m][0]==c){k=true;h=a.aaSorting[m][0];j=a.aaSorting[m][2]+1;if(typeof a.aoColumns[h].asSorting[j]=="undefined")a.aaSorting.splice(m,1);else{a.aaSorting[m][1]=a.aoColumns[h].asSorting[j];a.aaSorting[m][2]=j}break}k===false&&a.aaSorting.push([c,a.aoColumns[c].asSorting[0],0])}else if(a.aaSorting.length==1&&a.aaSorting[0][0]==c){h=a.aaSorting[0][0];j=a.aaSorting[0][2]+1;if(typeof a.aoColumns[h].asSorting[j]=="undefined")j=0;a.aaSorting[0][1]=a.aoColumns[h].asSorting[j];
+a.aaSorting[0][2]=j}else{a.aaSorting.splice(0,a.aaSorting.length);a.aaSorting.push([c,a.aoColumns[c].asSorting[0],0])}R(a)};if(a.oFeatures.bProcessing){K(a,true);setTimeout(function(){e();a.oFeatures.bServerSide||K(a,false)},0)}else e();typeof d=="function"&&d(a)}})}function V(a){var b,c,d,f,e,h=a.aoColumns.length,j=a.oClasses;for(b=0;b<h;b++)a.aoColumns[b].bSortable&&i(a.aoColumns[b].nTh).removeClass(j.sSortAsc+" "+j.sSortDesc+" "+a.aoColumns[b].sSortingClass);f=a.aaSortingFixed!==null?a.aaSortingFixed.concat(a.aaSorting):
+a.aaSorting.slice();for(b=0;b<a.aoColumns.length;b++)if(a.aoColumns[b].bSortable){e=a.aoColumns[b].sSortingClass;d=-1;for(c=0;c<f.length;c++)if(f[c][0]==b){e=f[c][1]=="asc"?j.sSortAsc:j.sSortDesc;d=c;break}i(a.aoColumns[b].nTh).addClass(e);if(a.bJUI){c=i("span",a.aoColumns[b].nTh);c.removeClass(j.sSortJUIAsc+" "+j.sSortJUIDesc+" "+j.sSortJUI+" "+j.sSortJUIAscAllowed+" "+j.sSortJUIDescAllowed);c.addClass(d==-1?a.aoColumns[b].sSortingClassJUI:f[d][1]=="asc"?j.sSortJUIAsc:j.sSortJUIDesc)}}else i(a.aoColumns[b].nTh).addClass(a.aoColumns[b].sSortingClass);
+e=j.sSortColumn;if(a.oFeatures.bSort&&a.oFeatures.bSortClasses){d=Q(a);if(a.oFeatures.bDeferRender)i(d).removeClass(e+"1 "+e+"2 "+e+"3");else if(d.length>=h)for(b=0;b<h;b++)if(d[b].className.indexOf(e+"1")!=-1){c=0;for(a=d.length/h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(e+"1",""))}else if(d[b].className.indexOf(e+"2")!=-1){c=0;for(a=d.length/h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(e+"2",""))}else if(d[b].className.indexOf(e+"3")!=-1){c=0;for(a=d.length/
+h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(" "+e+"3",""))}j=1;var k;for(b=0;b<f.length;b++){k=parseInt(f[b][0],10);c=0;for(a=d.length/h;c<a;c++)d[h*c+k].className+=" "+e+j;j<3&&j++}}}function La(a){if(a.oScroll.bInfinite)return null;var b=p.createElement("div");b.className=a.oClasses.sPaging+a.sPaginationType;n.oPagination[a.sPaginationType].fnInit(a,b,function(c){E(c);C(c)});typeof a.aanFeatures.p=="undefined"&&a.aoDrawCallback.push({fn:function(c){n.oPagination[c.sPaginationType].fnUpdate(c,
+function(d){E(d);C(d)})},sName:"pagination"});return b}function ma(a,b){var c=a._iDisplayStart;if(b=="first")a._iDisplayStart=0;else if(b=="previous"){a._iDisplayStart=a._iDisplayLength>=0?a._iDisplayStart-a._iDisplayLength:0;if(a._iDisplayStart<0)a._iDisplayStart=0}else if(b=="next")if(a._iDisplayLength>=0){if(a._iDisplayStart+a._iDisplayLength<a.fnRecordsDisplay())a._iDisplayStart+=a._iDisplayLength}else a._iDisplayStart=0;else if(b=="last")if(a._iDisplayLength>=0){b=parseInt((a.fnRecordsDisplay()-
+1)/a._iDisplayLength,10)+1;a._iDisplayStart=(b-1)*a._iDisplayLength}else a._iDisplayStart=0;else J(a,0,"Unknown paging action: "+b);i(a.oInstance).trigger("page",a);return c!=a._iDisplayStart}function Ka(a){var b=p.createElement("div");b.className=a.oClasses.sInfo;if(typeof a.aanFeatures.i=="undefined"){a.aoDrawCallback.push({fn:Ra,sName:"information"});a.sTableId!==""&&b.setAttribute("id",a.sTableId+"_info")}return b}function Ra(a){if(!(!a.oFeatures.bInfo||a.aanFeatures.i.length===0)){var b=a._iDisplayStart+
+1,c=a.fnDisplayEnd(),d=a.fnRecordsTotal(),f=a.fnRecordsDisplay(),e=a.fnFormatNumber(b),h=a.fnFormatNumber(c),j=a.fnFormatNumber(d),k=a.fnFormatNumber(f);if(a.oScroll.bInfinite)e=a.fnFormatNumber(1);e=a.fnRecordsDisplay()===0&&a.fnRecordsDisplay()==a.fnRecordsTotal()?a.oLanguage.sInfoEmpty+a.oLanguage.sInfoPostFix:a.fnRecordsDisplay()===0?a.oLanguage.sInfoEmpty+" "+a.oLanguage.sInfoFiltered.replace("_MAX_",j)+a.oLanguage.sInfoPostFix:a.fnRecordsDisplay()==a.fnRecordsTotal()?a.oLanguage.sInfo.replace("_START_",
+e).replace("_END_",h).replace("_TOTAL_",k)+a.oLanguage.sInfoPostFix:a.oLanguage.sInfo.replace("_START_",e).replace("_END_",h).replace("_TOTAL_",k)+" "+a.oLanguage.sInfoFiltered.replace("_MAX_",a.fnFormatNumber(a.fnRecordsTotal()))+a.oLanguage.sInfoPostFix;if(a.oLanguage.fnInfoCallback!==null)e=a.oLanguage.fnInfoCallback(a,b,c,d,f,e);a=a.aanFeatures.i;b=0;for(c=a.length;b<c;b++)i(a[b]).html(e)}}function Ga(a){if(a.oScroll.bInfinite)return null;var b='<select size="1" '+(a.sTableId===""?"":'name="'+
+a.sTableId+'_length"')+">",c,d;if(a.aLengthMenu.length==2&&typeof a.aLengthMenu[0]=="object"&&typeof a.aLengthMenu[1]=="object"){c=0;for(d=a.aLengthMenu[0].length;c<d;c++)b+='<option value="'+a.aLengthMenu[0][c]+'">'+a.aLengthMenu[1][c]+"</option>"}else{c=0;for(d=a.aLengthMenu.length;c<d;c++)b+='<option value="'+a.aLengthMenu[c]+'">'+a.aLengthMenu[c]+"</option>"}b+="</select>";var f=p.createElement("div");a.sTableId!==""&&typeof a.aanFeatures.l=="undefined"&&f.setAttribute("id",a.sTableId+"_length");
+f.className=a.oClasses.sLength;f.innerHTML="<label>"+a.oLanguage.sLengthMenu.replace("_MENU_",b)+"</label>";i('select option[value="'+a._iDisplayLength+'"]',f).attr("selected",true);i("select",f).bind("change.DT",function(){var e=i(this).val(),h=a.aanFeatures.l;c=0;for(d=h.length;c<d;c++)h[c]!=this.parentNode&&i("select",h[c]).val(e);a._iDisplayLength=parseInt(e,10);E(a);if(a.fnDisplayEnd()==a.fnRecordsDisplay()){a._iDisplayStart=a.fnDisplayEnd()-a._iDisplayLength;if(a._iDisplayStart<0)a._iDisplayStart=
+0}if(a._iDisplayLength==-1)a._iDisplayStart=0;C(a)});return f}function Ia(a){var b=p.createElement("div");a.sTableId!==""&&typeof a.aanFeatures.r=="undefined"&&b.setAttribute("id",a.sTableId+"_processing");b.innerHTML=a.oLanguage.sProcessing;b.className=a.oClasses.sProcessing;a.nTable.parentNode.insertBefore(b,a.nTable);return b}function K(a,b){if(a.oFeatures.bProcessing){a=a.aanFeatures.r;for(var c=0,d=a.length;c<d;c++)a[c].style.visibility=b?"visible":"hidden"}}function Na(a,b){for(var c=-1,d=0;d<
+a.aoColumns.length;d++){a.aoColumns[d].bVisible===true&&c++;if(c==b)return d}return null}function ta(a,b){for(var c=-1,d=0;d<a.aoColumns.length;d++){a.aoColumns[d].bVisible===true&&c++;if(d==b)return a.aoColumns[d].bVisible===true?c:null}return null}function W(a,b){var c,d;c=a._iDisplayStart;for(d=a._iDisplayEnd;c<d;c++)if(a.aoData[a.aiDisplay[c]].nTr==b)return a.aiDisplay[c];c=0;for(d=a.aoData.length;c<d;c++)if(a.aoData[c].nTr==b)return c;return null}function Z(a){for(var b=0,c=0;c<a.aoColumns.length;c++)a.aoColumns[c].bVisible===
+true&&b++;return b}function E(a){a._iDisplayEnd=a.oFeatures.bPaginate===false?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength>a.aiDisplay.length||a._iDisplayLength==-1?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength}function Sa(a,b){if(!a||a===null||a==="")return 0;if(typeof b=="undefined")b=p.getElementsByTagName("body")[0];var c=p.createElement("div");c.style.width=q(a);b.appendChild(c);a=c.offsetWidth;b.removeChild(c);return a}function ga(a){var b=0,c,d=0,f=a.aoColumns.length,e,h=i("th",
+a.nTHead);for(e=0;e<f;e++)if(a.aoColumns[e].bVisible){d++;if(a.aoColumns[e].sWidth!==null){c=Sa(a.aoColumns[e].sWidthOrig,a.nTable.parentNode);if(c!==null)a.aoColumns[e].sWidth=q(c);b++}}if(f==h.length&&b===0&&d==f&&a.oScroll.sX===""&&a.oScroll.sY==="")for(e=0;e<a.aoColumns.length;e++){c=i(h[e]).width();if(c!==null)a.aoColumns[e].sWidth=q(c)}else{b=a.nTable.cloneNode(false);e=a.nTHead.cloneNode(true);d=p.createElement("tbody");c=p.createElement("tr");b.removeAttribute("id");b.appendChild(e);if(a.nTFoot!==
+null){b.appendChild(a.nTFoot.cloneNode(true));P(function(k){k.style.width=""},b.getElementsByTagName("tr"))}b.appendChild(d);d.appendChild(c);d=i("thead th",b);if(d.length===0)d=i("tbody tr:eq(0)>td",b);h=S(a,e);for(e=d=0;e<f;e++){var j=a.aoColumns[e];if(j.bVisible&&j.sWidthOrig!==null&&j.sWidthOrig!=="")h[e-d].style.width=q(j.sWidthOrig);else if(j.bVisible)h[e-d].style.width="";else d++}for(e=0;e<f;e++)if(a.aoColumns[e].bVisible){d=Ta(a,e);if(d!==null){d=d.cloneNode(true);if(a.aoColumns[e].sContentPadding!==
+"")d.innerHTML+=a.aoColumns[e].sContentPadding;c.appendChild(d)}}f=a.nTable.parentNode;f.appendChild(b);if(a.oScroll.sX!==""&&a.oScroll.sXInner!=="")b.style.width=q(a.oScroll.sXInner);else if(a.oScroll.sX!==""){b.style.width="";if(i(b).width()<f.offsetWidth)b.style.width=q(f.offsetWidth)}else if(a.oScroll.sY!=="")b.style.width=q(f.offsetWidth);b.style.visibility="hidden";Ua(a,b);f=i("tbody tr:eq(0)",b).children();if(f.length===0)f=S(a,i("thead",b)[0]);if(a.oScroll.sX!==""){for(e=d=c=0;e<a.aoColumns.length;e++)if(a.aoColumns[e].bVisible){c+=
+a.aoColumns[e].sWidthOrig===null?i(f[d]).outerWidth():parseInt(a.aoColumns[e].sWidth.replace("px",""),10)+(i(f[d]).outerWidth()-i(f[d]).width());d++}b.style.width=q(c);a.nTable.style.width=q(c)}for(e=d=0;e<a.aoColumns.length;e++)if(a.aoColumns[e].bVisible){c=i(f[d]).width();if(c!==null&&c>0)a.aoColumns[e].sWidth=q(c);d++}a.nTable.style.width=q(i(b).outerWidth());b.parentNode.removeChild(b)}}function Ua(a,b){if(a.oScroll.sX===""&&a.oScroll.sY!==""){i(b).width();b.style.width=q(i(b).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sX!==
+"")b.style.width=q(i(b).outerWidth())}function Ta(a,b){var c=Va(a,b);if(c<0)return null;if(a.aoData[c].nTr===null){var d=p.createElement("td");d.innerHTML=G(a,c,b,"");return d}return Q(a,c)[b]}function Va(a,b){for(var c=-1,d=-1,f=0;f<a.aoData.length;f++){var e=G(a,f,b,"display")+"";e=e.replace(/<.*?>/g,"");if(e.length>c){c=e.length;d=f}}return d}function q(a){if(a===null)return"0px";if(typeof a=="number"){if(a<0)return"0px";return a+"px"}var b=a.charCodeAt(a.length-1);if(b<48||b>57)return a;return a+
+"px"}function Za(a,b){if(a.length!=b.length)return 1;for(var c=0;c<a.length;c++)if(a[c]!=b[c])return 2;return 0}function ia(a){for(var b=n.aTypes,c=b.length,d=0;d<c;d++){var f=b[d](a);if(f!==null)return f}return"string"}function A(a){for(var b=0;b<D.length;b++)if(D[b].nTable==a)return D[b];return null}function ca(a){for(var b=[],c=a.aoData.length,d=0;d<c;d++)b.push(a.aoData[d]._aData);return b}function ba(a){for(var b=[],c=0,d=a.aoData.length;c<d;c++)a.aoData[c].nTr!==null&&b.push(a.aoData[c].nTr);
+return b}function Q(a,b){var c=[],d,f,e,h,j;f=0;var k=a.aoData.length;if(typeof b!="undefined"){f=b;k=b+1}for(f=f;f<k;f++){j=a.aoData[f];if(j.nTr!==null){b=[];e=0;for(h=j.nTr.childNodes.length;e<h;e++){d=j.nTr.childNodes[e].nodeName.toLowerCase();if(d=="td"||d=="th")b.push(j.nTr.childNodes[e])}e=d=0;for(h=a.aoColumns.length;e<h;e++)if(a.aoColumns[e].bVisible)c.push(b[e-d]);else{c.push(j._anHidden[e]);d++}}}return c}function sa(a){return a.replace(new RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^)",
+"g"),"\\$1")}function ua(a,b){for(var c=-1,d=0,f=a.length;d<f;d++)if(a[d]==b)c=d;else a[d]>b&&a[d]--;c!=-1&&a.splice(c,1)}function Fa(a,b){b=b.split(",");for(var c=[],d=0,f=a.aoColumns.length;d<f;d++)for(var e=0;e<f;e++)if(a.aoColumns[d].sName==b[e]){c.push(e);break}return c}function ka(a){for(var b="",c=0,d=a.aoColumns.length;c<d;c++)b+=a.aoColumns[c].sName+",";if(b.length==d)return"";return b.slice(0,-1)}function J(a,b,c){a=a.sTableId===""?"DataTables warning: "+c:"DataTables warning (table id = '"+
+a.sTableId+"'): "+c;if(b===0)if(n.sErrMode=="alert")alert(a);else throw a;else typeof console!="undefined"&&typeof console.log!="undefined"&&console.log(a)}function la(a){a.aoData.splice(0,a.aoData.length);a.aiDisplayMaster.splice(0,a.aiDisplayMaster.length);a.aiDisplay.splice(0,a.aiDisplay.length);E(a)}function va(a){if(!(!a.oFeatures.bStateSave||typeof a.bDestroying!="undefined")){var b,c,d,f="{";f+='"iCreate":'+(new Date).getTime()+",";f+='"iStart":'+(a.oScroll.bInfinite?0:a._iDisplayStart)+",";
+f+='"iEnd":'+(a.oScroll.bInfinite?a._iDisplayLength:a._iDisplayEnd)+",";f+='"iLength":'+a._iDisplayLength+",";f+='"sFilter":"'+encodeURIComponent(a.oPreviousSearch.sSearch)+'",';f+='"sFilterEsc":'+!a.oPreviousSearch.bRegex+",";f+='"aaSorting":[ ';for(b=0;b<a.aaSorting.length;b++)f+="["+a.aaSorting[b][0]+',"'+a.aaSorting[b][1]+'"],';f=f.substring(0,f.length-1);f+="],";f+='"aaSearchCols":[ ';for(b=0;b<a.aoPreSearchCols.length;b++)f+='["'+encodeURIComponent(a.aoPreSearchCols[b].sSearch)+'",'+!a.aoPreSearchCols[b].bRegex+
+"],";f=f.substring(0,f.length-1);f+="],";f+='"abVisCols":[ ';for(b=0;b<a.aoColumns.length;b++)f+=a.aoColumns[b].bVisible+",";f=f.substring(0,f.length-1);f+="]";b=0;for(c=a.aoStateSave.length;b<c;b++){d=a.aoStateSave[b].fn(a,f);if(d!=="")f=d}f+="}";Wa(a.sCookiePrefix+a.sInstance,f,a.iCookieDuration,a.sCookiePrefix,a.fnCookieCallback)}}function Xa(a,b){if(a.oFeatures.bStateSave){var c,d,f;d=wa(a.sCookiePrefix+a.sInstance);if(d!==null&&d!==""){try{c=typeof i.parseJSON=="function"?i.parseJSON(d.replace(/'/g,
+'"')):eval("("+d+")")}catch(e){return}d=0;for(f=a.aoStateLoad.length;d<f;d++)if(!a.aoStateLoad[d].fn(a,c))return;a.oLoadedState=i.extend(true,{},c);a._iDisplayStart=c.iStart;a.iInitDisplayStart=c.iStart;a._iDisplayEnd=c.iEnd;a._iDisplayLength=c.iLength;a.oPreviousSearch.sSearch=decodeURIComponent(c.sFilter);a.aaSorting=c.aaSorting.slice();a.saved_aaSorting=c.aaSorting.slice();if(typeof c.sFilterEsc!="undefined")a.oPreviousSearch.bRegex=!c.sFilterEsc;if(typeof c.aaSearchCols!="undefined")for(d=0;d<
+c.aaSearchCols.length;d++)a.aoPreSearchCols[d]={sSearch:decodeURIComponent(c.aaSearchCols[d][0]),bRegex:!c.aaSearchCols[d][1]};if(typeof c.abVisCols!="undefined"){b.saved_aoColumns=[];for(d=0;d<c.abVisCols.length;d++){b.saved_aoColumns[d]={};b.saved_aoColumns[d].bVisible=c.abVisCols[d]}}}}}function Wa(a,b,c,d,f){var e=new Date;e.setTime(e.getTime()+c*1E3);c=za.location.pathname.split("/");a=a+"_"+c.pop().replace(/[\/:]/g,"").toLowerCase();var h;if(f!==null){h=typeof i.parseJSON=="function"?i.parseJSON(b):
+eval("("+b+")");b=f(a,h,e.toGMTString(),c.join("/")+"/")}else b=a+"="+encodeURIComponent(b)+"; expires="+e.toGMTString()+"; path="+c.join("/")+"/";f="";e=9999999999999;if((wa(a)!==null?p.cookie.length:b.length+p.cookie.length)+10>4096){a=p.cookie.split(";");for(var j=0,k=a.length;j<k;j++)if(a[j].indexOf(d)!=-1){var m=a[j].split("=");try{h=eval("("+decodeURIComponent(m[1])+")")}catch(u){continue}if(typeof h.iCreate!="undefined"&&h.iCreate<e){f=m[0];e=h.iCreate}}if(f!=="")p.cookie=f+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
+c.join("/")+"/"}p.cookie=b}function wa(a){var b=za.location.pathname.split("/");a=a+"_"+b[b.length-1].replace(/[\/:]/g,"").toLowerCase()+"=";b=p.cookie.split(";");for(var c=0;c<b.length;c++){for(var d=b[c];d.charAt(0)==" ";)d=d.substring(1,d.length);if(d.indexOf(a)===0)return decodeURIComponent(d.substring(a.length,d.length))}return null}function Y(a,b){b=i(b).children("tr");var c,d,f,e,h,j,k,m,u=function(L,T,B){for(;typeof L[T][B]!="undefined";)B++;return B};a.splice(0,a.length);d=0;for(j=b.length;d<
+j;d++)a.push([]);d=0;for(j=b.length;d<j;d++){f=0;for(k=b[d].childNodes.length;f<k;f++){c=b[d].childNodes[f];if(c.nodeName.toUpperCase()=="TD"||c.nodeName.toUpperCase()=="TH"){var r=c.getAttribute("colspan")*1,H=c.getAttribute("rowspan")*1;r=!r||r===0||r===1?1:r;H=!H||H===0||H===1?1:H;m=u(a,d,0);for(h=0;h<r;h++)for(e=0;e<H;e++){a[d+e][m+h]={cell:c,unique:r==1?true:false};a[d+e].nTr=b[d]}}}}}function S(a,b,c){var d=[];if(typeof c=="undefined"){c=a.aoHeader;if(typeof b!="undefined"){c=[];Y(c,b)}}b=0;
+for(var f=c.length;b<f;b++)for(var e=0,h=c[b].length;e<h;e++)if(c[b][e].unique&&(typeof d[e]=="undefined"||!a.bSortCellsTop))d[e]=c[b][e].cell;return d}function Ya(){var a=p.createElement("p"),b=a.style;b.width="100%";b.height="200px";b.padding="0px";var c=p.createElement("div");b=c.style;b.position="absolute";b.top="0px";b.left="0px";b.visibility="hidden";b.width="200px";b.height="150px";b.padding="0px";b.overflow="hidden";c.appendChild(a);p.body.appendChild(c);b=a.offsetWidth;c.style.overflow="scroll";
+a=a.offsetWidth;if(b==a)a=c.clientWidth;p.body.removeChild(c);return b-a}function P(a,b,c){for(var d=0,f=b.length;d<f;d++)for(var e=0,h=b[d].childNodes.length;e<h;e++)if(b[d].childNodes[e].nodeType==1)typeof c!="undefined"?a(b[d].childNodes[e],c[d].childNodes[e]):a(b[d].childNodes[e])}function o(a,b,c,d){if(typeof d=="undefined")d=c;if(typeof b[c]!="undefined")a[d]=b[c]}function fa(a,b,c){for(var d=[],f=0,e=a.aoColumns.length;f<e;f++)d.push(G(a,b,f,c));return d}function G(a,b,c,d){var f=a.aoColumns[c];
+if((c=f.fnGetData(a.aoData[b]._aData))===undefined){if(a.iDrawError!=a.iDraw&&f.sDefaultContent===null){J(a,0,"Requested unknown parameter '"+f.mDataProp+"' from the data source for row "+b);a.iDrawError=a.iDraw}return f.sDefaultContent}if(c===null&&f.sDefaultContent!==null)c=f.sDefaultContent;else if(typeof c=="function")return c();if(d=="display"&&c===null)return"";return c}function O(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,d)}function aa(a){if(a===null)return function(){return null};
+else if(typeof a=="function")return function(c){return a(c)};else if(typeof a=="string"&&a.indexOf(".")!=-1){var b=a.split(".");return b.length==2?function(c){return c[b[0]][b[1]]}:b.length==3?function(c){return c[b[0]][b[1]][b[2]]}:function(c){for(var d=0,f=b.length;d<f;d++)c=c[b[d]];return c}}else return function(c){return c[a]}}function Ba(a){if(a===null)return function(){};else if(typeof a=="function")return function(c,d){return a(c,d)};else if(typeof a=="string"&&a.indexOf(".")!=-1){var b=a.split(".");
+return b.length==2?function(c,d){c[b[0]][b[1]]=d}:b.length==3?function(c,d){c[b[0]][b[1]][b[2]]=d}:function(c,d){for(var f=0,e=b.length-1;f<e;f++)c=c[b[f]];c[b[b.length-1]]=d}}else return function(c,d){c[a]=d}}this.oApi={};this.fnDraw=function(a){var b=A(this[n.iApiIndex]);if(typeof a!="undefined"&&a===false){E(b);C(b)}else da(b)};this.fnFilter=function(a,b,c,d,f){var e=A(this[n.iApiIndex]);if(e.oFeatures.bFilter){if(typeof c=="undefined")c=false;if(typeof d=="undefined")d=true;if(typeof f=="undefined")f=
+true;if(typeof b=="undefined"||b===null){N(e,{sSearch:a,bRegex:c,bSmart:d},1);if(f&&typeof e.aanFeatures.f!="undefined"){b=e.aanFeatures.f;c=0;for(d=b.length;c<d;c++)i("input",b[c]).val(a)}}else{e.aoPreSearchCols[b].sSearch=a;e.aoPreSearchCols[b].bRegex=c;e.aoPreSearchCols[b].bSmart=d;N(e,e.oPreviousSearch,1)}}};this.fnSettings=function(){return A(this[n.iApiIndex])};this.fnVersionCheck=n.fnVersionCheck;this.fnSort=function(a){var b=A(this[n.iApiIndex]);b.aaSorting=a;R(b)};this.fnSortListener=function(a,
+b,c){ja(A(this[n.iApiIndex]),a,b,c)};this.fnAddData=function(a,b){if(a.length===0)return[];var c=[],d,f=A(this[n.iApiIndex]);if(typeof a[0]=="object")for(var e=0;e<a.length;e++){d=v(f,a[e]);if(d==-1)return c;c.push(d)}else{d=v(f,a);if(d==-1)return c;c.push(d)}f.aiDisplay=f.aiDisplayMaster.slice();if(typeof b=="undefined"||b)da(f);return c};this.fnDeleteRow=function(a,b,c){var d=A(this[n.iApiIndex]);a=typeof a=="object"?W(d,a):a;var f=d.aoData.splice(a,1),e=i.inArray(a,d.aiDisplay);d.asDataSearch.splice(e,
+1);ua(d.aiDisplayMaster,a);ua(d.aiDisplay,a);typeof b=="function"&&b.call(this,d,f);if(d._iDisplayStart>=d.aiDisplay.length){d._iDisplayStart-=d._iDisplayLength;if(d._iDisplayStart<0)d._iDisplayStart=0}if(typeof c=="undefined"||c){E(d);C(d)}return f};this.fnClearTable=function(a){var b=A(this[n.iApiIndex]);la(b);if(typeof a=="undefined"||a)C(b)};this.fnOpen=function(a,b,c){var d=A(this[n.iApiIndex]);this.fnClose(a);var f=p.createElement("tr"),e=p.createElement("td");f.appendChild(e);e.className=c;
+e.colSpan=Z(d);if(typeof b.jquery!="undefined"||typeof b=="object")e.appendChild(b);else e.innerHTML=b;b=i("tr",d.nTBody);i.inArray(a,b)!=-1&&i(f).insertAfter(a);d.aoOpenRows.push({nTr:f,nParent:a});return f};this.fnClose=function(a){for(var b=A(this[n.iApiIndex]),c=0;c<b.aoOpenRows.length;c++)if(b.aoOpenRows[c].nParent==a){(a=b.aoOpenRows[c].nTr.parentNode)&&a.removeChild(b.aoOpenRows[c].nTr);b.aoOpenRows.splice(c,1);return 0}return 1};this.fnGetData=function(a,b){var c=A(this[n.iApiIndex]);if(typeof a!=
+"undefined"){a=typeof a=="object"?W(c,a):a;if(typeof b!="undefined")return G(c,a,b,"");return typeof c.aoData[a]!="undefined"?c.aoData[a]._aData:null}return ca(c)};this.fnGetNodes=function(a){var b=A(this[n.iApiIndex]);if(typeof a!="undefined")return typeof b.aoData[a]!="undefined"?b.aoData[a].nTr:null;return ba(b)};this.fnGetPosition=function(a){var b=A(this[n.iApiIndex]),c=a.nodeName.toUpperCase();if(c=="TR")return W(b,a);else if(c=="TD"||c=="TH"){c=W(b,a.parentNode);for(var d=Q(b,c),f=0;f<b.aoColumns.length;f++)if(d[f]==
+a)return[c,ta(b,f),f]}return null};this.fnUpdate=function(a,b,c,d,f){var e=A(this[n.iApiIndex]);b=typeof b=="object"?W(e,b):b;if(i.isArray(a)&&typeof a=="object"){e.aoData[b]._aData=a.slice();for(c=0;c<e.aoColumns.length;c++)this.fnUpdate(G(e,b,c),b,c,false,false)}else if(a!==null&&typeof a=="object"){e.aoData[b]._aData=i.extend(true,{},a);for(c=0;c<e.aoColumns.length;c++)this.fnUpdate(G(e,b,c),b,c,false,false)}else{a=a;O(e,b,c,a);if(e.aoColumns[c].fnRender!==null){a=e.aoColumns[c].fnRender({iDataRow:b,
+iDataColumn:c,aData:e.aoData[b]._aData,oSettings:e});e.aoColumns[c].bUseRendered&&O(e,b,c,a)}if(e.aoData[b].nTr!==null)Q(e,b)[c].innerHTML=a}c=i.inArray(b,e.aiDisplay);e.asDataSearch[c]=ra(e,fa(e,b,"filter"));if(typeof f=="undefined"||f)ea(e);if(typeof d=="undefined"||d)da(e);return 0};this.fnSetColumnVis=function(a,b,c){var d=A(this[n.iApiIndex]),f,e;e=d.aoColumns.length;var h,j;if(d.aoColumns[a].bVisible!=b){if(b){for(f=j=0;f<a;f++)d.aoColumns[f].bVisible&&j++;j=j>=Z(d);if(!j)for(f=a;f<e;f++)if(d.aoColumns[f].bVisible){h=
+f;break}f=0;for(e=d.aoData.length;f<e;f++)if(d.aoData[f].nTr!==null)j?d.aoData[f].nTr.appendChild(d.aoData[f]._anHidden[a]):d.aoData[f].nTr.insertBefore(d.aoData[f]._anHidden[a],Q(d,f)[h])}else{f=0;for(e=d.aoData.length;f<e;f++)if(d.aoData[f].nTr!==null){h=Q(d,f)[a];d.aoData[f]._anHidden[a]=h;h.parentNode.removeChild(h)}}d.aoColumns[a].bVisible=b;M(d,d.aoHeader);d.nTFoot&&M(d,d.aoFooter);f=0;for(e=d.aoOpenRows.length;f<e;f++)d.aoOpenRows[f].nTr.colSpan=Z(d);if(typeof c=="undefined"||c){ea(d);C(d)}va(d)}};
+this.fnPageChange=function(a,b){var c=A(this[n.iApiIndex]);ma(c,a);E(c);if(typeof b=="undefined"||b)C(c)};this.fnDestroy=function(){var a=A(this[n.iApiIndex]),b=a.nTableWrapper.parentNode,c=a.nTBody,d,f;a.bDestroying=true;d=0;for(f=a.aoDestroyCallback.length;d<f;d++)a.aoDestroyCallback[d].fn();d=0;for(f=a.aoColumns.length;d<f;d++)a.aoColumns[d].bVisible===false&&this.fnSetColumnVis(d,true);i(a.nTableWrapper).find("*").andSelf().unbind(".DT");i("tbody>tr>td."+a.oClasses.sRowEmpty,a.nTable).parent().remove();
+if(a.nTable!=a.nTHead.parentNode){i(a.nTable).children("thead").remove();a.nTable.appendChild(a.nTHead)}if(a.nTFoot&&a.nTable!=a.nTFoot.parentNode){i(a.nTable).children("tfoot").remove();a.nTable.appendChild(a.nTFoot)}a.nTable.parentNode.removeChild(a.nTable);i(a.nTableWrapper).remove();a.aaSorting=[];a.aaSortingFixed=[];V(a);i(ba(a)).removeClass(a.asStripeClasses.join(" "));if(a.bJUI){i("th",a.nTHead).removeClass([n.oStdClasses.sSortable,n.oJUIClasses.sSortableAsc,n.oJUIClasses.sSortableDesc,n.oJUIClasses.sSortableNone].join(" "));
+i("th span."+n.oJUIClasses.sSortIcon,a.nTHead).remove();i("th",a.nTHead).each(function(){var e=i("div."+n.oJUIClasses.sSortJUIWrapper,this),h=e.contents();i(this).append(h);e.remove()})}else i("th",a.nTHead).removeClass([n.oStdClasses.sSortable,n.oStdClasses.sSortableAsc,n.oStdClasses.sSortableDesc,n.oStdClasses.sSortableNone].join(" "));a.nTableReinsertBefore?b.insertBefore(a.nTable,a.nTableReinsertBefore):b.appendChild(a.nTable);d=0;for(f=a.aoData.length;d<f;d++)a.aoData[d].nTr!==null&&c.appendChild(a.aoData[d].nTr);
+if(a.oFeatures.bAutoWidth===true)a.nTable.style.width=q(a.sDestroyWidth);i(c).children("tr:even").addClass(a.asDestroyStripes[0]);i(c).children("tr:odd").addClass(a.asDestroyStripes[1]);d=0;for(f=D.length;d<f;d++)D[d]==a&&D.splice(d,1);a=null};this.fnAdjustColumnSizing=function(a){var b=A(this[n.iApiIndex]);ea(b);if(typeof a=="undefined"||a)this.fnDraw(false);else if(b.oScroll.sX!==""||b.oScroll.sY!=="")this.oApi._fnScrollDraw(b)};for(var xa in n.oApi)if(xa)this[xa]=s(xa);this.oApi._fnExternApiFunc=
+s;this.oApi._fnInitialise=t;this.oApi._fnInitComplete=w;this.oApi._fnLanguageProcess=y;this.oApi._fnAddColumn=F;this.oApi._fnColumnOptions=x;this.oApi._fnAddData=v;this.oApi._fnCreateTr=z;this.oApi._fnGatherData=$;this.oApi._fnBuildHead=X;this.oApi._fnDrawHead=M;this.oApi._fnDraw=C;this.oApi._fnReDraw=da;this.oApi._fnAjaxUpdate=Ca;this.oApi._fnAjaxParameters=Da;this.oApi._fnAjaxUpdateDraw=Ea;this.oApi._fnServerParams=ha;this.oApi._fnAddOptionsHtml=Aa;this.oApi._fnFeatureHtmlTable=Ja;this.oApi._fnScrollDraw=
+Ma;this.oApi._fnAdjustColumnSizing=ea;this.oApi._fnFeatureHtmlFilter=Ha;this.oApi._fnFilterComplete=N;this.oApi._fnFilterCustom=Qa;this.oApi._fnFilterColumn=Pa;this.oApi._fnFilter=Oa;this.oApi._fnBuildSearchArray=oa;this.oApi._fnBuildSearchRow=ra;this.oApi._fnFilterCreateSearch=pa;this.oApi._fnDataToSearch=qa;this.oApi._fnSort=R;this.oApi._fnSortAttachListener=ja;this.oApi._fnSortingClasses=V;this.oApi._fnFeatureHtmlPaginate=La;this.oApi._fnPageChange=ma;this.oApi._fnFeatureHtmlInfo=Ka;this.oApi._fnUpdateInfo=
+Ra;this.oApi._fnFeatureHtmlLength=Ga;this.oApi._fnFeatureHtmlProcessing=Ia;this.oApi._fnProcessingDisplay=K;this.oApi._fnVisibleToColumnIndex=Na;this.oApi._fnColumnIndexToVisible=ta;this.oApi._fnNodeToDataIndex=W;this.oApi._fnVisbleColumns=Z;this.oApi._fnCalculateEnd=E;this.oApi._fnConvertToWidth=Sa;this.oApi._fnCalculateColumnWidths=ga;this.oApi._fnScrollingWidthAdjust=Ua;this.oApi._fnGetWidestNode=Ta;this.oApi._fnGetMaxLenString=Va;this.oApi._fnStringToCss=q;this.oApi._fnArrayCmp=Za;this.oApi._fnDetectType=
+ia;this.oApi._fnSettingsFromNode=A;this.oApi._fnGetDataMaster=ca;this.oApi._fnGetTrNodes=ba;this.oApi._fnGetTdNodes=Q;this.oApi._fnEscapeRegex=sa;this.oApi._fnDeleteIndex=ua;this.oApi._fnReOrderIndex=Fa;this.oApi._fnColumnOrdering=ka;this.oApi._fnLog=J;this.oApi._fnClearTable=la;this.oApi._fnSaveState=va;this.oApi._fnLoadState=Xa;this.oApi._fnCreateCookie=Wa;this.oApi._fnReadCookie=wa;this.oApi._fnDetectHeader=Y;this.oApi._fnGetUniqueThs=S;this.oApi._fnScrollBarWidth=Ya;this.oApi._fnApplyToChildren=
+P;this.oApi._fnMap=o;this.oApi._fnGetRowData=fa;this.oApi._fnGetCellData=G;this.oApi._fnSetCellData=O;this.oApi._fnGetObjectDataFn=aa;this.oApi._fnSetObjectDataFn=Ba;var ya=this;return this.each(function(){var a=0,b,c,d,f;a=0;for(b=D.length;a<b;a++){if(D[a].nTable==this)if(typeof g=="undefined"||typeof g.bRetrieve!="undefined"&&g.bRetrieve===true)return D[a].oInstance;else if(typeof g.bDestroy!="undefined"&&g.bDestroy===true){D[a].oInstance.fnDestroy();break}else{J(D[a],0,"Cannot reinitialise DataTable.\n\nTo retrieve the DataTables object for this table, please pass either no arguments to the dataTable() function, or set bRetrieve to true. Alternatively, to destory the old table and create a new one, set bDestroy to true (note that a lot of changes to the configuration can be made through the API which is usually much faster).");
+return}if(D[a].sTableId!==""&&D[a].sTableId==this.getAttribute("id")){D.splice(a,1);break}}var e=new l;D.push(e);var h=false,j=false;a=this.getAttribute("id");if(a!==null){e.sTableId=a;e.sInstance=a}else e.sInstance=n._oExternConfig.iNextUnique++;if(this.nodeName.toLowerCase()!="table")J(e,0,"Attempted to initialise DataTables on a node which is not a table: "+this.nodeName);else{e.nTable=this;e.oInstance=ya.length==1?ya:i(this).dataTable();e.oApi=ya.oApi;e.sDestroyWidth=i(this).width();if(typeof g!=
+"undefined"&&g!==null){e.oInit=g;o(e.oFeatures,g,"bPaginate");o(e.oFeatures,g,"bLengthChange");o(e.oFeatures,g,"bFilter");o(e.oFeatures,g,"bSort");o(e.oFeatures,g,"bInfo");o(e.oFeatures,g,"bProcessing");o(e.oFeatures,g,"bAutoWidth");o(e.oFeatures,g,"bSortClasses");o(e.oFeatures,g,"bServerSide");o(e.oFeatures,g,"bDeferRender");o(e.oScroll,g,"sScrollX","sX");o(e.oScroll,g,"sScrollXInner","sXInner");o(e.oScroll,g,"sScrollY","sY");o(e.oScroll,g,"bScrollCollapse","bCollapse");o(e.oScroll,g,"bScrollInfinite",
+"bInfinite");o(e.oScroll,g,"iScrollLoadGap","iLoadGap");o(e.oScroll,g,"bScrollAutoCss","bAutoCss");o(e,g,"asStripClasses","asStripeClasses");o(e,g,"asStripeClasses");o(e,g,"fnPreDrawCallback");o(e,g,"fnRowCallback");o(e,g,"fnHeaderCallback");o(e,g,"fnFooterCallback");o(e,g,"fnCookieCallback");o(e,g,"fnInitComplete");o(e,g,"fnServerData");o(e,g,"fnFormatNumber");o(e,g,"aaSorting");o(e,g,"aaSortingFixed");o(e,g,"aLengthMenu");o(e,g,"sPaginationType");o(e,g,"sAjaxSource");o(e,g,"sAjaxDataProp");o(e,
+g,"iCookieDuration");o(e,g,"sCookiePrefix");o(e,g,"sDom");o(e,g,"bSortCellsTop");o(e,g,"oSearch","oPreviousSearch");o(e,g,"aoSearchCols","aoPreSearchCols");o(e,g,"iDisplayLength","_iDisplayLength");o(e,g,"bJQueryUI","bJUI");o(e.oLanguage,g,"fnInfoCallback");typeof g.fnDrawCallback=="function"&&e.aoDrawCallback.push({fn:g.fnDrawCallback,sName:"user"});typeof g.fnServerParams=="function"&&e.aoServerParams.push({fn:g.fnServerParams,sName:"user"});typeof g.fnStateSaveCallback=="function"&&e.aoStateSave.push({fn:g.fnStateSaveCallback,
+sName:"user"});typeof g.fnStateLoadCallback=="function"&&e.aoStateLoad.push({fn:g.fnStateLoadCallback,sName:"user"});if(e.oFeatures.bServerSide&&e.oFeatures.bSort&&e.oFeatures.bSortClasses)e.aoDrawCallback.push({fn:V,sName:"server_side_sort_classes"});else e.oFeatures.bDeferRender&&e.aoDrawCallback.push({fn:V,sName:"defer_sort_classes"});if(typeof g.bJQueryUI!="undefined"&&g.bJQueryUI){e.oClasses=n.oJUIClasses;if(typeof g.sDom=="undefined")e.sDom='<"H"lfr>t<"F"ip>'}if(e.oScroll.sX!==""||e.oScroll.sY!==
+"")e.oScroll.iBarWidth=Ya();if(typeof g.iDisplayStart!="undefined"&&typeof e.iInitDisplayStart=="undefined"){e.iInitDisplayStart=g.iDisplayStart;e._iDisplayStart=g.iDisplayStart}if(typeof g.bStateSave!="undefined"){e.oFeatures.bStateSave=g.bStateSave;Xa(e,g);e.aoDrawCallback.push({fn:va,sName:"state_save"})}if(typeof g.iDeferLoading!="undefined"){e.bDeferLoading=true;e._iRecordsTotal=g.iDeferLoading;e._iRecordsDisplay=g.iDeferLoading}if(typeof g.aaData!="undefined")j=true;if(typeof g!="undefined"&&
+typeof g.aoData!="undefined")g.aoColumns=g.aoData;if(typeof g.oLanguage!="undefined")if(typeof g.oLanguage.sUrl!="undefined"&&g.oLanguage.sUrl!==""){e.oLanguage.sUrl=g.oLanguage.sUrl;i.getJSON(e.oLanguage.sUrl,null,function(u){y(e,u,true)});h=true}else y(e,g.oLanguage,false)}else g={};if(typeof g.asStripClasses=="undefined"&&typeof g.asStripeClasses=="undefined"){e.asStripeClasses.push(e.oClasses.sStripeOdd);e.asStripeClasses.push(e.oClasses.sStripeEven)}c=false;d=i(this).children("tbody").children("tr");
+a=0;for(b=e.asStripeClasses.length;a<b;a++)if(d.filter(":lt(2)").hasClass(e.asStripeClasses[a])){c=true;break}if(c){e.asDestroyStripes=["",""];if(i(d[0]).hasClass(e.oClasses.sStripeOdd))e.asDestroyStripes[0]+=e.oClasses.sStripeOdd+" ";if(i(d[0]).hasClass(e.oClasses.sStripeEven))e.asDestroyStripes[0]+=e.oClasses.sStripeEven;if(i(d[1]).hasClass(e.oClasses.sStripeOdd))e.asDestroyStripes[1]+=e.oClasses.sStripeOdd+" ";if(i(d[1]).hasClass(e.oClasses.sStripeEven))e.asDestroyStripes[1]+=e.oClasses.sStripeEven;
+d.removeClass(e.asStripeClasses.join(" "))}c=[];var k;a=this.getElementsByTagName("thead");if(a.length!==0){Y(e.aoHeader,a[0]);c=S(e)}if(typeof g.aoColumns=="undefined"){k=[];a=0;for(b=c.length;a<b;a++)k.push(null)}else k=g.aoColumns;a=0;for(b=k.length;a<b;a++){if(typeof g.saved_aoColumns!="undefined"&&g.saved_aoColumns.length==b){if(k[a]===null)k[a]={};k[a].bVisible=g.saved_aoColumns[a].bVisible}F(e,c?c[a]:null)}if(typeof g.aoColumnDefs!="undefined")for(a=g.aoColumnDefs.length-1;a>=0;a--){var m=
+g.aoColumnDefs[a].aTargets;i.isArray(m)||J(e,1,"aTargets must be an array of targets, not a "+typeof m);c=0;for(d=m.length;c<d;c++)if(typeof m[c]=="number"&&m[c]>=0){for(;e.aoColumns.length<=m[c];)F(e);x(e,m[c],g.aoColumnDefs[a])}else if(typeof m[c]=="number"&&m[c]<0)x(e,e.aoColumns.length+m[c],g.aoColumnDefs[a]);else if(typeof m[c]=="string"){b=0;for(f=e.aoColumns.length;b<f;b++)if(m[c]=="_all"||i(e.aoColumns[b].nTh).hasClass(m[c]))x(e,b,g.aoColumnDefs[a])}}if(typeof k!="undefined"){a=0;for(b=k.length;a<
+b;a++)x(e,a,k[a])}a=0;for(b=e.aaSorting.length;a<b;a++){if(e.aaSorting[a][0]>=e.aoColumns.length)e.aaSorting[a][0]=0;k=e.aoColumns[e.aaSorting[a][0]];if(typeof e.aaSorting[a][2]=="undefined")e.aaSorting[a][2]=0;if(typeof g.aaSorting=="undefined"&&typeof e.saved_aaSorting=="undefined")e.aaSorting[a][1]=k.asSorting[0];c=0;for(d=k.asSorting.length;c<d;c++)if(e.aaSorting[a][1]==k.asSorting[c]){e.aaSorting[a][2]=c;break}}V(e);a=i(this).children("thead");if(a.length===0){a=[p.createElement("thead")];this.appendChild(a[0])}e.nTHead=
+a[0];a=i(this).children("tbody");if(a.length===0){a=[p.createElement("tbody")];this.appendChild(a[0])}e.nTBody=a[0];a=i(this).children("tfoot");if(a.length>0){e.nTFoot=a[0];Y(e.aoFooter,e.nTFoot)}if(j)for(a=0;a<g.aaData.length;a++)v(e,g.aaData[a]);else $(e);e.aiDisplay=e.aiDisplayMaster.slice();e.bInitialised=true;h===false&&t(e)}})}})(jQuery,window,document);
diff --git a/plugins/DataTables/media/js/jquery.dataTables.min.js.gz b/plugins/DataTables/media/js/jquery.dataTables.min.js.gz
new file mode 100644
index 0000000000000000000000000000000000000000..c50b3c37063ef4f8b92cf0ac3ded2df638a4d0ab
GIT binary patch
literal 19393
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
M3j{-5gX0td0C#yfMgRZ+

literal 0
HcmV?d00001

diff --git a/plugins/images/progressbar.gif b/plugins/images/progressbar.gif
new file mode 100644
index 0000000000000000000000000000000000000000..abe588c15c4eda53bcecc0ab00434c925cb1b45f
GIT binary patch
literal 120
zcmV-;0EhoaNk%w1VS4}!0FeLyVr61;d3%F}ga7~kA^8LW2LJ;AEC2ui0DAxo0006>
zT!5*|?GK}zlnMie-n{z{hT=$;rU?ky$+qqbpGZvC_U+C1&iA733kq?-q4CHnBA3kE
astJWEqtqebP|XTcn2Prc4vQz30suSWax&ro

literal 0
HcmV?d00001

diff --git a/plugins/images/progressbg_black.gif b/plugins/images/progressbg_black.gif
new file mode 100644
index 0000000000000000000000000000000000000000..74fd1f9b3e052e2ca18058526c39f6eaef580cba
GIT binary patch
literal 1626
zcmZ?wbhEHb{J_A&(9XcX%*^cP=jZS5KWEOIsZ*!+_xJbq_I7u7Po6xvt*x!5rlz8z
zqNJpxtgNh|p`od%simc*s;a84uI|&PPaPc{A3uIfN=iydNQjM%jgODd%*;$nOG{5r
zPf1CMh=_=aihB3%-TU|Nqobqm-@kwF-n}bVu3Wx+`ND+@=g*&K7zLvtFwh~O19AW;
zFR*j`V_@Wv@z}87U^9oXR?LYF3lFz5DtXQE*tqCuw*)hXz=S}h<9!PDO?xyhJz`*D
z4O(VWut2qOI!B0{$ASb#W;WiUS0x`FFt*GWkh>7GVDmxWIW~>o{;X7R3Y^O$!xGE2
zW!c5m?n{|?xfGc$uC^@nda-j$I@9_@X`hJG+t%LRUhw$TT<`7c?(VMOl(=zv(^Ez+
z)_`?6KN~d8Ei#|{Pv!T4mx&vAmrRO{u~_?biJiK1T}`Ob;|2cRNy~11T^2Z}B6xM&
z*<Ge_`|CB&_-xq3ki)sW_;Jsx$=Lzyy9E2$x^91YH#c}Yllz=$>CcuP@U=g>N5ZM(
z98XO4{d0SF-`>Bu_McUQ?(C+o7Z$Cay|*u7P0;2Y7oUIeF+2F_aKB{FS6)TOnlC~c
zqEl)=t$cd7kwZ;GJb=$cqcL72V8Vg?Igw^z`NDPucLOi6M%>SzP<W%Eo%vTtOvG_l
z)6je=r=v65)4rC(=Y?x#Jnj_^xiGg`=*tg>maB8WhqTXBHCGO?JoqBI#G|eBVX^Ka
zj+=FR|BB4-cvr|8TafA5@OXN{y0n9=5-AfJS*3CoJY$n7xX{QhU-N)*PT{#q$JyCM
z8ywk-E?KJ2Enj!?*}TG2kr@lcpX^-5T5v8iV{YfGnd<XP|7oT#>JrM*oLi{Ib!fRl
z3&X1wOQvP5TDho8>eb5StGrgN{<r2>l-8QXmriN1DRn$(WUcyk>e=jGJ1cdzed|0k
z=eHi}Y+O{ul)Yl9e2Rj`yc)LK&o@*&JGo+hU)6@3*^}Pw)Lbs-arz*~i$&f}yI!rc
zZrc53nK#p(kDI*L@A`VrI)BgaYuW4fGu*D$-~WHn>g+9-|DDp@R5;6e#jLAgvvf8d
z?aF?;_0p!7uV<HXy<U5aKRO_Dae-CO^4Thre;?c~Jtd)uQ}xInqcaA(eTvWOJ)cv2
z*5>%0&F4){+iX5-{rS(P3m*4tN-o%)USoX0Uz*qCoZ;!28)r=_D=;|B%iXp0TKlh^
zMTe@p4wNoDlGU@EHO;z8<51l6xjDx()1$x5&pW;Ijne}4FTGCp8`$4%f6(wh*5aXt
zL|xOvE@i!n$1U!4KORkBKeyw_MDxBw&U4D&{d~T_+2xXlWMRmq7fbw0etE23elO60
G!5RRh`|ElD

literal 0
HcmV?d00001

diff --git a/plugins/images/progressbg_green.gif b/plugins/images/progressbg_green.gif
new file mode 100644
index 0000000000000000000000000000000000000000..f3f3bf681141982dc72429b21b4c8af7dbfd2b70
GIT binary patch
literal 1308
zcmZ?wbhEHb{J_A&(9Xb+l$vz#b;JHwRXbng?Rb&B?RnasS0x*s#jbelz3h?O(udAV
z9y+ak61L%K)Yq?HH$P8Y{Uo6GmT=cio{6`mr`}bWa!0ZCI#bJahEJb9wclWS_Uzf+
zyLWHhy2UUGMnhmIgn$mnk)XW5&hd|dkweB~!-9j&9Ku>LCpIiR+%BN(HOFIP(jf*W
zwhS4E28YIeHYceEFFrCfv2cm4h}qfT*gV}pdWOr%qNS&2=%+7Rb5kh&++^$Qdq+5@
zr2Ege3b%?lwPoey<pGPm=6Y>ib#-+FZ^@S*8xJ(IGrLXtbE9F|#f27UKe<Q;uML>5
ze|J*~xABYU9Yq^o-95d1ef*l(y>+$)4F@6)^1Qisbmol}Ub}dutlM;c>fAZiotzZ*
zYy0Ns=NHVbt9AIH+I(O!zd^woA*M@L*Tg;!Ia}5EX2zD}{nu=zw_Ut&pqYDq)d3S`
zHUT*fwcK6ba%Y~ceBSf!N9D&8=L&<LXVzMufB4|=KW6(rnWonVXNhjE;Vb_9>-4Me
zP3E%uP5-?5zPkJV`T6-R96~G>jI5#}84p;*Vir7T;&w}T(9B_^v9Ot6Ok!aRZ&*fR
zlZ2K>Vyk%455*RlvKI<$8p~EZ>eAWv;!(H3F^$JPCf76^*(4(j9yO{aX*9Npw!BcD
z;N<mVaht1Ag-VC5mS@Ujzpw=dr-X!MKAjqow({w;n6j5orzfN>crZz&%;MSPv^WQ~
z{>*hd6K7|Xy-1r7pts`*Tg9`L&*#;Ad-;5R1Dn>11ubG#YLk*?Sw3g!Jms0P*jb5d
y`I5e%sLV#0lvOX6%_)2Ja`}Qbtye3SOv_s3x?<U?SF6@+d-ZBHGaD-lgEasotT<f&

literal 0
HcmV?d00001

diff --git a/plugins/images/progressbg_orange.gif b/plugins/images/progressbg_orange.gif
new file mode 100644
index 0000000000000000000000000000000000000000..808cac7cfbcdaea93fa42d1c5de30ee9a0b060a5
GIT binary patch
literal 1308
zcmZ?wbhEHb{J_A&(9Xb+nw<1{ZNsaTRWBCiy;zX_d`{Y{WhKw1$3E`%e$?Uiu+{lt
ztJ9N7VNa(-ef|3N`OL&8eF3-9g>NSD+|H4{TcmQQK=FDE)AcBZPoF;Bh+}*9?AhJB
zcW>Rg#V`s+LtrR`fDXu!puE7&@sEL#L&jsnf`iQ*!dfvWHY_~cE}-l+$75sCAqFP4
z3>k+8hsJ(3C#eT7J~A}1aEYym+1cROJl#NghReyKrKe};r!QM`Qz-r1Wb5pEM>waX
z`_H!uw~9EmW##4N0gJundTm{Gb#(-9$(J7+4>YqgyG{CYqhZ;_g%)Q&xkv}E4VbTg
zcT)<t@r&pkMH^q;J-vN>{F>Okb+!c!2O<vgyt#LD=8Y9zyLhFn+jM^F+&R^qoD}wJ
z`{w887tF4!b@-v$d|)xZLBSd!rb}1X#6Au=Th;hx#+Ky$*KDP?UA%ChnR|ZK0TX66
z0XYt}++E*tXP&Km-t+E9<;N4}3WJ|#)>@x`_~7tAX8S&wrq>5&iEggpEB^fJ^sDeq
z=Cb=u|GfIXy8Hh5`S~pzLM#@HtfC?r4_L%v7CdO;c1w8B%weRlu$f;>Vqpt!SVm%#
zgqBBQt9a56#TJ>e7Yb||%T_$<(%JUnQMbV{jmJGE*EAg2BqIzSHL50QG`5MhyilIt
z<n?25o2yZUN{6kMXUb&1umuOFgoI^2of?t0^69jgvX@V%C!{TSFiEA%;@RZ1I0v=<
z%yl~xXJ?eXNShF#x8n(0#j};q=hb|B`Fwr@o7Rg3En-z_lagjxK4<AX<(abBS&3`;
xlD?p*%to1%RWFy#DSP#D`GPjBS1XoG%Ub2SV%e%!tJZ9L^=dUU8!HQgH2}4nHJtze

literal 0
HcmV?d00001

diff --git a/plugins/images/progressbg_red.gif b/plugins/images/progressbg_red.gif
new file mode 100644
index 0000000000000000000000000000000000000000..54dfa135f0e82934190141f2c991817dd616a2e3
GIT binary patch
literal 1308
zcmZ?wbhEHb{J_A&(9Xb+nw0dqso_;k)r-Qs7kSywGt*v`mpn^|eeCD`$kXkioAX0g
zrzeqNPh+FLe*OA9CGkmcz%6m%n*uzy<)!axsN7Liyw1*aot5F!r%yMy*`7UncK7bx
zTeog8jDpb+7z!bv19BuNFR*j`V_@Wv@z}87U^9oXR?LYF3lFyoD0|KE*qC&Pfr%|c
z#-YKXv7gOJ>cNYT3{5OtVk=^HHaIp<H;|s;a<XXY=^6Uz%huc!N<TN*I{V%c&ME2s
z^R2?IB2H~td3kxjVz0SgTUT9O9l=}j<;TVY&Fsu>lm6UjSaxxt#o13T(!py3=Ih_x
zl)`QNB6>&B##eVwZ(kq3CU$R~Z9&6<h=V+D?j4<ZV};i)UMcG~ou4{)PIV_Ih5g#T
z`T6+;v+HUdeyBDdSj=xwuttdK($zJwk3-H@HNKg#C3*ifTj^~VFC1v*o?msqgqcl1
zjzcYX*SFl6XDgrgy!%o4@x-~p;OCjO*5@BSIQ);<zE7s<^}$)9n``)rKmR)YDtwc<
z?0(ZfufDJDzJGpxehY^Xiv=UAs7S^G7O|KG51P2$5*{>j7-=kQ<`<J#*uoo@k=P`m
z<&oGbp7cYpMW*b90-MIN6_2`fw!L`NZE#HEagWJ04M#S~2!ltBs!1A+ZK5qNlqWcO
z{aD=QYE+@pVXNhtGTAR|!NDmZVVO^-Mx?ELIxVK`<<sd2X$u}qQYo`|HaRWML9IV?
z-Oj|>8D%fhCIsm1c*0ikY~}NLHQ!!7pWncy^<qJbSe4qOq*<2FSvpU7rYv?=;#$6>
wFDNRrQ6^>8%Vl%QUcFqtpiS%5iY3#sR=KWNw(8ZYHQQdjTFuPH%EDj`0N~Rv?EnA(

literal 0
HcmV?d00001

diff --git a/plugins/images/progressbg_yellow.gif b/plugins/images/progressbg_yellow.gif
new file mode 100644
index 0000000000000000000000000000000000000000..fdb0dfc981cbbfbca4e80f4ff5ccfac247b3be91
GIT binary patch
literal 1308
zcmZ?wbhEHb{J_A&(9Xb+nws?bX~U~0RWBaqy?B`Y{6X5Q$0g71#Xi36{phCK!yC>I
zZ#X@<6ZZ6O)Yq?HpFc=^awp){dEuMqcy3>izI$2a&Lzd`r<tywX882!(~UE1&z?QI
zd-v|GTelcS!Dt8!g%HpIITDl?*g5_&FmlLvY*=uxnL}7B=ER1DhuZ~|z2<mqOghBC
z#FioB(BRP6&*mic;KfIVCKfKS6)`&-9Gj;bNY8LNS+w-@4E^+FYi<gqpPOu*eeVe8
zlyv|3R^e6=r?#xTygXpB*IciytFEq&;4S&`W8;Blc4oIpe{M7^ySUKe>?arL;I#qs
z_3v&<;WmB|y`yO3tGlPSua937ySL7^py5EoL7q4Fj?TQX!fO|=ly#fVPn|obx|5T_
zer@0U{QQF1b+ryZRGSYh<~Jx<BgAy+>YCWcA!n-^-^|#Oy#Jc5^tOu^4m5MmuR37D
z%qAemp_aSrTkg!WmCt+L{iytS;#^_y^UPZ7^A8^!{>N<JC)4!$;4IP2HGIXNf1Q36
zzR6s6zv-V>-&c3vKR-Xeg+qwNf{|5JB;x^#Sj>V4P26q?51KiQG!{1Vi%BeO;SI}3
zY?9FONNg2P`k~k&Q}#lEO=H=LM_oGGUOehHIHvKq$K;xZBb#J|!J|giB#p*4(Uup=
z6P&z$EN*i(s!-{$)$&Z4>=(A+;FOTC%%@W$(pEm57E|`}>GXuO1rH{vlvzBRoEGPx
z)}Og<XX5OPvKMI+0`zt~VXJty^7*`)Z!e$EZ(!4Uv7kk)N^MfoEX(ICou@og7CS3(
zEnm_X6qVU1ld|gNvN>h1UM^qIruAyYl4)71Tvsex^=j3cZLeOfW@ck$VXy`OH^w~^

literal 0
HcmV?d00001

diff --git a/plugins/jquery-fieldselection.js b/plugins/jquery-fieldselection.js
new file mode 100644
index 0000000..78eb5b7
--- /dev/null
+++ b/plugins/jquery-fieldselection.js
@@ -0,0 +1,126 @@
+/*
+ * jQuery plugin: fieldSelection - v0.1.0 - last change: 2006-12-16
+ * (c) 2006 Alex Brem <alex@0xab.cd> - http://blog.0xab.cd
+ *
+ * fieldSelection - a jQuery plugin
+ *
+ * Copyright (c) 2006, Alex Brem
+ * All rights reserved.
+ *
+ * Redistribution and use of this software in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * Neither my name nor the names of its contributors may
+ * be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+(function() {
+
+	var fieldSelection = {
+
+		getSelection: function() {
+
+			var e = this.jquery ? this[0] : this;
+
+			return (
+
+				/* mozilla / dom 3.0 */
+				('selectionStart' in e && function() {
+					var l = e.selectionEnd - e.selectionStart;
+					return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr(e.selectionStart, l) };
+				}) ||
+
+				/* exploder */
+				(document.selection && function() {
+
+					e.focus();
+
+					var r = document.selection.createRange();
+					if (r == null) {
+						return { start: 0, end: e.value.length, length: 0 }
+					}
+
+					var re = e.createTextRange();
+					var rc = re.duplicate();
+					re.moveToBookmark(r.getBookmark());
+					rc.setEndPoint('EndToStart', re);
+
+					return { start: rc.text.length, end: rc.text.length + r.text.length, length: r.text.length, text: r.text };
+				}) ||
+
+				/* browser not supported */
+				function() {
+					return { start: 0, end: e.value.length, length: 0 };
+				}
+
+			)();
+
+		},
+
+		replaceSelection: function() {
+
+			var e = this.jquery ? this[0] : this;
+			var text = arguments[0] || '';
+
+			return (
+
+				/* mozilla / dom 3.0 */
+				('selectionStart' in e && function() {
+                    var cursorlength = e.selectionStart + text.length;
+                    e.value = e.value.substr(0, e.selectionStart) + text + e.value.substr(e.selectionEnd, e.value.length);
+					e.selectionStart = e.selectionEnd = cursorlength;
+                    return this;
+				}) ||
+
+				/* exploder */
+				(document.selection && function() {
+                    // get the current cursor position
+                    // currently IE 8 does not support methods to get cursor start position
+                    // replace below code with the equivalent once method is available 
+                    var startPosition = e.value.length;
+                    var endPosition = startPosition + text.length;
+		    
+                    // set the value
+                    e.value = e.value.substr(0, startPosition) + text + e.value.substr( endPosition, e.value.length);
+                    
+                    // move the focus to correct position, end of inserted token 
+					e.focus();
+                    var range = e.createTextRange(); 
+                    range.move( "character", endPosition ); 
+                    range.select(); 
+					return this;
+				}) ||
+
+				/* browser not supported */
+				function() {
+					e.value += text;
+					return this;
+				}
+
+			)();
+
+		}
+	};
+
+	jQuery.each(fieldSelection, function(i) { jQuery.fn[i] = this; });
+
+})();
diff --git a/plugins/jquery.FormNavigate.js b/plugins/jquery.FormNavigate.js
new file mode 100644
index 0000000..a1696cc
--- /dev/null
+++ b/plugins/jquery.FormNavigate.js
@@ -0,0 +1,94 @@
+﻿/**
+
+ * jQuery.FormNavigate.js
+
+ * jQuery Form onChange Navigate Confirmation plugin
+
+ * Browser Compatibility : IE 6.0, 7.0, 8.0; Firefox 2.0+;  Safari 3+; Opera 9+; Chrome 1+;
+
+ *
+
+ * Copyright (c) 2009 Law Ding Yong
+
+ * 
+
+ * Licensed under the MIT license:
+
+ * http://www.opensource.org/licenses/mit-license.php
+
+ *
+
+ * See the file license.txt for copying permission.
+
+ */
+
+ 
+
+ /**
+
+  * Documentation :
+
+  * ==================
+
+  *
+
+  * How to Use:
+
+  * $("YourForm").FormNavigate("YourMessage");
+
+  *  -- "YourForm" as Your Form ID $("#form") or any method to grab your form
+
+  *  -- "YourMessage" as Your onBeforeUnload Prompt Message Here
+
+  *
+
+  * This plugin handles onchange of input type of text, textarea, password, radio, checkbox, select and file to toggle on and off of window.onbeforeunload event.
+
+  * Users are able to configure the custom onBeforeUnload message.
+
+  */
+
+var global_formNavigate = true;		// Js Global Variable for onChange Flag
+
+(function($){
+
+    $.fn.FormNavigate = function(message) {
+
+        window.onbeforeunload = confirmExit;  
+
+        function confirmExit( event ) {  
+
+            if (global_formNavigate == true) {  event.cancelBubble = true;  }  else  { return message;  }
+
+        }
+
+        $(this+ ":input[type=text], :input[type='textarea'], :input[type='password'], :input[type='radio'], :input[type='checkbox'], :input[type='file'], select").change(function(){
+
+            global_formNavigate = false;
+
+        });
+
+		//to handle back button
+
+		$(this+ ":input[type='textarea']").keyup(function(){ 
+
+			global_formNavigate = false; 
+
+		}); 
+
+        $(this+ ":submit").click(function(){
+
+            global_formNavigate = true;
+
+        });
+
+        $(".token-input-list-facebook").bind( "DOMNodeRemoved DOMNodeInserted", function(){ 
+
+            global_formNavigate = false; 
+
+        });
+
+    }
+
+})(jQuery);
+

diff --git a/plugins/jquery.autocomplete.js b/plugins/jquery.autocomplete.js
new file mode 100644
index 0000000..1585052
--- /dev/null
+++ b/plugins/jquery.autocomplete.js
@@ -0,0 +1,769 @@
+/*
+ * Autocomplete - jQuery plugin 1.0.2
+ *
+ * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $
+ *
+ */
+
+;(function($) {
+	
+$.fn.extend({
+	autocomplete: function(urlOrData, options) {
+		var isUrl = typeof urlOrData == "string";
+		options = $.extend({}, $.Autocompleter.defaults, {
+			url: isUrl ? urlOrData : null,
+			data: isUrl ? null : urlOrData,
+			delay: isUrl ? $.Autocompleter.defaults.delay : 10,
+			max: options && !options.scroll ? 10 : 150
+		}, options);
+		
+		// if highlight is set to false, replace it with a do-nothing function
+		options.highlight = options.highlight || function(value) { return value; };
+		
+		// if the formatMatch option is not specified, then use formatItem for backwards compatibility
+		options.formatMatch = options.formatMatch || options.formatItem;
+		
+		return this.each(function() {
+			new $.Autocompleter(this, options);
+		});
+	},
+	result: function(handler) {
+		return this.bind("result", handler);
+	},
+	search: function(handler) {
+		return this.trigger("search", [handler]);
+	},
+	flushCache: function() {
+		return this.trigger("flushCache");
+	},
+	setOptions: function(options){
+		return this.trigger("setOptions", [options]);
+	},
+	unautocomplete: function() {
+		return this.trigger("unautocomplete");
+	}
+});
+
+$.Autocompleter = function(input, options) {
+
+	var KEY = {
+		UP: 38,
+		DOWN: 40,
+		DEL: 46,
+		TAB: 9,
+		RETURN: 13,
+		ESC: 27,
+		COMMA: 188,
+		PAGEUP: 33,
+		PAGEDOWN: 34,
+		BACKSPACE: 8
+	};
+
+	// Create $ object for input element
+	var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+
+	var timeout;
+	var previousValue = "";
+	var cache = $.Autocompleter.Cache(options);
+	var hasFocus = 0;
+	var lastKeyPressCode;
+	var config = {
+		mouseDownOnSelect: false
+	};
+	var select = $.Autocompleter.Select(options, input, selectCurrent, config);
+	
+	var blockSubmit;
+	
+	// prevent form submit in opera when selecting with return key
+	$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
+		if (blockSubmit) {
+			blockSubmit = false;
+			return false;
+		}
+	});
+	
+	// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
+	$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
+		// track last key pressed
+		lastKeyPressCode = event.keyCode;
+		switch(event.keyCode) {
+		
+			case KEY.UP:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.prev();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.DOWN:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.next();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.PAGEUP:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.pageUp();
+				} else {
+					onChange(0, true);
+				}
+				break;
+				
+			case KEY.PAGEDOWN:
+				event.preventDefault();
+				if ( select.visible() ) {
+					select.pageDown();
+				} else {
+					onChange(0, true);
+				}
+				break;
+			
+			// matches also semicolon
+			case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
+			case KEY.TAB:
+			case KEY.RETURN:
+				if( selectCurrent() ) {
+					// stop default to prevent a form submit, Opera needs special handling
+					event.preventDefault();
+					blockSubmit = true;
+					return false;
+				}
+				break;
+				
+			case KEY.ESC:
+				select.hide();
+				break;
+				
+			default:
+				clearTimeout(timeout);
+				timeout = setTimeout(onChange, options.delay);
+				break;
+		}
+	}).focus(function(){
+		// track whether the field has focus, we shouldn't process any
+		// results if the field no longer has focus
+		hasFocus++;
+	}).blur(function() {
+		hasFocus = 0;
+		if (!config.mouseDownOnSelect) {
+			hideResults();
+		}
+	}).click(function() {
+		// show select when clicking in a focused field
+		if ( hasFocus++ > 1 && !select.visible() ) {
+			onChange(0, true);
+		}
+	}).bind("search", function() {
+		// TODO why not just specifying both arguments?
+		var fn = (arguments.length > 1) ? arguments[1] : null;
+		function findValueCallback(q, data) {
+			var result;
+			if( data && data.length ) {
+				for (var i=0; i < data.length; i++) {
+					if( data[i].result.toLowerCase() == q.toLowerCase() ) {
+						result = data[i];
+						break;
+					}
+				}
+			}
+			if( typeof fn == "function" ) fn(result);
+			else $input.trigger("result", result && [result.data, result.value]);
+		}
+		$.each(trimWords($input.val()), function(i, value) {
+			request(value, findValueCallback, findValueCallback);
+		});
+	}).bind("flushCache", function() {
+		cache.flush();
+	}).bind("setOptions", function() {
+		$.extend(options, arguments[1]);
+		// if we've updated the data, repopulate
+		if ( "data" in arguments[1] )
+			cache.populate();
+	}).bind("unautocomplete", function() {
+		select.unbind();
+		$input.unbind();
+		$(input.form).unbind(".autocomplete");
+	}).bind("input", function() {
+        // needed for chinese input? see CRM-6135 and http://plugins.jquery.com/node/14682 
+        // this breaks "delay" though, so lets only use it with chinese minChars setting 
+        if (options.minChars <= 1) { 
+            onChange(0, true); 
+        } 
+  });
+	
+	function selectCurrent() {
+		var selected = select.selected();
+		if( !selected )
+			return false;
+		
+		var v = selected.result;
+		previousValue = v;
+		
+		if ( options.multiple ) {
+			var words = trimWords($input.val());
+			if ( words.length > 1 ) {
+				v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
+			}
+			v += options.multipleSeparator;
+		}
+		
+		$input.val(v);
+		hideResultsNow();
+		$input.trigger("result", [selected.data, selected.value]);
+		return true;
+	}
+	
+	function onChange(crap, skipPrevCheck) {
+		if( lastKeyPressCode == KEY.DEL ) {
+			select.hide();
+			return;
+		}
+		
+		var currentValue = $input.val();
+		
+		if ( !skipPrevCheck && currentValue == previousValue )
+			return;
+		
+		previousValue = currentValue;
+		
+		currentValue = lastWord(currentValue);
+		if ( currentValue.length >= options.minChars) {
+			$input.addClass(options.loadingClass);
+			if (!options.matchCase)
+				currentValue = currentValue.toLowerCase();
+			request(currentValue, receiveData, hideResultsNow);
+		} else {
+			stopLoading();
+			select.hide();
+		}
+	};
+	
+	function trimWords(value) {
+		if ( !value ) {
+			return [""];
+		}
+		var words = value.split( options.multipleSeparator );
+		var result = [];
+		$.each(words, function(i, value) {
+			if ( $.trim(value) )
+				result[i] = $.trim(value);
+		});
+		return result;
+	}
+	
+	function lastWord(value) {
+		if ( !options.multiple )
+			return value;
+		var words = trimWords(value);
+		return words[words.length - 1];
+	}
+	
+	// fills in the input box w/the first match (assumed to be the best match)
+	// q: the term entered
+	// sValue: the first matching result
+	function autoFill(q, sValue){
+		// autofill in the complete box w/the first match as long as the user hasn't entered in more data
+		// if the last user key pressed was backspace, don't autofill
+		if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
+			// fill in the value (keep the case the user has typed)
+			$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
+			// select the portion of the value not typed by the user (so the next character will erase)
+			$.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
+		}
+	};
+
+	function hideResults() {
+		clearTimeout(timeout);
+		timeout = setTimeout(hideResultsNow, 200);
+	};
+
+	function hideResultsNow() {
+		var wasVisible = select.visible();
+		select.hide();
+		clearTimeout(timeout);
+		stopLoading();
+		if (options.mustMatch) {
+			// call search and run callback
+			$input.search(
+				function (result){
+					// if no value found, clear the input box
+					if( !result ) {
+						if (options.multiple) {
+							var words = trimWords($input.val()).slice(0, -1);
+							$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
+						}
+						else
+							$input.val( "" );
+					}
+				}
+			);
+		}
+		if (wasVisible)
+			// position cursor at end of input field
+			$.Autocompleter.Selection(input, input.value.length, input.value.length);
+	};
+
+	function receiveData(q, data) {
+		if ( data && data.length && hasFocus ) {
+			stopLoading();
+			select.display(data, q);
+			autoFill(q, data[0].value);
+			select.show();
+		} else {
+			hideResultsNow();
+		}
+	};
+
+	function request(term, success, failure) {
+		if (!options.matchCase)
+			term = term.toLowerCase();
+		var data = cache.load(term);
+		// recieve the cached data
+		if (data && data.length) {
+			success(term, data);
+		// if an AJAX url has been supplied, try loading the data now
+		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
+			
+			var extraParams = {
+				timestamp: +new Date()
+			};
+			$.each(options.extraParams, function(key, param) {
+				extraParams[key] = typeof param == "function" ? param() : param;
+			});
+			
+			$.ajax({
+				// try to leverage ajaxQueue plugin to abort previous requests
+				mode: "abort",
+				// limit abortion to this input
+				port: "autocomplete" + input.name,
+				dataType: options.dataType,
+				url: options.url,
+				data: $.extend({
+					s: lastWord(term),
+					limit: options.max
+				}, extraParams),
+				success: function(data) {
+					var parsed = options.parse && options.parse(data) || parse(data);
+					cache.add(term, parsed);
+					success(term, parsed);
+				}
+			});
+		} else {
+			// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
+			select.emptyList();
+			failure(term);
+		}
+	};
+	
+	function parse(data) {
+		var parsed = [];
+		var rows = data.split("\n");
+		for (var i=0; i < rows.length; i++) {
+			var row = $.trim(rows[i]);
+			if (row) {
+				row = row.split("|");
+				parsed[parsed.length] = {
+					data: row,
+					value: row[0],
+					result: options.formatResult && options.formatResult(row, row[0]) || row[0]
+				};
+			}
+		}
+		return parsed;
+	};
+
+	function stopLoading() {
+		$input.removeClass(options.loadingClass);
+	};
+
+};
+
+$.Autocompleter.defaults = {
+	inputClass: "ac_input",
+	resultsClass: "ac_results",
+	loadingClass: "ac_loading",
+	minChars: 0,
+	delay: 400,
+	matchCase: false,
+	matchSubset: false,
+	matchContains: false,
+	cacheLength: 10,
+	max: 100,
+	mustMatch: false,
+	extraParams: {},
+	selectFirst: true,
+	formatItem: function(row) { return row[0]; },
+	formatMatch: null,
+	autoFill: false,
+	width: 0,
+	multiple: false,
+	multipleSeparator: ", ",
+	highlight: function(value, term) {
+		return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+	},
+    scroll: true,
+    scrollHeight: 300
+};
+
+$.Autocompleter.Cache = function(options) {
+
+	var data = {};
+	var length = 0;
+	
+	function matchSubset(s, sub) {
+		if (!options.matchCase) 
+			s = s.toLowerCase();
+		var i = s.indexOf(sub);
+		if (i == -1) return false;
+		return i == 0 || options.matchContains;
+	};
+	
+	function add(q, value) {
+		if (length > options.cacheLength){
+			flush();
+		}
+		if (!data[q]){ 
+			length++;
+		}
+		data[q] = value;
+	}
+	
+	function populate(){
+		if( !options.data ) return false;
+		// track the matches
+		var stMatchSets = {},
+			nullData = 0;
+
+		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
+		if( !options.url ) options.cacheLength = 1;
+		
+		// track all options for minChars = 0
+		stMatchSets[""] = [];
+		
+		// loop through the array and create a lookup structure
+		for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
+			var rawValue = options.data[i];
+			// if rawValue is a string, make an array otherwise just reference the array
+			rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
+			
+			var value = options.formatMatch(rawValue, i+1, options.data.length);
+			if ( value === false )
+				continue;
+				
+			var firstChar = value.charAt(0).toLowerCase();
+			// if no lookup array for this character exists, look it up now
+			if( !stMatchSets[firstChar] ) 
+				stMatchSets[firstChar] = [];
+
+			// if the match is a string
+			var row = {
+				value: value,
+				data: rawValue,
+				result: options.formatResult && options.formatResult(rawValue) || value
+			};
+			
+			// push the current match into the set list
+			stMatchSets[firstChar].push(row);
+
+			// keep track of minChars zero items
+			if ( nullData++ < options.max ) {
+				stMatchSets[""].push(row);
+			}
+		};
+
+		// add the data items to the cache
+		$.each(stMatchSets, function(i, value) {
+			// increase the cache size
+			options.cacheLength++;
+			// add to the cache
+			add(i, value);
+		});
+	}
+	
+	// populate any existing data
+	setTimeout(populate, 25);
+	
+	function flush(){
+		data = {};
+		length = 0;
+	}
+	
+	return {
+		flush: flush,
+		add: add,
+		populate: populate,
+		load: function(q) {
+			if (!options.cacheLength || !length)
+				return null;
+			/* 
+			 * if dealing w/local data and matchContains than we must make sure
+			 * to loop through all the data collections looking for matches
+			 */
+			if( !options.url && options.matchContains ){
+				// track all matches
+				var csub = [];
+				// loop through all the data grids for matches
+				for( var k in data ){
+					// don't search through the stMatchSets[""] (minChars: 0) cache
+					// this prevents duplicates
+					if( k.length > 0 ){
+						var c = data[k];
+						$.each(c, function(i, x) {
+							// if we've got a match, add it to the array
+							if (matchSubset(x.value, q)) {
+								csub.push(x);
+							}
+						});
+					}
+				}				
+				return csub;
+			} else 
+			// if the exact item exists, use it
+			if (data[q]){
+				return data[q];
+			} else
+			if (options.matchSubset) {
+				for (var i = q.length - 1; i >= options.minChars; i--) {
+					var c = data[q.substr(0, i)];
+					if (c) {
+						var csub = [];
+						$.each(c, function(i, x) {
+							if (matchSubset(x.value, q)) {
+								csub[csub.length] = x;
+							}
+						});
+						return csub;
+					}
+				}
+			}
+			return null;
+		}
+	};
+};
+
+$.Autocompleter.Select = function (options, input, select, config) {
+	var CLASSES = {
+		ACTIVE: "ac_over"
+	};
+	
+	var listItems,
+		active = -1,
+		data,
+		term = "",
+		needsInit = true,
+		element,
+		list;
+	
+	// Create results
+	function init() {
+		if (!needsInit)
+			return;
+
+		element = $("<div/>")
+		.hide()
+		.addClass(options.resultsClass)
+		.css("position", "absolute")
+		.appendTo(document.body);
+		 
+		innerElement = $("<div/>")
+		.addClass('ac_results-inner')
+		.appendTo(element);
+	
+		list = $("<ul/>").appendTo(innerElement).mouseover( function(event) {
+			if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
+	            active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
+			    $(target(event)).addClass(CLASSES.ACTIVE);            
+	        }
+		}).click(function(event) {
+			$(target(event)).addClass(CLASSES.ACTIVE);
+			select();
+			// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
+			input.focus();
+			return false;
+		}).mousedown(function() {
+			config.mouseDownOnSelect = true;
+		}).mouseup(function() {
+			config.mouseDownOnSelect = false;
+		});
+		
+		if( options.width > 0 )
+			element.css("width", options.width);
+			
+		needsInit = false;
+	} 
+	
+	function target(event) {
+		var element = event.target;
+		while(element && element.tagName != "LI")
+			element = element.parentNode;
+		// more fun with IE, sometimes event.target is empty, just ignore it then
+		if(!element)
+			return [];
+		return element;
+	}
+
+	function moveSelect(step) {
+		listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
+		movePosition(step);
+        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
+        if(options.scroll) {
+            var offset = 0;
+            listItems.slice(0, active).each(function() {
+				offset += this.offsetHeight;
+			});
+            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
+                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
+            } else if(offset < list.scrollTop()) {
+                list.scrollTop(offset);
+            }
+        }
+	};
+	
+	function movePosition(step) {
+		active += step;
+		if (active < 0) {
+			active = listItems.size() - 1;
+		} else if (active >= listItems.size()) {
+			active = 0;
+		}
+	}
+	
+	function limitNumberOfItems(available) {
+		return options.max && options.max < available
+			? options.max
+			: available;
+	}
+	
+	function fillList() {
+		list.empty();
+		var max = limitNumberOfItems(data.length);
+		for (var i=0; i < max; i++) {
+			if (!data[i])
+				continue;
+			var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
+			if ( formatted === false )
+				continue;
+			var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
+			$.data(li, "ac_data", data[i]);
+		}
+		listItems = list.find("li");
+		if ( options.selectFirst ) {
+			listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
+			active = 0;
+		}
+		// apply bgiframe if available
+		if ( $.fn.bgiframe )
+			list.bgiframe();
+	}
+	
+	return {
+		display: function(d, q) {
+			init();
+			data = d;
+			term = q;
+			fillList();
+		},
+		next: function() {
+			moveSelect(1);
+		},
+		prev: function() {
+			moveSelect(-1);
+		},
+		pageUp: function() {
+			if (active != 0 && active - 8 < 0) {
+				moveSelect( -active );
+			} else {
+				moveSelect(-8);
+			}
+		},
+		pageDown: function() {
+			if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
+				moveSelect( listItems.size() - 1 - active );
+			} else {
+				moveSelect(8);
+			}
+		},
+		hide: function() {
+			element && element.hide();
+			listItems && listItems.removeClass(CLASSES.ACTIVE);
+			active = -1;
+		},
+		visible : function() {
+			return element && element.is(":visible");
+		},
+		current: function() {
+			return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
+		},
+		show: function() {
+			var offset = $(input).offset();
+			element.css({
+				width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
+				top: offset.top + input.offsetHeight,
+				left: offset.left
+			}).show();
+            if(options.scroll) {
+                list.scrollTop(0);
+                list.css({
+					maxHeight: options.scrollHeight,
+					overflow: 'auto'
+				});
+				
+                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
+					var listHeight = 0;
+					listItems.each(function() {
+						listHeight += this.offsetHeight;
+					});
+					var scrollbarsVisible = listHeight > options.scrollHeight;
+                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
+					if (!scrollbarsVisible) {
+						// IE doesn't recalculate width when scrollbar disappears
+						listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
+					}
+                }
+                
+            }
+		},
+		selected: function() {
+			var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
+			return selected && selected.length && $.data(selected[0], "ac_data");
+		},
+		emptyList: function (){
+			list && list.empty();
+		},
+		unbind: function() {
+			element && element.remove();
+		}
+	};
+};
+
+$.Autocompleter.Selection = function(field, start, end) {
+	if( field.createTextRange ){
+		var selRange = field.createTextRange();
+		selRange.collapse(true);
+		selRange.moveStart("character", start);
+		selRange.moveEnd("character", end);
+		selRange.select();
+	} else if( field.setSelectionRange ){
+		field.setSelectionRange(start, end);
+	} else {
+		if( field.selectionStart ){
+			field.selectionStart = start;
+			field.selectionEnd = end;
+		}
+	}
+	field.focus();
+};
+
+})(jQuery);
diff --git a/plugins/jquery.blockUI.js b/plugins/jquery.blockUI.js
new file mode 100644
index 0000000..ad51df0
--- /dev/null
+++ b/plugins/jquery.blockUI.js
@@ -0,0 +1,530 @@
+﻿/*!
+ * jQuery blockUI plugin
+ * Version 2.42 (11-MAY-2012)
+ * @requires jQuery v1.2.3 or later
+ *
+ * Examples at: http://malsup.com/jquery/block/
+ * Copyright (c) 2007-2010 M. Alsup
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Thanks to Amir-Hossein Sobhi for some excellent contributions!
+ */
+
+;(function() {
+
+	function setup($) {
+		if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) {
+			alert('blockUI requires jQuery v1.2.3 or later!  You are using v' + $.fn.jquery);
+			return;
+		}
+
+		$.fn._fadeIn = $.fn.fadeIn;
+
+		var noOp = function() {};
+
+		// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
+		// retarded userAgent strings on Vista)
+		var mode = document.documentMode || 0;
+		var setExpr = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8);
+		var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent) && !mode;
+
+		// global $ methods for blocking/unblocking the entire page
+		$.blockUI   = function(opts) { install(window, opts); };
+		$.unblockUI = function(opts) { remove(window, opts); };
+
+		// convenience method for quick growl-like notifications  (http://www.google.com/search?q=growl)
+		$.growlUI = function(title, message, timeout, onClose) {
+			var $m = $('<div class="growlUI"></div>');
+			if (title) $m.append('<h1>'+title+'</h1>');
+			if (message) $m.append('<h2>'+message+'</h2>');
+			if (timeout == undefined) timeout = 3000;
+			$.blockUI({
+				message: $m, fadeIn: 700, fadeOut: 1000, centerY: false,
+				timeout: timeout, showOverlay: false,
+				onUnblock: onClose,
+				css: $.blockUI.defaults.growlCSS
+			});
+		};
+
+		// plugin method for blocking element content
+		$.fn.block = function(opts) {
+			var fullOpts = $.extend({}, $.blockUI.defaults, opts || {});
+			this.each(function() {
+				var $el = $(this);
+				if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked'))
+					return;
+				$el.unblock({ fadeOut: 0 });
+			});
+
+			return this.each(function() {
+				if ($.css(this,'position') == 'static')
+					this.style.position = 'relative';
+				if ($.browser.msie)
+					this.style.zoom = 1; // force 'hasLayout'
+				install(this, opts);
+			});
+		};
+
+		// plugin method for unblocking element content
+		$.fn.unblock = function(opts) {
+			return this.each(function() {
+				remove(this, opts);
+			});
+		};
+
+		$.blockUI.version = 2.42; // 2nd generation blocking at no extra cost!
+
+		// override these in your code to change the default behavior and style
+		$.blockUI.defaults = {
+			// message displayed when blocking (use null for no message)
+			message:  '<h1>Please wait...</h1>',
+
+			title: null,	  // title string; only used when theme == true
+			draggable: true,  // only used when theme == true (requires jquery-ui.js to be loaded)
+
+			theme: false, // set to true to use with jQuery UI themes
+
+			// styles for the message when blocking; if you wish to disable
+			// these and use an external stylesheet then do this in your code:
+			// $.blockUI.defaults.css = {};
+			css: {
+				padding:	0,
+				margin:		0,
+				width:		'30%',
+				top:		'40%',
+				left:		'35%',
+				textAlign:	'center',
+				color:		'#000',
+				border:		'3px solid #aaa',
+				backgroundColor:'#fff',
+				cursor:		'wait'
+			},
+
+			// minimal style set used when themes are used
+			themedCSS: {
+				width:	'30%',
+				top:	'40%',
+				left:	'35%'
+			},
+
+			// styles for the overlay
+			overlayCSS:  {
+				backgroundColor: '#000',
+				opacity:	  	 0.6,
+				cursor:		  	 'wait'
+			},
+
+			// styles applied when using $.growlUI
+			growlCSS: {
+				width:  	'350px',
+				top:		'10px',
+				left:   	'',
+				right:  	'10px',
+				border: 	'none',
+				padding:	'5px',
+				opacity:	0.6,
+				cursor: 	'default',
+				color:		'#fff',
+				backgroundColor: '#000',
+				'-webkit-border-radius': '10px',
+				'-moz-border-radius':	 '10px',
+				'border-radius': 		 '10px'
+			},
+
+			// IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
+			// (hat tip to Jorge H. N. de Vasconcelos)
+			iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',
+
+			// force usage of iframe in non-IE browsers (handy for blocking applets)
+			forceIframe: false,
+
+			// z-index for the blocking overlay
+			baseZ: 1000,
+
+			// set these to true to have the message automatically centered
+			centerX: true, // <-- only effects element blocking (page block controlled via css above)
+			centerY: true,
+
+			// allow body element to be stetched in ie6; this makes blocking look better
+			// on "short" pages.  disable if you wish to prevent changes to the body height
+			allowBodyStretch: true,
+
+			// enable if you want key and mouse events to be disabled for content that is blocked
+			bindEvents: true,
+
+			// be default blockUI will supress tab navigation from leaving blocking content
+			// (if bindEvents is true)
+			constrainTabKey: true,
+
+			// fadeIn time in millis; set to 0 to disable fadeIn on block
+			fadeIn:  200,
+
+			// fadeOut time in millis; set to 0 to disable fadeOut on unblock
+			fadeOut:  400,
+
+			// time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
+			timeout: 0,
+
+			// disable if you don't want to show the overlay
+			showOverlay: true,
+
+			// if true, focus will be placed in the first available input field when
+			// page blocking
+			focusInput: true,
+
+			// suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
+			applyPlatformOpacityRules: true,
+
+			// callback method invoked when fadeIn has completed and blocking message is visible
+			onBlock: null,
+
+			// callback method invoked when unblocking has completed; the callback is
+			// passed the element that has been unblocked (which is the window object for page
+			// blocks) and the options that were passed to the unblock call:
+			//	 onUnblock(element, options)
+			onUnblock: null,
+
+			// don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
+			quirksmodeOffsetHack: 4,
+
+			// class name of the message block
+			blockMsgClass: 'blockMsg',
+
+			// if it is already blocked, then ignore it (don't unblock and reblock)
+			ignoreIfBlocked: false
+		};
+
+		// private data and functions follow...
+
+		var pageBlock = null;
+		var pageBlockEls = [];
+
+		function install(el, opts) {
+			var css, themedCSS;
+			var full = (el == window);
+			var msg = (opts && opts.message !== undefined ? opts.message : undefined);
+			opts = $.extend({}, $.blockUI.defaults, opts || {});
+
+			if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked'))
+				return;
+
+			opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
+			css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
+			themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
+			msg = msg === undefined ? opts.message : msg;
+
+			// remove the current block (if there is one)
+			if (full && pageBlock)
+				remove(window, {fadeOut:0});
+
+			// if an existing element is being used as the blocking content then we capture
+			// its current place in the DOM (and current display style) so we can restore
+			// it when we unblock
+			if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
+				var node = msg.jquery ? msg[0] : msg;
+				var data = {};
+				$(el).data('blockUI.history', data);
+				data.el = node;
+				data.parent = node.parentNode;
+				data.display = node.style.display;
+				data.position = node.style.position;
+				if (data.parent)
+					data.parent.removeChild(node);
+			}
+
+			$(el).data('blockUI.onUnblock', opts.onUnblock);
+			var z = opts.baseZ;
+
+			// blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
+			// layer1 is the iframe layer which is used to supress bleed through of underlying content
+			// layer2 is the overlay layer which has opacity and a wait cursor (by default)
+			// layer3 is the message content that is displayed while blocking
+
+			var lyr1 = ($.browser.msie || opts.forceIframe)
+				? $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>')
+				: $('<div class="blockUI" style="display:none"></div>');
+
+			var lyr2 = opts.theme
+				? $('<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+ (z++) +';display:none"></div>')
+				: $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
+
+			var lyr3, s;
+			if (opts.theme && full) {
+				s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:fixed">';
+        if ( opts.title ) {
+				  s += '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || '&nbsp;')+'</div>';
+        }
+        
+        s += '<div class="ui-widget-content ui-dialog-content"></div>' +
+					'</div>';
+			}
+			else if (opts.theme) {
+				s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:absolute">';
+        if ( opts.title ) {
+          s += '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || '&nbsp;')+'</div>'
+        }  
+        s += '<div class="ui-widget-content ui-dialog-content"></div>' +
+					'</div>';
+			}
+			else if (full) {
+				s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage" style="z-index:'+(z+10)+';display:none;position:fixed"></div>';
+			}
+			else {
+				s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement" style="z-index:'+(z+10)+';display:none;position:absolute"></div>';
+			}
+			lyr3 = $(s);
+
+			// if we have a message, style it
+			if (msg) {
+				if (opts.theme) {
+					lyr3.css(themedCSS);
+					lyr3.addClass('ui-widget-content');
+				}
+				else
+					lyr3.css(css);
+			}
+
+			// style the overlay
+			if (!opts.theme && (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform))))
+				lyr2.css(opts.overlayCSS);
+			lyr2.css('position', full ? 'fixed' : 'absolute');
+
+			// make iframe layer transparent in IE
+			if ($.browser.msie || opts.forceIframe)
+				lyr1.css('opacity',0.0);
+
+			//$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
+			var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
+			$.each(layers, function() {
+				this.appendTo($par);
+			});
+
+			if (opts.theme && opts.draggable && $.fn.draggable) {
+				lyr3.draggable({
+					handle: '.ui-dialog-titlebar',
+					cancel: 'li'
+				});
+			}
+
+			// ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
+			var expr = setExpr && (!$.boxModel || $('object,embed', full ? null : el).length > 0);
+			if (ie6 || expr) {
+				// give body 100% height
+				if (full && opts.allowBodyStretch && $.boxModel)
+					$('html,body').css('height','100%');
+
+				// fix ie6 issue when blocked element has a border width
+				if ((ie6 || !$.boxModel) && !full) {
+					var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
+					var fixT = t ? '(0 - '+t+')' : 0;
+					var fixL = l ? '(0 - '+l+')' : 0;
+				}
+
+				// simulate fixed position
+				$.each([lyr1,lyr2,lyr3], function(i,o) {
+					var s = o[0].style;
+					s.position = 'absolute';
+					if (i < 2) {
+						full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"')
+							 : s.setExpression('height','this.parentNode.offsetHeight + "px"');
+						full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"')
+							 : s.setExpression('width','this.parentNode.offsetWidth + "px"');
+						if (fixL) s.setExpression('left', fixL);
+						if (fixT) s.setExpression('top', fixT);
+					}
+					else if (opts.centerY) {
+						if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
+						s.marginTop = 0;
+					}
+					else if (!opts.centerY && full) {
+						var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0;
+						var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
+						s.setExpression('top',expression);
+					}
+				});
+			}
+
+			// show the message
+			if (msg) {
+				if (opts.theme)
+					lyr3.find('.ui-widget-content').append(msg);
+				else
+					lyr3.append(msg);
+				if (msg.jquery || msg.nodeType)
+					$(msg).show();
+			}
+
+			if (($.browser.msie || opts.forceIframe) && opts.showOverlay)
+				lyr1.show(); // opacity is zero
+			if (opts.fadeIn) {
+				var cb = opts.onBlock ? opts.onBlock : noOp;
+				var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
+				var cb2 = msg ? cb : noOp;
+				if (opts.showOverlay)
+					lyr2._fadeIn(opts.fadeIn, cb1);
+				if (msg)
+					lyr3._fadeIn(opts.fadeIn, cb2);
+			}
+			else {
+				if (opts.showOverlay)
+					lyr2.show();
+				if (msg)
+					lyr3.show();
+				if (opts.onBlock)
+					opts.onBlock();
+			}
+
+			// bind key and mouse events
+			bind(1, el, opts);
+
+			if (full) {
+				pageBlock = lyr3[0];
+				pageBlockEls = $(':input:enabled:visible',pageBlock);
+				if (opts.focusInput)
+					setTimeout(focus, 20);
+			}
+			else
+				center(lyr3[0], opts.centerX, opts.centerY);
+
+			if (opts.timeout) {
+				// auto-unblock
+				var to = setTimeout(function() {
+					full ? $.unblockUI(opts) : $(el).unblock(opts);
+				}, opts.timeout);
+				$(el).data('blockUI.timeout', to);
+			}
+		};
+
+		// remove the block
+		function remove(el, opts) {
+			var full = (el == window);
+			var $el = $(el);
+			var data = $el.data('blockUI.history');
+			var to = $el.data('blockUI.timeout');
+			if (to) {
+				clearTimeout(to);
+				$el.removeData('blockUI.timeout');
+			}
+			opts = $.extend({}, $.blockUI.defaults, opts || {});
+			bind(0, el, opts); // unbind events
+
+			if (opts.onUnblock === null) {
+				opts.onUnblock = $el.data('blockUI.onUnblock');
+				$el.removeData('blockUI.onUnblock');
+			}
+
+			var els;
+			if (full) // crazy selector to handle odd field errors in ie6/7
+				els = $('body').children().filter('.blockUI').add('body > .blockUI');
+			else
+				els = $('.blockUI', el);
+
+			if (full)
+				pageBlock = pageBlockEls = null;
+
+			if (opts.fadeOut) {
+				els.fadeOut(opts.fadeOut);
+				setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut);
+			}
+			else
+				reset(els, data, opts, el);
+		};
+
+		// move blocking element back into the DOM where it started
+		function reset(els,data,opts,el) {
+			els.each(function(i,o) {
+				// remove via DOM calls so we don't lose event handlers
+				if (this.parentNode)
+					this.parentNode.removeChild(this);
+			});
+
+			if (data && data.el) {
+				data.el.style.display = data.display;
+				data.el.style.position = data.position;
+				if (data.parent)
+					data.parent.appendChild(data.el);
+				$(el).removeData('blockUI.history');
+			}
+
+			if (typeof opts.onUnblock == 'function')
+				opts.onUnblock(el,opts);
+		};
+
+		// bind/unbind the handler
+		function bind(b, el, opts) {
+			var full = el == window, $el = $(el);
+
+			// don't bother unbinding if there is nothing to unbind
+			if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
+				return;
+
+			$el.data('blockUI.isBlocked', b);
+
+			// don't bind events when overlay is not in use or if bindEvents is false
+			if (!opts.bindEvents || (b && !opts.showOverlay))
+				return;
+
+			// bind anchors and inputs for mouse and key events
+			var events = 'mousedown mouseup keydown keypress';
+			b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler);
+
+		// former impl...
+		//	   var $e = $('a,:input');
+		//	   b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
+		};
+
+		// event handler to suppress keyboard/mouse events when blocking
+		function handler(e) {
+			// allow tab navigation (conditionally)
+			if (e.keyCode && e.keyCode == 9) {
+				if (pageBlock && e.data.constrainTabKey) {
+					var els = pageBlockEls;
+					var fwd = !e.shiftKey && e.target === els[els.length-1];
+					var back = e.shiftKey && e.target === els[0];
+					if (fwd || back) {
+						setTimeout(function(){focus(back)},10);
+						return false;
+					}
+				}
+			}
+			var opts = e.data;
+			// allow events within the message content
+			if ($(e.target).parents('div.' + opts.blockMsgClass).length > 0)
+				return true;
+
+			// allow events for content that is not being blocked
+			return $(e.target).parents().children().filter('div.blockUI').length == 0;
+		};
+
+		function focus(back) {
+			if (!pageBlockEls)
+				return;
+			var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
+			if (e)
+				e.focus();
+		};
+
+		function center(el, x, y) {
+			var p = el.parentNode, s = el.style;
+			var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
+			var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
+			if (x) s.left = l > 0 ? (l+'px') : '0';
+			if (y) s.top  = t > 0 ? (t+'px') : '0';
+		};
+
+		function sz(el, p) {
+			return parseInt($.css(el,p))||0;
+		};
+
+	};
+
+
+	if (typeof define === 'function' && define.amd && define.amd.jQuery) {
+		define(['jquery'], setup);
+	} else {
+		setup(jQuery);
+	}
+
+})();
diff --git a/plugins/jquery.chainedSelects.js b/plugins/jquery.chainedSelects.js
new file mode 100644
index 0000000..886681d
--- /dev/null
+++ b/plugins/jquery.chainedSelects.js
@@ -0,0 +1,177 @@
+/**
+
+*    Chained Selects for jQuery 
+
+*    Copyright (C) 2008 Ziadin Givan www.CodeAssembly.com  
+
+*
+
+*    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 http://www.gnu.org/licenses/
+
+*
+
+*    
+
+*   settings = { usePost : true, before:function() {}, after: function() {}, default: null, parameters : { parameter1 : 'value1', parameter2 : 'value2'} }	
+
+*   if usePost is true, then the form will use POST to pass the parameters to the target, otherwise will use GET
+
+*   "before" function is called before the ajax request and "after" function is called after the ajax request.
+
+*   If defaultValue is not null then the specified option will be selected.
+
+*   You can specify additional parameters to be sent to the the server in settings.parameters.
+
+*
+
+*/
+
+;(function($) {
+
+    
+
+jQuery.fn.chainSelect = function( target, url, settings ) 
+
+{
+
+  return this.each( function()
+
+  {
+
+	$(this).change( function( ) 
+
+	{
+
+		settings = $.extend(
+
+		{
+
+			after : null,
+
+			before : null,
+
+			usePost : false,
+
+			defaultValue : null,
+
+			parameters : {'_id' : $(this).attr('id'), '_name' : $(this).attr('name')}
+
+        } , settings);
+
+
+
+		settings.parameters._value =  $(this).val();
+
+
+
+		if (settings.before != null) 
+
+		{
+
+			settings.before( target );
+
+		}
+
+
+
+		ajaxCallback = function(data, textStatus) 
+
+		{
+
+			$(target).html("");//clear old options
+
+			data = eval(data);//get json array
+
+                        if ( data != null ) {
+
+			    for (i = 0; i < data.length; i++) {
+
+				$(target).get(0).add(new Option(data[i].name, data[i].value), document.all ? i : null);
+
+			    }
+
+			} else {
+
+				$(target).get(0).add(new Option('- select a country -', ''), document.all ? i : null);
+
+			}
+
+
+
+			if (settings.defaultValue != null)
+
+			{
+
+				$(target).val(settings.defaultValue);//select default value
+
+			} else
+
+			{
+
+				$("option:first", target).attr( "selected", "selected" );//select first option
+
+			}
+
+
+
+			if (settings.after != null) 
+
+			{
+
+				settings.after(target);
+
+			}
+
+
+
+			$(target).change();//call next chain
+
+		};
+
+
+
+		if (settings.usePost == true)
+
+		{
+
+			$.post( url, settings.parameters, ajaxCallback );
+
+		} else
+
+		{
+
+			$.get( url, settings.parameters, ajaxCallback );
+
+		}
+
+	});
+
+  });
+
+};
+
+
+
+})(jQuery);

diff --git a/plugins/jquery.civicrm-validate.js b/plugins/jquery.civicrm-validate.js
new file mode 100644
index 0000000..bd770c2
--- /dev/null
+++ b/plugins/jquery.civicrm-validate.js
@@ -0,0 +1,8 @@
+/**
+ *  Additional methods to extend jQuery Validation Plugin 1.8.0
+ *  You can add validation methods here based on classes prefixed with crm_
+ *  Example: class='crm_phone' is assigned to phone number fields
+ *  To define phone validation for your site:
+ *  jQuery.validator.addMethod("crm_phone", function(phone_number, element) { validation logic here }
+ */
+
diff --git a/plugins/jquery.contextMenu.js b/plugins/jquery.contextMenu.js
new file mode 100644
index 0000000..29f6b59
--- /dev/null
+++ b/plugins/jquery.contextMenu.js
@@ -0,0 +1,427 @@
+// jQuery Context Menu Plugin
+
+//
+
+// Version 1.01
+
+//
+
+// Cory S.N. LaViska
+
+// A Beautiful Site (http://abeautifulsite.net/)
+
+//
+
+// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/
+
+//
+
+// Terms of Use
+
+//
+
+// This plugin is dual-licensed under the GNU General Public License
+
+//   and the MIT License and is copyright A Beautiful Site, LLC.
+
+//
+
+( function($) {
+
+	$.extend($.fn, {
+
+		
+
+		contextMenu: function(o, callback) {
+
+			// Defaults
+
+			if( o.menu == undefined ) return false;
+
+			if( o.inSpeed == undefined ) o.inSpeed = 150;
+
+			if( o.outSpeed == undefined ) o.outSpeed = 75;
+
+			// 0 needs to be -1 for expected results (no fade)
+
+			if( o.inSpeed == 0 ) o.inSpeed = -1;
+
+			if( o.outSpeed == 0 ) o.outSpeed = -1;
+
+			// Loop each context menu
+
+			$(this).each( function() {
+
+				var el = $(this);
+
+				var offset = $(el).offset();
+
+				// Add contextMenu class
+
+				$('#' + o.menu).addClass('contextMenu');
+
+				// Simulate a true right click
+
+				$(this).mousedown( function(e) {
+
+					var evt = e;
+
+					// move contextmenu elements to end of DOM so that they get positioned correctly
+
+                    $(".contextMenu").addClass('crm-contextmenu').remove().appendTo('body');
+
+
+
+                    evt.stopPropagation();
+
+					$(this).mouseup( function(e) {
+
+						e.stopPropagation();
+
+						var srcElement = $(this);
+
+						$(this).unbind('mouseup');
+
+						if( evt.button == 2 ) {
+
+							// Hide context menus that may be showing
+
+							$(".contextMenu").hide();
+
+							// Get this context menu
+
+							var menu = $('#' + o.menu);
+
+							
+
+							if( $(el).hasClass('disabled') ) return false;
+
+							
+
+							// Detect mouse position
+
+							var d = {}, x, y;
+
+							if( self.innerHeight ) {
+
+								d.pageYOffset = self.pageYOffset;
+
+								d.pageXOffset = self.pageXOffset;
+
+								d.innerHeight = self.innerHeight;
+
+								d.innerWidth = self.innerWidth;
+
+							} else if( document.documentElement &&
+
+								document.documentElement.clientHeight ) {
+
+								d.pageYOffset = document.documentElement.scrollTop;
+
+								d.pageXOffset = document.documentElement.scrollLeft;
+
+								d.innerHeight = document.documentElement.clientHeight;
+
+								d.innerWidth = document.documentElement.clientWidth;
+
+							} else if( document.body ) {
+
+								d.pageYOffset = document.body.scrollTop;
+
+								d.pageXOffset = document.body.scrollLeft;
+
+								d.innerHeight = document.body.clientHeight;
+
+								d.innerWidth = document.body.clientWidth;
+
+							}
+
+							(e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;
+
+							(e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop;
+
+							
+
+							// Show the menu
+
+							$(document).unbind('click');
+
+							$(menu).css({ top: y, left: x }).fadeIn(o.inSpeed);
+
+							// Hover events
+
+							$(menu).find('A').mouseover( function() {
+
+								$(menu).find('LI.hover').removeClass('hover');
+
+								$(this).parent().addClass('hover');
+
+							}).mouseout( function() {
+
+								$(menu).find('LI.hover').removeClass('hover');
+
+							});
+
+							
+
+							// Keyboard
+
+							$(document).keypress( function(e) {
+
+								switch( e.keyCode ) {
+
+									case 38: // up
+
+										if( $(menu).find('LI.hover').size() == 0 ) {
+
+											$(menu).find('LI:last').addClass('hover');
+
+										} else {
+
+											$(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover');
+
+											if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:last').addClass('hover');
+
+										}
+
+									break;
+
+									case 40: // down
+
+										if( $(menu).find('LI.hover').size() == 0 ) {
+
+											$(menu).find('LI:first').addClass('hover');
+
+										} else {
+
+											$(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover');
+
+											if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:first').addClass('hover');
+
+										}
+
+									break;
+
+									case 13: // enter
+
+										$(menu).find('LI.hover A').trigger('click');
+
+									break;
+
+									case 27: // esc
+
+										$(document).trigger('click');
+
+									break
+
+								}
+
+							});
+
+							
+
+							// When items are selected
+
+							$('#' + o.menu).find('A').unbind('click');
+
+							$('#' + o.menu).find('LI:not(.disabled) A').click( function() {
+
+								$(document).unbind('click').unbind('keypress');
+
+								$(".contextMenu").hide();
+
+								// Callback
+
+								if( callback ) callback( $(this).attr('href').substr(1), $(srcElement), {x: x - offset.left, y: y - offset.top, docX: x, docY: y} );
+
+								return false;
+
+							});
+
+							
+
+							// Hide bindings
+
+							setTimeout( function() { // Delay for Mozilla
+
+								$(document).click( function() {
+
+									$(document).unbind('click').unbind('keypress');
+
+									$(menu).fadeOut(o.outSpeed);
+
+									return false;
+
+								});
+
+							}, 0);
+
+						}
+
+					});
+
+				});
+
+				
+
+				// Disable text selection
+
+				if( $.browser.mozilla ) {
+
+					$('#' + o.menu).each( function() { $(this).css({ 'MozUserSelect' : 'none' }); });
+
+				} else if( $.browser.msie ) {
+
+					$('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); });
+
+				} else {
+
+					$('#' + o.menu).each(function() { $(this).bind('mousedown.disableTextSelect', function() { return false; }); });
+
+				}
+
+				// Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome)
+
+				$(el).add($('UL.contextMenu')).bind('contextmenu', function() { return false; });
+
+				
+
+			});
+
+			return $(this);
+
+		},
+
+		
+
+		// Disable context menu items on the fly
+
+		disableContextMenuItems: function(o) {
+
+			if( o == undefined ) {
+
+				// Disable all
+
+				$(this).find('LI').addClass('disabled');
+
+				return( $(this) );
+
+			}
+
+			$(this).each( function() {
+
+				if( o != undefined ) {
+
+					var d = o.split(',');
+
+					for( var i = 0; i < d.length; i++ ) {
+
+						$(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled');
+
+						
+
+					}
+
+				}
+
+			});
+
+			return( $(this) );
+
+		},
+
+		
+
+		// Enable context menu items on the fly
+
+		enableContextMenuItems: function(o) {
+
+			if( o == undefined ) {
+
+				// Enable all
+
+				$(this).find('LI.disabled').removeClass('disabled');
+
+				return( $(this) );
+
+			}
+
+			$(this).each( function() {
+
+				if( o != undefined ) {
+
+					var d = o.split(',');
+
+					for( var i = 0; i < d.length; i++ ) {
+
+						$(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled');
+
+						
+
+					}
+
+				}
+
+			});
+
+			return( $(this) );
+
+		},
+
+		
+
+		// Disable context menu(s)
+
+		disableContextMenu: function() {
+
+			$(this).each( function() {
+
+				$(this).addClass('disabled');
+
+			});
+
+			return( $(this) );
+
+		},
+
+		
+
+		// Enable context menu(s)
+
+		enableContextMenu: function() {
+
+			$(this).each( function() {
+
+				$(this).removeClass('disabled');
+
+			});
+
+			return( $(this) );
+
+		},
+
+		
+
+		// Destroy context menu(s)
+
+		destroyContextMenu: function() {
+
+			// Destroy specified context menus
+
+			$(this).each( function() {
+
+				// Disable action
+
+				$(this).unbind('mousedown').unbind('mouseup');
+
+			});
+
+			return( $(this) );
+
+		}
+
+		
+
+	});
+
+})(jQuery);

diff --git a/plugins/jquery.dashboard.js b/plugins/jquery.dashboard.js
new file mode 100644
index 0000000..55a09a4
--- /dev/null
+++ b/plugins/jquery.dashboard.js
@@ -0,0 +1,641 @@
+/**
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.2                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2012                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM 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 Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ *
+ * Copyright (C) 2009 Bevan Rudge
+ * Licensed to CiviCRM under the Academic Free License version 3.0.
+ *
+ * @file Defines the jQuery.dashboard() plugin.
+ *
+ * Uses jQuery 1.3, jQuery UI 1.6 and several jQuery UI extensions, most of all Sortable
+ *    http://visualjquery.com/
+ *    http://docs.jquery.com/UI/Sortable
+ *    http://ui.jquery.com/download
+ *      Sortable
+ *      Draggable
+ *      UI Core
+ *
+ */
+
+(function($) { // Create closure.
+  // Constructor for dashboard object.
+  $.fn.dashboard = function(options) {
+    // Public properties of dashboard.
+    var dashboard = {};
+    dashboard.element = this.empty();
+    dashboard.ready = false;
+    dashboard.columns = Array();
+    dashboard.widgets = Array();
+    // End of public properties of dashboard.
+
+    /**
+     * Public methods of dashboard.
+     */
+
+    // Saves the order of widgets for all columns including the widget.minimized status to options.ajaxCallbacks.saveColumns.
+    dashboard.saveColumns = function() {
+      // Update the display status of the empty placeholders.
+      for (var c in dashboard.columns) {
+        var col = dashboard.columns[c];
+       if ( typeof col == 'object' ) {
+			// Are there any visible children of the column (excluding the empty placeholder)?
+			if (col.element.children(':visible').not(col.emptyPlaceholder).length > 0) {
+			  col.emptyPlaceholder.hide();
+			}
+			else {
+			  col.emptyPlaceholder.show();
+			}
+		}
+	  }
+
+      // Don't save any changes to the server unless the dashboard has finished initiating.
+      if (!dashboard.ready) {
+        return;
+      }
+
+      // Build a list of params to post to the server.
+      var params = {};
+
+      // For each column...
+      for (var c in dashboard.columns) {
+
+        // IDs of the sortable elements in this column.
+        if( typeof dashboard.columns[c] == 'object' ) var ids = dashboard.columns[c].element.sortable('toArray');
+
+        // For each id...
+        for (var w in ids) {
+          // Chop 'widget-' off of the front so that we have the real widget id.
+          if( typeof ids[w] == 'string' ) var id = ids[w].substring('widget-'.length);
+
+          // Add one flat property to the params object that will look like an array element to the PHP server.
+          // Unfortunately jQuery doesn't do this for us.
+          if ( typeof dashboard.widgets[id] == 'object' ) params['columns[' + c + '][' + id + ']'] = (dashboard.widgets[id].minimized ? '1' : '0');
+        }
+      }
+
+      // The ajaxCallback settings overwrite any duplicate properties.
+      $.extend(params, opts.ajaxCallbacks.saveColumns.data);
+      $.post(opts.ajaxCallbacks.saveColumns.url, params, function(response, status) {
+        invokeCallback(opts.callbacks.saveColumns, dashboard);
+      });
+    };
+
+    // Puts the dashboard into full screen mode, saving element for when the user exits full-screen mode.
+    // Does not add element to the DOM – this is the caller's responsibility.
+    // Does show and hide element though.
+    dashboard.enterFullscreen = function(element) {
+      // Hide the columns.
+      for (var c in dashboard.columns) {
+       if ( typeof dashboard.columns[c] == 'object' ) dashboard.columns[c].element.hide();
+      }
+
+      if (!dashboard.fullscreen) {
+        // Initialize.
+        var markup = '<a id="full-screen-header" class="full-screen-close-icon">' + opts.fullscreenHeaderInner + '</a>';
+        dashboard.fullscreen = {
+          headerElement: $(markup).prependTo(dashboard.element).click(dashboard.exitFullscreen).hide()
+        };
+      }
+
+      dashboard.fullscreen.headerElement.slideDown();
+      dashboard.fullscreen.currentElement = element.show();
+      dashboard.fullscreen.displayed = true;
+      invokeCallback(opts.callbacks.enterFullscreen, dashboard, dashboard.fullscreen.currentElement);
+    };
+
+    // Takes the dashboard out of full screen mode, hiding the active fullscreen element.
+    dashboard.exitFullscreen = function() {
+      if (!dashboard.fullscreen.displayed) {
+        return;
+      }
+
+      dashboard.fullscreen.headerElement.slideUp();
+      dashboard.fullscreen.currentElement.hide();
+      dashboard.fullscreen.displayed = false;
+
+      // Show the columns.
+      for (var c in dashboard.columns) {
+          if ( typeof dashboard.columns[c] == 'object' ) dashboard.columns[c].element.show();
+      }
+
+      invokeCallback(opts.callbacks.exitFullscreen, dashboard, dashboard.fullscreen.currentElement);
+    };
+    // End of public methods of dashboard.
+
+    /**
+     * Private properties of dashboard.
+     */
+    
+    // Used to determine whether there are any incomplete ajax requests pending initialization of the dashboard.
+    var asynchronousRequestCounter = 0;
+
+    // Used to determine whether two resort events are resulting from the same UI event.
+    var currentReSortEvent = null;
+
+    // Merge in the caller's options with the defaults.
+    var opts = $.extend({}, $.fn.dashboard.defaults, options);
+
+    // Execution 'forks' here and restarts in init().  Tell the user we're busy with a throbber.
+    var throbber = $(opts.throbberMarkup).appendTo(dashboard.element);
+    $.getJSON(opts.ajaxCallbacks.getWidgetsByColumn.url, opts.ajaxCallbacks.getWidgetsByColumn.data, init);
+    asynchronousRequestCounter++;
+    return dashboard;
+    // End of constructor and private properties for dashboard object.
+
+    /**
+     * Private methods of dashboard.
+     */
+
+    // Ajax callback for getWidgetsByColumn.
+    function init(widgets, status) {
+      asynchronousRequestCounter--;
+      throbber.remove();
+      var markup = '<li class="empty-placeholder">' + opts.emptyPlaceholderInner + '</li>';
+
+      // Build the dashboard in the DOM.  For each column...
+      // (Don't iterate on widgets since this will break badly if the dataset has empty columns.)
+      var emptyDashboard = true;
+      for (var c = 0; c < opts.columns; c++) {
+          // Save the column to both the public scope for external accessibility and the local scope for readability.
+          var col = dashboard.columns[c] = {
+              initialWidgets: Array(),
+              element: $('<ul id="column-' + c + '" class="column column-' + c + '"></ul>').appendTo(dashboard.element)
+          };
+        
+          // Add the empty placeholder now, hide it and save it.
+          col.emptyPlaceholder = $(markup).appendTo(col.element).hide();
+
+          // For each widget in this column.
+          for (var id in widgets[c]) {
+              var widgetID = id.split('-');
+              // Build a new widget object and save it to various publicly accessible places.
+              col.initialWidgets[id] = dashboard.widgets[widgetID[1]] = widget({
+                  id: widgetID[1],
+                  element: $('<li class="widget"></li>').appendTo(col.element),
+                  initialColumn: col,
+                  minimized: ( widgets[c][widgetID[1]] > 0  ? true : false )
+              });
+          
+              //set empty Dashboard to false
+              emptyDashboard = false;
+          }
+      }
+
+      if ( emptyDashboard ) {
+          emptyDashboardCondition( );
+      }
+      
+      invokeCallback(opts.callbacks.init, dashboard);
+    }
+
+    // function that is called when dashboard is empty
+    function emptyDashboardCondition( ) {
+        cj(".show-refresh").hide( );
+        cj("#empty-message").show( );
+    }
+    
+    // Contructors for each widget call this when initialization has finished so that dashboard can complete it's intitialization.
+    function completeInit() {
+      // Don't do anything if any widgets are waiting for ajax requests to complete in order to finish initialization.
+      if (asynchronousRequestCounter > 0) {
+          return;
+      }
+
+      // Make widgets sortable across columns.
+      dashboard.sortableElement = $('.column').sortable({
+        connectWith: ['.column'],
+
+        // The class of the element by which widgets are draggable.
+        handle: '.widget-header',
+
+        // The class of placeholder elements (the 'ghost' widget showing where the dragged item would land if released now.)
+        placeholder: 'placeholder',   
+        activate: function(event, ui) { 
+		  var h= cj(ui.item).height(); 
+		  $('.placeholder').css('height', h +'px'); },
+         	    
+        opacity: 0.2,
+
+        // Maks sure that only widgets are sortable, and not empty placeholders.
+        items: '> .widget',
+        
+        forcePlaceholderSize: true,
+        
+        // Callback functions.
+        update: resorted,
+        start: hideEmptyPlaceholders
+      });
+
+      // Update empty placeholders.
+      dashboard.saveColumns();
+      dashboard.ready = true;
+      invokeCallback(opts.callbacks.ready, dashboard);
+    }
+
+    // Callback for when any list has changed (and the user has finished resorting).
+    function resorted(e, ui) {
+        // Only do anything if we haven't already handled resorts based on changes from this UI DOM event.
+        // (resorted() gets invoked once for each list when an item is moved from one to another.)
+        if (!currentReSortEvent || e.originalEvent != currentReSortEvent) {
+            currentReSortEvent = e.originalEvent;
+            dashboard.saveColumns();
+        }
+    }
+
+    // Callback for when a user starts resorting a list.  Hides all the empty placeholders.
+    function hideEmptyPlaceholders(e, ui) {
+        for (var c in dashboard.columns) {
+            if( typeof dashboard.columns[c] == 'object ' ) dashboard.columns[c].emptyPlaceholder.hide();
+        }
+    }
+
+    // @todo use an event library to register, bind to and invoke events.
+    //  @param callback is a function.
+    //  @param theThis is the context given to that function when it executes.  It becomes 'this' inside of that function.
+    function invokeCallback(callback, theThis, parameterOne) {
+        if (callback) {
+            callback.call(theThis, parameterOne);
+        }
+    }
+
+    /**
+     * widget object
+     *    Private sub-class of dashboard
+     * Constructor starts
+     */
+    function widget(widget) {
+      // Merge default options with the options defined for this widget.
+      widget = $.extend({}, $.fn.dashboard.widget.defaults, widget);
+
+      /**
+       * Public methods of widget.
+       */
+
+      // Toggles the minimize() & maximize() methods.
+      widget.toggleMinimize = function() {
+        if (widget.minimized) {
+          widget.maximize();
+        }
+        else {
+          widget.minimize();
+        }
+
+        widget.hideSettings();
+        dashboard.saveColumns();
+      };
+      widget.minimize = function() {
+        $('.widget-content', widget.element).slideUp(opts.animationSpeed);
+        $(widget.controls.minimize.element).addClass( 'maximize-icon' );
+        $(widget.controls.minimize.element).removeClass( 'minimize-icon' );
+        widget.minimized = true;
+      };
+      widget.maximize = function() {
+        $('.widget-content', widget.element).slideDown(opts.animationSpeed);
+        $(widget.controls.minimize.element).removeClass( 'maximize-icon' );
+        $(widget.controls.minimize.element).addClass( 'minimize-icon' );
+        widget.minimized = false;
+      };
+
+      // Toggles whether the widget is in settings-display mode or not.
+      widget.toggleSettings = function() {
+        if (widget.settings.displayed) {
+          // Widgets always exit settings into maximized state.
+          widget.maximize();
+          widget.hideSettings();
+          invokeCallback(opts.widgetCallbacks.hideSettings, widget);
+        }
+        else {
+          widget.minimize();
+          widget.showSettings();
+          invokeCallback(opts.widgetCallbacks.showSettings, widget);
+        }
+      };
+      widget.showSettings = function() {
+        if (widget.settings.element) {
+          widget.settings.element.show();
+
+          // Settings are loaded via AJAX.  Only execute the script if the settings have been loaded.
+          if (widget.settings.ready) {
+            getJavascript(widget.settings.script);
+          }
+        }
+        else {
+          // Settings have not been initialized.  Do so now.
+          initSettings();
+        }
+        widget.settings.displayed = true;
+      };
+      widget.hideSettings = function() {
+        if (widget.settings.element) {
+          widget.settings.element.hide();
+        }
+        widget.settings.displayed = false;
+      };
+      widget.saveSettings = function() {
+        // Build list of parameters to POST to server.
+        var params = {};
+        // serializeArray() returns an array of objects.  Process it.
+        var fields = widget.settings.element.serializeArray();
+        for (var i in fields) {
+            var field = fields[i];
+            // Put the values into flat object properties that PHP will parse into an array server-side.
+            // (Unfortunately jQuery doesn't do this)
+            params['settings[' + field.name + ']'] = field.value;
+        }
+
+        // Things get messy here.
+        // @todo Refactor to use currentState and targetedState properties to determine what needs 
+        // to be done to get to any desired state on any UI or AJAX event – since these don't always 
+        // match.  
+        // E.g.  When a user starts a new UI event before the Ajax event handler from a previous 
+        // UI event gets invoked.
+
+        // Hide the settings first of all.
+        widget.toggleSettings();
+        // Save the real settings element so that we can restore the reference later.
+        var settingsElement = widget.settings.element;
+        // Empty the settings form.
+        widget.settings.innerElement.empty();
+        initThrobber();
+        // So that showSettings() and hideSettings() can do SOMETHING, without showing the empty settings form.
+        widget.settings.element = widget.throbber.hide();
+        widget.settings.ready = false;
+
+        // Save the settings to the server.
+        $.extend(params, opts.ajaxCallbacks.widgetSettings.data, { id: widget.id });
+        $.post(opts.ajaxCallbacks.widgetSettings.url, params, function(response, status) {
+          // Merge the response into widget.settings.
+          $.extend(widget.settings, response);
+          // Restore the reference to the real settings element.
+          widget.settings.element = settingsElement;
+          // Make sure the settings form is empty and add the updated settings form.
+          widget.settings.innerElement.empty().append(widget.settings.markup);
+          widget.settings.ready = true;
+
+          // Did the user already jump back into settings-display mode before we could finish reloading the settings form?
+          if (widget.settings.displayed) {
+            // Ooops!  We had better take care of hiding the throbber and showing the settings form then.
+            widget.throbber.hide();
+            widget.showSettings();
+            invokeCallback(opts.widgetCallbacks.saveSettings, dashboard);
+          }
+        }, 'json');
+
+        // Don't let form submittal bubble up.
+        return false;
+      };
+
+      widget.enterFullscreen = function() {
+        // Make sure the widget actually supports full screen mode.
+        if (!widget.fullscreenUrl) {
+          return;
+        }
+
+        $('<div id="crm-dashlet-container"></div>')
+            .html('<div id="crm-container"><div id="crm-dashlet-fullscreen-content">Loading...</div></div>')
+            .dialog({
+                autoOpen: true,
+                title: widget.title,
+                modal: true,
+                height: 'auto',
+                width: 'auto',
+                position: [100,125],
+                close: function(event, ui) {
+                    cj(this).dialog("destroy");
+                    $('#crm-dashlet-container').remove();
+                    $('#crm-dashlet-fullscreen-content').remove();
+                }
+            });
+
+        $.ajax({
+            url: widget.fullscreenUrl,
+            success: function ( content ) {
+                $('#crm-dashlet-fullscreen-content').html( content );
+            }
+        }); 
+      };
+      
+      // Exit fullscreen mode.
+      widget.exitFullscreen = function() {
+        // This is just a wrapper for dashboard.exitFullscreen() which does the heavy lifting.
+        dashboard.exitFullscreen();
+      };
+
+      // Adds controls to a widget.  id is for internal use and image file name in images/dashboard/ (a .gif).
+      widget.addControl = function(id, control) {
+          var markup = '<a class="widget-icon ' + id + '-icon" alt="' + control.description + '" title="' + control.description + '"></a>';    
+          control.element = $(markup).prependTo($('.widget-controls', widget.element)).click(control.callback);
+      };
+
+      // An external method used only by and from external scripts to reload content.  Not invoked or used internally.
+      // The widget must provide the script that executes this, as well as the script that invokes it.
+      widget.reloadContent = function() {
+          getJavascript(widget.reloadContentScript);
+          invokeCallback(opts.widgetCallbacks.reloadContent, widget);
+      };
+
+      // Removes the widget from the dashboard, and saves columns.
+      widget.remove = function() {
+          if ( confirm( 'Are you sure you want to remove "' + widget.title + '"?') ) {  
+              invokeCallback(opts.widgetCallbacks.remove, widget);
+              widget.element.fadeOut(opts.animationSpeed, function() {
+                  $(this).remove();
+                  dashboard.saveColumns();
+              });
+          }
+      };
+      // End public methods of widget.
+
+      /**
+       * Public properties of widget.
+       */
+
+      // Default controls.  External script can add more with widget.addControls()
+      widget.controls = {
+        settings: {
+          description: 'Configure this dashlet',
+          callback: widget.toggleSettings
+        },
+        minimize: {
+          description: 'Collapse or expand this dashlet',
+          callback: widget.toggleMinimize
+        },
+        fullscreen: {
+          description: 'View this dashlet in full screen mode',
+          callback: widget.enterFullscreen
+        },
+        close: {
+          description: 'Remove this dashlet from your dashboard',
+          callback: widget.remove
+        }
+      };
+      // End public properties of widget.
+
+      /**
+       * Private properties of widget.
+       */
+
+      // We're gonna 'fork' execution again, so let's tell the user to hold with us till the AJAX callback gets invoked.
+      var throbber = $(opts.throbberMarkup).appendTo(widget.element);
+      var params = $.extend({}, opts.ajaxCallbacks.getWidget.data, {id: widget.id});
+      $.getJSON(opts.ajaxCallbacks.getWidget.url, params, init);
+
+      // Help dashboard track whether we've got any outstanding requests on which initialization is pending.
+      asynchronousRequestCounter++;
+      return widget;
+      // End of private properties of widget.
+
+      /**
+       * Private methods of widget.
+       */
+
+      // Ajax callback for widget initialization.
+      function init(data, status) {
+        asynchronousRequestCounter--;
+        $.extend(widget, data);
+
+        // Delete controls that don't apply to this widget.
+        if (!widget.settings) {
+          delete widget.controls.settings;
+        }
+        if (!widget.fullscreenUrl) {
+          delete widget.controls.fullscreen;
+        }
+
+        widget.element.attr('id', 'widget-' + widget.id).addClass(widget.classes);
+        throbber.remove();
+        // Build and add the widget's DOM element.
+        $(widgetHTML()).appendTo(widget.element);
+        // Save the content element so that external scripts can reload it easily.
+        widget.contentElement = $('.widget-content', widget.element);
+        $.each(widget.controls, widget.addControl);
+
+        // Switch the initial state so that it initializes to the correct state.
+        widget.minimized = !widget.minimized;
+        widget.toggleMinimize();
+        getJavascript(widget.initScript);
+        invokeCallback(opts.widgetCallbacks.get, widget);
+
+        // completeInit() is a private method of the dashboard.  Let it complete initialization of the dashboard.
+        completeInit();
+      }
+
+      // Builds inner HTML for widgets.
+      function widgetHTML() {
+        var html = '';
+        html += '<div class="widget-wrapper">';
+        html += '  <div class="widget-controls"><h3 class="widget-header">' + widget.title + '</h3></div>';
+        html += '  <div class="widget-content">' + widget.content + '</div>';
+        html += '</div>';
+        return html;
+      }
+
+      // Initializes a widgets settings pane.
+      function initSettings() {
+        // Overwrite widget.settings (boolean).
+        initThrobber();
+        widget.settings = {
+          element: widget.throbber.show(),
+          ready: false
+        };
+
+        // Get the settings markup and script executables for this widget.
+        var params = $.extend({}, opts.ajaxCallbacks.widgetSettings.data, { id: widget.id });
+        $.getJSON(opts.ajaxCallbacks.widgetSettings.url, params, function(response, status) {
+          $.extend(widget.settings, response);
+          // Build and add the settings form to the DOM.  Bind the form's submit event handler/callback.
+          widget.settings.element = $(widgetSettingsHTML()).appendTo($('.widget-wrapper', widget.element)).submit(widget.saveSettings);
+          // Bind the cancel button's event handler too.
+          widget.settings.cancelButton = $('.widget-settings-cancel', widget.settings.element).click(cancelEditSettings);
+          // Build and add the inner form elements from the HTML markup provided in the AJAX data.
+          widget.settings.innerElement = $('.widget-settings-inner', widget.settings.element).append(widget.settings.markup);
+          widget.settings.ready = true;
+
+          if (widget.settings.displayed) {
+            // If the user hasn't clicked away from the settings pane, then display the form.
+            widget.throbber.hide();
+            widget.showSettings();
+          }
+
+          getJavascript(widget.settings.initScript);
+        });
+      }
+
+      // Builds HTML for widget settings forms.
+      function widgetSettingsHTML() {
+        var html = '';
+        html += '<form class="widget-settings">';
+        html += '  <div class="widget-settings-inner"></div>';
+        html += '  <div class="widget-settings-buttons">';
+        html += '    <input id="' + widget.id + '-settings-save" class="widget-settings-save" value="Save" type="submit" />';
+        html += '    <input id="' + widget.id + '-settings-cancel" class="widget-settings-cancel" value="Cancel" type="submit" />';
+        html += '  </div>';
+        html += '</form>';
+        return html;
+      }
+
+      // Initializes a generic widget content throbber, for use by settings form and external scripts.
+      function initThrobber() {
+        if (!widget.throbber) {
+          widget.throbber = $(opts.throbberMarkup).appendTo($('.widget-wrapper', widget.element));
+        }
+      };
+
+      // Event handler/callback for cancel button clicks.
+      // @todo test this gets caught by all browsers when the cancel button is 'clicked' via the keyboard.
+      function cancelEditSettings() {
+        widget.toggleSettings();
+        return false;
+      };
+
+      // Helper function to execute external script on the server.
+      // @todo It would be nice to provide some context to the script.  How?
+      function getJavascript(url) {
+        if (url) {
+          $.getScript(url);
+        }
+      }
+    };
+  };
+
+  // Public static properties of dashboard.  Default settings.
+  $.fn.dashboard.defaults = {
+    columns: 2,
+    emptyPlaceholderInner: 'There are no dashlets in this column of your dashboard.',
+    fullscreenHeaderInner: 'Back to dashboard mode',
+    throbberMarkup: '<div class="crm-loading-element">Loading...</div>',
+    animationSpeed: 200,
+    callbacks: {},
+    widgetCallbacks: {}
+  };
+
+  // Default widget settings.
+  $.fn.dashboard.widget = {
+    defaults: {
+      minimized: false,
+      settings: false,
+      fullscreen: false
+    }
+  };
+})(jQuery); // end of closure
diff --git a/plugins/jquery.fixedPosition.js b/plugins/jquery.fixedPosition.js
new file mode 100644
index 0000000..aa7d010
--- /dev/null
+++ b/plugins/jquery.fixedPosition.js
@@ -0,0 +1,57 @@
+/**
+ *  jQuery Fixed Position Plugin
+ *  @requires jQuery v1.3
+ *  http://www.socialembedded.com/labs
+ *
+ *  Copyright (c)  Hernan Amiune (hernan.amiune.com)
+ *  Dual licensed under the MIT and GPL licenses:
+ *  http://www.opensource.org/licenses/mit-license.php
+ *  http://www.gnu.org/licenses/gpl.html
+ * 
+ *  Version: 1.0
+ */
+ 
+(function($){ $.fn.fixedPosition = function(options){
+
+    var defaults = {
+	  vpos: null,    // posible values: "top", "middle", "bottom". if it is null the original position is taken
+	  hpos: null     // posible values: "left", "center", "right". if it is null the original position is taken
+	};
+	
+    var options = $.extend(defaults, options);
+	
+	return this.each(function(index) {
+		
+		
+		var $this = $(this);
+		$this.css("position","absolute");
+		
+		if(options.vpos === "top"){
+		    $this.css("top","0");
+		}
+		else if(options.vpos === "middle"){
+		    $this.css("top",((parseInt($(window).height())/2)-($(this).height()/2))+"px");
+		}
+		else if(options.vpos === "bottom"){
+		    $this.css("bottom","0");
+		}
+		
+		if(options.hpos === "left"){
+		    $this.css("left","0");
+		}
+		else if(options.hpos === "center"){
+		    $this.css("left",((parseInt($(window).width())/2)-($(this).width()/2))+"px");
+		}
+		else if(options.hpos === "right"){
+		    $this.css("right","0");
+		}
+		
+		var top = parseInt($this.offset().top);
+		var left = parseInt($this.offset().left);
+		$(window).scroll(function () {
+			$this.css("top",top+$(document).scrollTop()).css("left",left+$(document).scrollLeft());
+		});
+	});
+
+  
+}})(jQuery);
+

diff --git a/plugins/jquery.form.js b/plugins/jquery.form.js
new file mode 100644
index 0000000..8a7a67f
--- /dev/null
+++ b/plugins/jquery.form.js
@@ -0,0 +1,1118 @@
+/*!
+ * jQuery Form Plugin
+ * version: 3.18 (28-SEP-2012)
+ * @requires jQuery v1.5 or later
+ *
+ * Examples and documentation at: http://malsup.com/jquery/form/
+ * Project repository: https://github.com/malsup/form
+ * Dual licensed under the MIT and GPL licenses:
+ *    http://malsup.github.com/mit-license.txt
+ *    http://malsup.github.com/gpl-license-v2.txt
+ */
+/*global ActiveXObject alert */
+;(function($) {
+"use strict";
+
+/*
+    Usage Note:
+    -----------
+    Do not use both ajaxSubmit and ajaxForm on the same form.  These
+    functions are mutually exclusive.  Use ajaxSubmit if you want
+    to bind your own submit handler to the form.  For example,
+
+    $(document).ready(function() {
+        $('#myForm').on('submit', function(e) {
+            e.preventDefault(); // <-- important
+            $(this).ajaxSubmit({
+                target: '#output'
+            });
+        });
+    });
+
+    Use ajaxForm when you want the plugin to manage all the event binding
+    for you.  For example,
+
+    $(document).ready(function() {
+        $('#myForm').ajaxForm({
+            target: '#output'
+        });
+    });
+    
+    You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
+    form does not have to exist when you invoke ajaxForm:
+
+    $('#myForm').ajaxForm({
+        delegation: true,
+        target: '#output'
+    });
+    
+    When using ajaxForm, the ajaxSubmit function will be invoked for you
+    at the appropriate time.
+*/
+
+/**
+ * Feature detection
+ */
+var feature = {};
+feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
+feature.formdata = window.FormData !== undefined;
+
+/**
+ * ajaxSubmit() provides a mechanism for immediately submitting
+ * an HTML form using AJAX.
+ */
+$.fn.ajaxSubmit = function(options) {
+    /*jshint scripturl:true */
+
+    // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
+    if (!this.length) {
+        log('ajaxSubmit: skipping submit process - no element selected');
+        return this;
+    }
+    
+    var method, action, url, $form = this;
+
+    if (typeof options == 'function') {
+        options = { success: options };
+    }
+
+    method = this.attr('method');
+    action = this.attr('action');
+    url = (typeof action === 'string') ? $.trim(action) : '';
+    url = url || window.location.href || '';
+    if (url) {
+        // clean url (don't include hash vaue)
+        url = (url.match(/^([^#]+)/)||[])[1];
+    }
+
+    options = $.extend(true, {
+        url:  url,
+        success: $.ajaxSettings.success,
+        type: method || 'GET',
+        iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
+    }, options);
+
+    // hook for manipulating the form data before it is extracted;
+    // convenient for use with rich editors like tinyMCE or FCKEditor
+    var veto = {};
+    this.trigger('form-pre-serialize', [this, options, veto]);
+    if (veto.veto) {
+        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
+        return this;
+    }
+
+    // provide opportunity to alter form data before it is serialized
+    if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
+        log('ajaxSubmit: submit aborted via beforeSerialize callback');
+        return this;
+    }
+
+    var traditional = options.traditional;
+    if ( traditional === undefined ) {
+        traditional = $.ajaxSettings.traditional;
+    }
+    
+    var elements = [];
+    var qx, a = this.formToArray(options.semantic, elements);
+    if (options.data) {
+        options.extraData = options.data;
+        qx = $.param(options.data, traditional);
+    }
+
+    // give pre-submit callback an opportunity to abort the submit
+    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
+        log('ajaxSubmit: submit aborted via beforeSubmit callback');
+        return this;
+    }
+
+    // fire vetoable 'validate' event
+    this.trigger('form-submit-validate', [a, this, options, veto]);
+    if (veto.veto) {
+        log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
+        return this;
+    }
+
+    var q = $.param(a, traditional);
+    if (qx) {
+        q = ( q ? (q + '&' + qx) : qx );
+    }    
+    if (options.type.toUpperCase() == 'GET') {
+        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
+        options.data = null;  // data is null for 'get'
+    }
+    else {
+        options.data = q; // data is the query string for 'post'
+    }
+
+    var callbacks = [];
+    if (options.resetForm) {
+        callbacks.push(function() { $form.resetForm(); });
+    }
+    if (options.clearForm) {
+        callbacks.push(function() { $form.clearForm(options.includeHidden); });
+    }
+
+    // perform a load on the target only if dataType is not provided
+    if (!options.dataType && options.target) {
+        var oldSuccess = options.success || function(){};
+        callbacks.push(function(data) {
+            var fn = options.replaceTarget ? 'replaceWith' : 'html';
+            $(options.target)[fn](data).each(oldSuccess, arguments);
+        });
+    }
+    else if (options.success) {
+        callbacks.push(options.success);
+    }
+
+    options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
+        var context = options.context || this ;    // jQuery 1.4+ supports scope context 
+        for (var i=0, max=callbacks.length; i < max; i++) {
+            callbacks[i].apply(context, [data, status, xhr || $form, $form]);
+        }
+    };
+
+    // are there files to upload?
+    var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113)
+    var hasFileInputs = fileInputs.length > 0;
+    var mp = 'multipart/form-data';
+    var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
+
+    var fileAPI = feature.fileapi && feature.formdata;
+    log("fileAPI :" + fileAPI);
+    var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
+
+    var jqxhr;
+
+    // options.iframe allows user to force iframe mode
+    // 06-NOV-09: now defaulting to iframe mode if file input is detected
+    if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
+        // hack to fix Safari hang (thanks to Tim Molendijk for this)
+        // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
+        if (options.closeKeepAlive) {
+            $.get(options.closeKeepAlive, function() {
+                jqxhr = fileUploadIframe(a);
+            });
+        }
+        else {
+            jqxhr = fileUploadIframe(a);
+        }
+
+    }
+    else if ((hasFileInputs || multipart) && fileAPI) {
+        jqxhr = fileUploadXhr(a);
+    }
+    else {
+        jqxhr = $.ajax(options);
+    }
+
+    $form.removeData('jqxhr').data('jqxhr', jqxhr);
+
+    // clear element array
+    for (var k=0; k < elements.length; k++)
+        elements[k] = null;
+
+    // fire 'notify' event
+    this.trigger('form-submit-notify', [this, options]);
+    return this;
+
+    // utility fn for deep serialization
+    function deepSerialize(extraData){
+        var serialized = $.param(extraData).split('&');
+        var len = serialized.length;
+        var result = {};
+        var i, part;
+        for (i=0; i < len; i++) {
+            part = serialized[i].split('=');
+            result[decodeURIComponent(part[0])] = decodeURIComponent(part[1]);
+        }
+        return result;
+    }
+
+     // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
+    function fileUploadXhr(a) {
+        var formdata = new FormData();
+
+        for (var i=0; i < a.length; i++) {
+            formdata.append(a[i].name, a[i].value);
+        }
+
+        if (options.extraData) {
+            var serializedData = deepSerialize(options.extraData);
+            for (var p in serializedData)
+                if (serializedData.hasOwnProperty(p))
+                    formdata.append(p, serializedData[p]);
+        }
+
+        options.data = null;
+
+        var s = $.extend(true, {}, $.ajaxSettings, options, {
+            contentType: false,
+            processData: false,
+            cache: false,
+            type: method || 'POST'
+        });
+        
+        if (options.uploadProgress) {
+            // workaround because jqXHR does not expose upload property
+            s.xhr = function() {
+                var xhr = jQuery.ajaxSettings.xhr();
+                if (xhr.upload) {
+                    xhr.upload.onprogress = function(event) {
+                        var percent = 0;
+                        var position = event.loaded || event.position; /*event.position is deprecated*/
+                        var total = event.total;
+                        if (event.lengthComputable) {
+                            percent = Math.ceil(position / total * 100);
+                        }
+                        options.uploadProgress(event, position, total, percent);
+                    };
+                }
+                return xhr;
+            };
+        }
+
+        s.data = null;
+            var beforeSend = s.beforeSend;
+            s.beforeSend = function(xhr, o) {
+                o.data = formdata;
+                if(beforeSend)
+                    beforeSend.call(this, xhr, o);
+        };
+        return $.ajax(s);
+    }
+
+    // private function for handling file uploads (hat tip to YAHOO!)
+    function fileUploadIframe(a) {
+        var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
+        var useProp = !!$.fn.prop;
+        var deferred = $.Deferred();
+
+        if ($(':input[name=submit],:input[id=submit]', form).length) {
+            // if there is an input with a name or id of 'submit' then we won't be
+            // able to invoke the submit fn on the form (at least not x-browser)
+            alert('Error: Form elements must not have name or id of "submit".');
+            deferred.reject();
+            return deferred;
+        }
+        
+        if (a) {
+            // ensure that every serialized input is still enabled
+            for (i=0; i < elements.length; i++) {
+                el = $(elements[i]);
+                if ( useProp )
+                    el.prop('disabled', false);
+                else
+                    el.removeAttr('disabled');
+            }
+        }
+
+        s = $.extend(true, {}, $.ajaxSettings, options);
+        s.context = s.context || s;
+        id = 'jqFormIO' + (new Date().getTime());
+        if (s.iframeTarget) {
+            $io = $(s.iframeTarget);
+            n = $io.attr('name');
+            if (!n)
+                 $io.attr('name', id);
+            else
+                id = n;
+        }
+        else {
+            $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
+            $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
+        }
+        io = $io[0];
+
+
+        xhr = { // mock object
+            aborted: 0,
+            responseText: null,
+            responseXML: null,
+            status: 0,
+            statusText: 'n/a',
+            getAllResponseHeaders: function() {},
+            getResponseHeader: function() {},
+            setRequestHeader: function() {},
+            abort: function(status) {
+                var e = (status === 'timeout' ? 'timeout' : 'aborted');
+                log('aborting upload... ' + e);
+                this.aborted = 1;
+                // #214
+                if (io.contentWindow.document.execCommand) {
+                    try { // #214
+                        io.contentWindow.document.execCommand('Stop');
+                    } catch(ignore) {}
+                }
+                $io.attr('src', s.iframeSrc); // abort op in progress
+                xhr.error = e;
+                if (s.error)
+                    s.error.call(s.context, xhr, e, status);
+                if (g)
+                    $.event.trigger("ajaxError", [xhr, s, e]);
+                if (s.complete)
+                    s.complete.call(s.context, xhr, e);
+            }
+        };
+
+        g = s.global;
+        // trigger ajax global events so that activity/block indicators work like normal
+        if (g && 0 === $.active++) {
+            $.event.trigger("ajaxStart");
+        }
+        if (g) {
+            $.event.trigger("ajaxSend", [xhr, s]);
+        }
+
+        if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
+            if (s.global) {
+                $.active--;
+            }
+            deferred.reject();
+            return deferred;
+        }
+        if (xhr.aborted) {
+            deferred.reject();
+            return deferred;
+        }
+
+        // add submitting element to data if we know it
+        sub = form.clk;
+        if (sub) {
+            n = sub.name;
+            if (n && !sub.disabled) {
+                s.extraData = s.extraData || {};
+                s.extraData[n] = sub.value;
+                if (sub.type == "image") {
+                    s.extraData[n+'.x'] = form.clk_x;
+                    s.extraData[n+'.y'] = form.clk_y;
+                }
+            }
+        }
+        
+        var CLIENT_TIMEOUT_ABORT = 1;
+        var SERVER_ABORT = 2;
+
+        function getDoc(frame) {
+            var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
+            return doc;
+        }
+        
+        // Rails CSRF hack (thanks to Yvan Barthelemy)
+        var csrf_token = $('meta[name=csrf-token]').attr('content');
+        var csrf_param = $('meta[name=csrf-param]').attr('content');
+        if (csrf_param && csrf_token) {
+            s.extraData = s.extraData || {};
+            s.extraData[csrf_param] = csrf_token;
+        }
+
+        // take a breath so that pending repaints get some cpu time before the upload starts
+        function doSubmit() {
+            // make sure form attrs are set
+            var t = $form.attr('target'), a = $form.attr('action');
+
+            // update form attrs in IE friendly way
+            form.setAttribute('target',id);
+            if (!method) {
+                form.setAttribute('method', 'POST');
+            }
+            if (a != s.url) {
+                form.setAttribute('action', s.url);
+            }
+
+            // ie borks in some cases when setting encoding
+            if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
+                $form.attr({
+                    encoding: 'multipart/form-data',
+                    enctype:  'multipart/form-data'
+                });
+            }
+
+            // support timout
+            if (s.timeout) {
+                timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
+            }
+            
+            // look for server aborts
+            function checkState() {
+                try {
+                    var state = getDoc(io).readyState;
+                    log('state = ' + state);
+                    if (state && state.toLowerCase() == 'uninitialized')
+                        setTimeout(checkState,50);
+                }
+                catch(e) {
+                    log('Server abort: ' , e, ' (', e.name, ')');
+                    cb(SERVER_ABORT);
+                    if (timeoutHandle)
+                        clearTimeout(timeoutHandle);
+                    timeoutHandle = undefined;
+                }
+            }
+
+            // add "extra" data to form if provided in options
+            var extraInputs = [];
+            try {
+                if (s.extraData) {
+                    for (var n in s.extraData) {
+                        if (s.extraData.hasOwnProperty(n)) {
+                           // if using the $.param format that allows for multiple values with the same name
+                           if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
+                               extraInputs.push(
+                               $('<input type="hidden" name="'+s.extraData[n].name+'">').attr('value',s.extraData[n].value)
+                                   .appendTo(form)[0]);
+                           } else {
+                               extraInputs.push(
+                               $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n])
+                                   .appendTo(form)[0]);
+                           }
+                        }
+                    }
+                }
+
+                if (!s.iframeTarget) {
+                    // add iframe to doc and submit the form
+                    $io.appendTo('body');
+                    if (io.attachEvent)
+                        io.attachEvent('onload', cb);
+                    else
+                        io.addEventListener('load', cb, false);
+                }
+                setTimeout(checkState,15);
+                form.submit();
+            }
+            finally {
+                // reset attrs and remove "extra" input elements
+                form.setAttribute('action',a);
+                if(t) {
+                    form.setAttribute('target', t);
+                } else {
+                    $form.removeAttr('target');
+                }
+                $(extraInputs).remove();
+            }
+        }
+
+        if (s.forceSync) {
+            doSubmit();
+        }
+        else {
+            setTimeout(doSubmit, 10); // this lets dom updates render
+        }
+
+        var data, doc, domCheckCount = 50, callbackProcessed;
+
+        function cb(e) {
+            if (xhr.aborted || callbackProcessed) {
+                return;
+            }
+            try {
+                doc = getDoc(io);
+            }
+            catch(ex) {
+                log('cannot access response document: ', ex);
+                e = SERVER_ABORT;
+            }
+            if (e === CLIENT_TIMEOUT_ABORT && xhr) {
+                xhr.abort('timeout');
+                deferred.reject(xhr, 'timeout');
+                return;
+            }
+            else if (e == SERVER_ABORT && xhr) {
+                xhr.abort('server abort');
+                deferred.reject(xhr, 'error', 'server abort');
+                return;
+            }
+
+            if (!doc || doc.location.href == s.iframeSrc) {
+                // response not received yet
+                if (!timedOut)
+                    return;
+            }
+            if (io.detachEvent)
+                io.detachEvent('onload', cb);
+            else    
+                io.removeEventListener('load', cb, false);
+
+            var status = 'success', errMsg;
+            try {
+                if (timedOut) {
+                    throw 'timeout';
+                }
+
+                var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
+                log('isXml='+isXml);
+                if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
+                    if (--domCheckCount) {
+                        // in some browsers (Opera) the iframe DOM is not always traversable when
+                        // the onload callback fires, so we loop a bit to accommodate
+                        log('requeing onLoad callback, DOM not available');
+                        setTimeout(cb, 250);
+                        return;
+                    }
+                    // let this fall through because server response could be an empty document
+                    //log('Could not access iframe DOM after mutiple tries.');
+                    //throw 'DOMException: not available';
+                }
+
+                //log('response detected');
+                var docRoot = doc.body ? doc.body : doc.documentElement;
+                xhr.responseText = docRoot ? docRoot.innerHTML : null;
+                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
+                if (isXml)
+                    s.dataType = 'xml';
+                xhr.getResponseHeader = function(header){
+                    var headers = {'content-type': s.dataType};
+                    return headers[header];
+                };
+                // support for XHR 'status' & 'statusText' emulation :
+                if (docRoot) {
+                    xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
+                    xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
+                }
+
+                var dt = (s.dataType || '').toLowerCase();
+                var scr = /(json|script|text)/.test(dt);
+                if (scr || s.textarea) {
+                    // see if user embedded response in textarea
+                    var ta = doc.getElementsByTagName('textarea')[0];
+                    if (ta) {
+                        xhr.responseText = ta.value;
+                        // support for XHR 'status' & 'statusText' emulation :
+                        xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
+                        xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
+                    }
+                    else if (scr) {
+                        // account for browsers injecting pre around json response
+                        var pre = doc.getElementsByTagName('pre')[0];
+                        var b = doc.getElementsByTagName('body')[0];
+                        if (pre) {
+                            xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
+                        }
+                        else if (b) {
+                            xhr.responseText = b.textContent ? b.textContent : b.innerText;
+                        }
+                    }
+                }
+                else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
+                    xhr.responseXML = toXml(xhr.responseText);
+                }
+
+                try {
+                    data = httpData(xhr, dt, s);
+                }
+                catch (e) {
+                    status = 'parsererror';
+                    xhr.error = errMsg = (e || status);
+                }
+            }
+            catch (e) {
+                log('error caught: ',e);
+                status = 'error';
+                xhr.error = errMsg = (e || status);
+            }
+
+            if (xhr.aborted) {
+                log('upload aborted');
+                status = null;
+            }
+
+            if (xhr.status) { // we've set xhr.status
+                status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
+            }
+
+            // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
+            if (status === 'success') {
+                if (s.success)
+                    s.success.call(s.context, data, 'success', xhr);
+                deferred.resolve(xhr.responseText, 'success', xhr);
+                if (g)
+                    $.event.trigger("ajaxSuccess", [xhr, s]);
+            }
+            else if (status) {
+                if (errMsg === undefined)
+                    errMsg = xhr.statusText;
+                if (s.error)
+                    s.error.call(s.context, xhr, status, errMsg);
+                deferred.reject(xhr, 'error', errMsg);
+                if (g)
+                    $.event.trigger("ajaxError", [xhr, s, errMsg]);
+            }
+
+            if (g)
+                $.event.trigger("ajaxComplete", [xhr, s]);
+
+            if (g && ! --$.active) {
+                $.event.trigger("ajaxStop");
+            }
+
+            if (s.complete)
+                s.complete.call(s.context, xhr, status);
+
+            callbackProcessed = true;
+            if (s.timeout)
+                clearTimeout(timeoutHandle);
+
+            // clean up
+            setTimeout(function() {
+                if (!s.iframeTarget)
+                    $io.remove();
+                xhr.responseXML = null;
+            }, 100);
+        }
+
+        var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
+            if (window.ActiveXObject) {
+                doc = new ActiveXObject('Microsoft.XMLDOM');
+                doc.async = 'false';
+                doc.loadXML(s);
+            }
+            else {
+                doc = (new DOMParser()).parseFromString(s, 'text/xml');
+            }
+            return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
+        };
+        var parseJSON = $.parseJSON || function(s) {
+            /*jslint evil:true */
+            return window['eval']('(' + s + ')');
+        };
+
+        var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
+
+            var ct = xhr.getResponseHeader('content-type') || '',
+                xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
+                data = xml ? xhr.responseXML : xhr.responseText;
+
+            if (xml && data.documentElement.nodeName === 'parsererror') {
+                if ($.error)
+                    $.error('parsererror');
+            }
+            if (s && s.dataFilter) {
+                data = s.dataFilter(data, type);
+            }
+            if (typeof data === 'string') {
+                if (type === 'json' || !type && ct.indexOf('json') >= 0) {
+                    data = parseJSON(data);
+                } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
+                    $.globalEval(data);
+                }
+            }
+            return data;
+        };
+
+        return deferred;
+    }
+};
+
+/**
+ * ajaxForm() provides a mechanism for fully automating form submission.
+ *
+ * The advantages of using this method instead of ajaxSubmit() are:
+ *
+ * 1: This method will include coordinates for <input type="image" /> elements (if the element
+ *    is used to submit the form).
+ * 2. This method will include the submit element's name/value data (for the element that was
+ *    used to submit the form).
+ * 3. This method binds the submit() method to the form for you.
+ *
+ * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
+ * passes the options argument along after properly binding events for submit elements and
+ * the form itself.
+ */
+$.fn.ajaxForm = function(options) {
+    options = options || {};
+    options.delegation = options.delegation && $.isFunction($.fn.on);
+    
+    // in jQuery 1.3+ we can fix mistakes with the ready state
+    if (!options.delegation && this.length === 0) {
+        var o = { s: this.selector, c: this.context };
+        if (!$.isReady && o.s) {
+            log('DOM not ready, queuing ajaxForm');
+            $(function() {
+                $(o.s,o.c).ajaxForm(options);
+            });
+            return this;
+        }
+        // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
+        log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
+        return this;
+    }
+
+    if ( options.delegation ) {
+        $(document)
+            .off('submit.form-plugin', this.selector, doAjaxSubmit)
+            .off('click.form-plugin', this.selector, captureSubmittingElement)
+            .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
+            .on('click.form-plugin', this.selector, options, captureSubmittingElement);
+        return this;
+    }
+
+    return this.ajaxFormUnbind()
+        .bind('submit.form-plugin', options, doAjaxSubmit)
+        .bind('click.form-plugin', options, captureSubmittingElement);
+};
+
+// private event handlers    
+function doAjaxSubmit(e) {
+    /*jshint validthis:true */
+    var options = e.data;
+    if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
+        e.preventDefault();
+        $(this).ajaxSubmit(options);
+    }
+}
+    
+function captureSubmittingElement(e) {
+    /*jshint validthis:true */
+    var target = e.target;
+    var $el = $(target);
+    if (!($el.is(":submit,input:image"))) {
+        // is this a child element of the submit el?  (ex: a span within a button)
+        var t = $el.closest(':submit');
+        if (t.length === 0) {
+            return;
+        }
+        target = t[0];
+    }
+    var form = this;
+    form.clk = target;
+    if (target.type == 'image') {
+        if (e.offsetX !== undefined) {
+            form.clk_x = e.offsetX;
+            form.clk_y = e.offsetY;
+        } else if (typeof $.fn.offset == 'function') {
+            var offset = $el.offset();
+            form.clk_x = e.pageX - offset.left;
+            form.clk_y = e.pageY - offset.top;
+        } else {
+            form.clk_x = e.pageX - target.offsetLeft;
+            form.clk_y = e.pageY - target.offsetTop;
+        }
+    }
+    // clear form vars
+    setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
+}
+
+
+// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
+$.fn.ajaxFormUnbind = function() {
+    return this.unbind('submit.form-plugin click.form-plugin');
+};
+
+/**
+ * formToArray() gathers form element data into an array of objects that can
+ * be passed to any of the following ajax functions: $.get, $.post, or load.
+ * Each object in the array has both a 'name' and 'value' property.  An example of
+ * an array for a simple login form might be:
+ *
+ * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
+ *
+ * It is this array that is passed to pre-submit callback functions provided to the
+ * ajaxSubmit() and ajaxForm() methods.
+ */
+$.fn.formToArray = function(semantic, elements) {
+    var a = [];
+    if (this.length === 0) {
+        return a;
+    }
+
+    var form = this[0];
+    var els = semantic ? form.getElementsByTagName('*') : form.elements;
+    if (!els) {
+        return a;
+    }
+
+    var i,j,n,v,el,max,jmax;
+    for(i=0, max=els.length; i < max; i++) {
+        el = els[i];
+        n = el.name;
+        if (!n) {
+            continue;
+        }
+
+        if (semantic && form.clk && el.type == "image") {
+            // handle image inputs on the fly when semantic == true
+            if(!el.disabled && form.clk == el) {
+                a.push({name: n, value: $(el).val(), type: el.type });
+                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+            }
+            continue;
+        }
+
+        v = $.fieldValue(el, true);
+        if (v && v.constructor == Array) {
+            if (elements) 
+                elements.push(el);
+            for(j=0, jmax=v.length; j < jmax; j++) {
+                a.push({name: n, value: v[j]});
+            }
+        }
+        else if (feature.fileapi && el.type == 'file' && !el.disabled) {
+            if (elements) 
+                elements.push(el);
+            var files = el.files;
+            if (files.length) {
+                for (j=0; j < files.length; j++) {
+                    a.push({name: n, value: files[j], type: el.type});
+                }
+            }
+            else {
+                // #180
+                a.push({ name: n, value: '', type: el.type });
+            }
+        }
+        else if (v !== null && typeof v != 'undefined') {
+            if (elements) 
+                elements.push(el);
+            a.push({name: n, value: v, type: el.type, required: el.required});
+        }
+    }
+
+    if (!semantic && form.clk) {
+        // input type=='image' are not found in elements array! handle it here
+        var $input = $(form.clk), input = $input[0];
+        n = input.name;
+        if (n && !input.disabled && input.type == 'image') {
+            a.push({name: n, value: $input.val()});
+            a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+        }
+    }
+    return a;
+};
+
+/**
+ * Serializes form data into a 'submittable' string. This method will return a string
+ * in the format: name1=value1&amp;name2=value2
+ */
+$.fn.formSerialize = function(semantic) {
+    //hand off to jQuery.param for proper encoding
+    return $.param(this.formToArray(semantic));
+};
+
+/**
+ * Serializes all field elements in the jQuery object into a query string.
+ * This method will return a string in the format: name1=value1&amp;name2=value2
+ */
+$.fn.fieldSerialize = function(successful) {
+    var a = [];
+    this.each(function() {
+        var n = this.name;
+        if (!n) {
+            return;
+        }
+        var v = $.fieldValue(this, successful);
+        if (v && v.constructor == Array) {
+            for (var i=0,max=v.length; i < max; i++) {
+                a.push({name: n, value: v[i]});
+            }
+        }
+        else if (v !== null && typeof v != 'undefined') {
+            a.push({name: this.name, value: v});
+        }
+    });
+    //hand off to jQuery.param for proper encoding
+    return $.param(a);
+};
+
+/**
+ * Returns the value(s) of the element in the matched set.  For example, consider the following form:
+ *
+ *  <form><fieldset>
+ *      <input name="A" type="text" />
+ *      <input name="A" type="text" />
+ *      <input name="B" type="checkbox" value="B1" />
+ *      <input name="B" type="checkbox" value="B2"/>
+ *      <input name="C" type="radio" value="C1" />
+ *      <input name="C" type="radio" value="C2" />
+ *  </fieldset></form>
+ *
+ *  var v = $(':text').fieldValue();
+ *  // if no values are entered into the text inputs
+ *  v == ['','']
+ *  // if values entered into the text inputs are 'foo' and 'bar'
+ *  v == ['foo','bar']
+ *
+ *  var v = $(':checkbox').fieldValue();
+ *  // if neither checkbox is checked
+ *  v === undefined
+ *  // if both checkboxes are checked
+ *  v == ['B1', 'B2']
+ *
+ *  var v = $(':radio').fieldValue();
+ *  // if neither radio is checked
+ *  v === undefined
+ *  // if first radio is checked
+ *  v == ['C1']
+ *
+ * The successful argument controls whether or not the field element must be 'successful'
+ * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
+ * The default value of the successful argument is true.  If this value is false the value(s)
+ * for each element is returned.
+ *
+ * Note: This method *always* returns an array.  If no valid value can be determined the
+ *    array will be empty, otherwise it will contain one or more values.
+ */
+$.fn.fieldValue = function(successful) {
+    for (var val=[], i=0, max=this.length; i < max; i++) {
+        var el = this[i];
+        var v = $.fieldValue(el, successful);
+        if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
+            continue;
+        }
+        if (v.constructor == Array)
+            $.merge(val, v);
+        else
+            val.push(v);
+    }
+    return val;
+};
+
+/**
+ * Returns the value of the field element.
+ */
+$.fieldValue = function(el, successful) {
+    var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
+    if (successful === undefined) {
+        successful = true;
+    }
+
+    if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
+        (t == 'checkbox' || t == 'radio') && !el.checked ||
+        (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
+        tag == 'select' && el.selectedIndex == -1)) {
+            return null;
+    }
+
+    if (tag == 'select') {
+        var index = el.selectedIndex;
+        if (index < 0) {
+            return null;
+        }
+        var a = [], ops = el.options;
+        var one = (t == 'select-one');
+        var max = (one ? index+1 : ops.length);
+        for(var i=(one ? index : 0); i < max; i++) {
+            var op = ops[i];
+            if (op.selected) {
+                var v = op.value;
+                if (!v) { // extra pain for IE...
+                    v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
+                }
+                if (one) {
+                    return v;
+                }
+                a.push(v);
+            }
+        }
+        return a;
+    }
+    return $(el).val();
+};
+
+/**
+ * Clears the form data.  Takes the following actions on the form's input fields:
+ *  - input text fields will have their 'value' property set to the empty string
+ *  - select elements will have their 'selectedIndex' property set to -1
+ *  - checkbox and radio inputs will have their 'checked' property set to false
+ *  - inputs of type submit, button, reset, and hidden will *not* be effected
+ *  - button elements will *not* be effected
+ */
+$.fn.clearForm = function(includeHidden) {
+    return this.each(function() {
+        $('input,select,textarea', this).clearFields(includeHidden);
+    });
+};
+
+/**
+ * Clears the selected form elements.
+ */
+$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
+    var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
+    return this.each(function() {
+        var t = this.type, tag = this.tagName.toLowerCase();
+        if (re.test(t) || tag == 'textarea') {
+            this.value = '';
+        }
+        else if (t == 'checkbox' || t == 'radio') {
+            this.checked = false;
+        }
+        else if (tag == 'select') {
+            this.selectedIndex = -1;
+        }
+        else if (includeHidden) {
+            // includeHidden can be the value true, or it can be a selector string
+            // indicating a special test; for example:
+            //  $('#myForm').clearForm('.special:hidden')
+            // the above would clean hidden inputs that have the class of 'special'
+            if ( (includeHidden === true && /hidden/.test(t)) ||
+                 (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
+                this.value = '';
+        }
+    });
+};
+
+/**
+ * Resets the form data.  Causes all form elements to be reset to their original value.
+ */
+$.fn.resetForm = function() {
+    return this.each(function() {
+        // guard against an input with the name of 'reset'
+        // note that IE reports the reset function as an 'object'
+        if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
+            this.reset();
+        }
+    });
+};
+
+/**
+ * Enables or disables any matching elements.
+ */
+$.fn.enable = function(b) {
+    if (b === undefined) {
+        b = true;
+    }
+    return this.each(function() {
+        this.disabled = !b;
+    });
+};
+
+/**
+ * Checks/unchecks any matching checkboxes or radio buttons and
+ * selects/deselects and matching option elements.
+ */
+$.fn.selected = function(select) {
+    if (select === undefined) {
+        select = true;
+    }
+    return this.each(function() {
+        var t = this.type;
+        if (t == 'checkbox' || t == 'radio') {
+            this.checked = select;
+        }
+        else if (this.tagName.toLowerCase() == 'option') {
+            var $sel = $(this).parent('select');
+            if (select && $sel[0] && $sel[0].type == 'select-one') {
+                // deselect all other options
+                $sel.find('option').selected(false);
+            }
+            this.selected = select;
+        }
+    });
+};
+
+// expose debug var
+$.fn.ajaxSubmit.debug = false;
+
+// helper fn for console logging
+function log() {
+    if (!$.fn.ajaxSubmit.debug) 
+        return;
+    var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
+    if (window.console && window.console.log) {
+        window.console.log(msg);
+    }
+    else if (window.opera && window.opera.postError) {
+        window.opera.postError(msg);
+    }
+}
+
+})(jQuery);
diff --git a/plugins/jquery.jeditable.mini.js b/plugins/jquery.jeditable.mini.js
new file mode 100644
index 0000000..ef885f0
--- /dev/null
+++ b/plugins/jquery.jeditable.mini.js
@@ -0,0 +1,38 @@
+
+(function($){$.fn.editable=function(target,options){if('disable'==target){$(this).data('disabled.editable',true);return;}
+if('enable'==target){$(this).data('disabled.editable',false);return;}
+if('destroy'==target){$(this).unbind($(this).data('event.editable')).removeData('disabled.editable').removeData('event.editable');return;}
+var settings=$.extend({},$.fn.editable.defaults,{target:target},options);var plugin=$.editable.types[settings.type].plugin||function(){};var submit=$.editable.types[settings.type].submit||function(){};var buttons=$.editable.types[settings.type].buttons||$.editable.types['defaults'].buttons;var content=$.editable.types[settings.type].content||$.editable.types['defaults'].content;var element=$.editable.types[settings.type].element||$.editable.types['defaults'].element;var reset=$.editable.types[settings.type].reset||$.editable.types['defaults'].reset;var callback=settings.callback||function(){};var onedit=settings.onedit||function(){};var onsubmit=settings.onsubmit||function(){};var onreset=settings.onreset||function(){};var onerror=settings.onerror||reset;if(settings.tooltip){$(this).attr('title',settings.tooltip);}
+settings.autowidth='auto'==settings.width;settings.autoheight='auto'==settings.height;return this.each(function(){var self=this;var savedwidth=$(self).width();var savedheight=$(self).height();$(this).data('event.editable',settings.event);if(!$.trim($(this).html())){$(this).html(settings.placeholder);}
+$(this).bind(settings.event,function(e){if(true===$(this).data('disabled.editable')){return;}
+if(self.editing){return;}
+if(false===onedit.apply(this,[settings,self])){return;}
+e.preventDefault();e.stopPropagation();if(settings.tooltip){$(self).removeAttr('title');}
+if(0==$(self).width()){settings.width=savedwidth;settings.height=savedheight;}else{if(settings.width!='none'){settings.width=settings.autowidth?$(self).width():settings.width;}
+if(settings.height!='none'){settings.height=settings.autoheight?$(self).height():settings.height;}}
+if($(this).html().toLowerCase().replace(/(;|")/g,'')==settings.placeholder.toLowerCase().replace(/(;|")/g,'')){$(this).html('');}
+self.editing=true;self.revert=$(self).html();$(self).html('');var form=$('<form />');if(settings.cssclass){if('inherit'==settings.cssclass){form.attr('class',$(self).attr('class'));}else{form.attr('class',settings.cssclass);}}
+if(settings.style){if('inherit'==settings.style){form.attr('style',$(self).attr('style'));form.css('display',$(self).css('display'));}else{form.attr('style',settings.style);}}
+var input=element.apply(form,[settings,self]);var input_content;if(settings.loadurl){var t=setTimeout(function(){input.disabled=true;content.apply(form,[settings.loadtext,settings,self]);},100);var loaddata={};loaddata[settings.id]=self.id;if($.isFunction(settings.loaddata)){$.extend(loaddata,settings.loaddata.apply(self,[self.revert,settings]));}else{$.extend(loaddata,settings.loaddata);}
+$.ajax({type:settings.loadtype,url:settings.loadurl,data:loaddata,async:false,success:function(result){window.clearTimeout(t);input_content=result;input.disabled=false;}});}else if(settings.data){input_content=settings.data;if($.isFunction(settings.data)){input_content=settings.data.apply(self,[self.revert,settings]);}}else{input_content=self.revert;}
+content.apply(form,[input_content,settings,self]);input.attr('name',settings.name);buttons.apply(form,[settings,self]);$(self).append(form);plugin.apply(form,[settings,self]);$(':input:visible:enabled:first',form).focus();if(settings.select){input.select();}
+input.keydown(function(e){if(e.keyCode==27){e.preventDefault();reset.apply(form,[settings,self]);}});var t;if('cancel'==settings.onblur){input.blur(function(e){t=setTimeout(function(){reset.apply(form,[settings,self]);},500);});}else if('submit'==settings.onblur){input.blur(function(e){t=setTimeout(function(){form.submit();},200);});}else if($.isFunction(settings.onblur)){input.blur(function(e){settings.onblur.apply(self,[input.val(),settings]);});}else{input.blur(function(e){});}
+form.submit(function(e){if(t){clearTimeout(t);}
+e.preventDefault();if(false!==onsubmit.apply(form,[settings,self])){if(false!==submit.apply(form,[settings,self])){if($.isFunction(settings.target)){var str=settings.target.apply(self,[input.val(),settings]);$(self).html(str);self.editing=false;callback.apply(self,[self.innerHTML,settings]);if(!$.trim($(self).html())){$(self).html(settings.placeholder);}}else{var submitdata={};submitdata[settings.name]=input.val();submitdata[settings.id]=self.id;if($.isFunction(settings.submitdata)){$.extend(submitdata,settings.submitdata.apply(self,[self.revert,settings]));}else{$.extend(submitdata,settings.submitdata);}
+if('PUT'==settings.method){submitdata['_method']='put';}
+$(self).html(settings.indicator);var ajaxoptions={type:'POST',data:submitdata,dataType:'html',url:settings.target,success:function(result,status){if(ajaxoptions.dataType=='html'){$(self).html(result);}
+self.editing=false;callback.apply(self,[result,settings]);if(!$.trim($(self).html())){$(self).html(settings.placeholder);}},error:function(xhr,status,error){onerror.apply(form,[settings,self,xhr]);}};$.extend(ajaxoptions,settings.ajaxoptions);$.ajax(ajaxoptions);}}}
+$(self).attr('title',settings.tooltip);return false;});});this.reset=function(form){if(this.editing){if(false!==onreset.apply(form,[settings,self])){$(self).html(self.revert);self.editing=false;if(!$.trim($(self).html())){$(self).html(settings.placeholder);}
+if(settings.tooltip){$(self).attr('title',settings.tooltip);}}}};});};$.editable={types:{defaults:{element:function(settings,original){var input=$('<input type="hidden"></input>');$(this).append(input);return(input);},content:function(string,settings,original){$(':input:first',this).val(string);},reset:function(settings,original){original.reset(this);},buttons:function(settings,original){var form=this;if(settings.submit){if(settings.submit.match(/>$/)){var submit=$(settings.submit).click(function(){if(submit.attr("type")!="submit"){form.submit();}});}else{var submit=$('<button type="submit" />');submit.html(settings.submit);}
+$(this).append(submit);}
+if(settings.cancel){if(settings.cancel.match(/>$/)){var cancel=$(settings.cancel);}else{var cancel=$('<button type="cancel" />');cancel.html(settings.cancel);}
+$(this).append(cancel);$(cancel).click(function(event){if($.isFunction($.editable.types[settings.type].reset)){var reset=$.editable.types[settings.type].reset;}else{var reset=$.editable.types['defaults'].reset;}
+reset.apply(form,[settings,original]);return false;});}}},text:{element:function(settings,original){var input=$('<input />');if(settings.width!='none'){input.width(settings.width);}
+if(settings.height!='none'){input.height(settings.height);}
+input.attr('autocomplete','off');$(this).append(input);return(input);}},textarea:{element:function(settings,original){var textarea=$('<textarea />');if(settings.rows){textarea.attr('rows',settings.rows);}else if(settings.height!="none"){textarea.height(settings.height);}
+if(settings.cols){textarea.attr('cols',settings.cols);}else if(settings.width!="none"){textarea.width(settings.width);}
+$(this).append(textarea);return(textarea);}},select:{element:function(settings,original){var select=$('<select />');$(this).append(select);return(select);},content:function(data,settings,original){if(String==data.constructor){eval('var json = '+data);}else{var json=data;}
+for(var key in json){if(!json.hasOwnProperty(key)){continue;}
+if('selected'==key){continue;}
+var option=$('<option />').val(key).append(json[key]);$('select',this).append(option);}
+$('select',this).children().each(function(){if($(this).val()==json['selected']||$(this).text()==$.trim(original.revert)){$(this).attr('selected','selected');}});}}},addInputType:function(name,input){$.editable.types[name]=input;}};$.fn.editable.defaults={name:'value',id:'id',type:'text',width:'auto',height:'auto',event:'click.editable',onblur:'cancel',loadtype:'GET',loadtext:'Loading...',placeholder:'Click to edit',loaddata:{},submitdata:{},ajaxoptions:{}};})(jQuery);
+

diff --git a/plugins/jquery.menu.js b/plugins/jquery.menu.js
new file mode 100755
index 0000000..56c9945
--- /dev/null
+++ b/plugins/jquery.menu.js
@@ -0,0 +1,943 @@
+/*
+
+ * jQuery Menu plugin
+
+ * Version: 0.0.9
+
+ *
+
+ * Copyright (c) 2007 Roman Weich
+
+ * http://p.sohei.org
+
+ *
+
+ * Dual licensed under the MIT and GPL licenses 
+
+ * (This means that you can choose the license that best suits your project, and use it accordingly):
+
+ *   http://www.opensource.org/licenses/mit-license.php
+
+ *   http://www.gnu.org/licenses/gpl.html
+
+ *
+
+ * Changelog: 
+
+ * v 0.0.9 - 2008-01-19
+
+ */
+
+
+
+(function($)
+
+{
+
+	var menus = [], //list of all menus
+
+		visibleMenus = [], //list of all visible menus
+
+		activeMenu = activeItem = null,
+
+		menuDIVElement = $('<div class="menu-div outerbox" style="position:absolute;top:0;left:0;display:none;"><div class="shadowbox1"></div><div class="shadowbox2"></div><div class="shadowbox3"></div></div>')[0],
+
+		menuULElement = $('<ul class="menu-ul innerbox"></ul>')[0],
+
+		menuItemElement = $('<li style="position:relative;"><div class="menu-item"></div></li>')[0],
+
+		arrowElement = $('<img class="menu-item-arrow" />')[0],
+
+		$rootDiv = $('<div id="root-menu-div" style="position:absolute;top:0;left:0;"></div>'), //create main menu div
+
+		defaults = {
+
+			// $.Menu options
+
+			showDelay : 200,
+
+			hideDelay : 200,
+
+			hoverOpenDelay: 0,
+
+			offsetTop : 0,
+
+			offsetLeft : 0,
+
+			minWidth: 0,
+
+			onOpen: null,
+
+			onClose: null,
+
+
+
+			// $.MenuItem options
+
+			onClick: null,
+
+			arrowSrc: null,
+
+			addExpando: false,
+
+			
+
+			// $.fn.menuFromElement options
+
+			copyClassAttr: false
+
+		};
+
+	
+
+	$(function(){
+
+		$rootDiv.appendTo('body');
+
+	});
+
+	
+
+	$.extend({
+
+		MenuCollection : function(items) {
+
+		
+
+			this.menus = [];
+
+		
+
+			this.init(items);
+
+		}
+
+	});
+
+	$.extend($.MenuCollection, {
+
+		prototype : {
+
+			init : function(items)
+
+			{
+
+				if ( items && items.length )
+
+				{
+
+					for ( var i = 0; i < items.length; i++ )
+
+					{
+
+						this.addMenu(items[i]);
+
+						items[i].menuCollection = this;
+
+					}
+
+				}
+
+			},
+
+			addMenu : function(menu)
+
+			{
+
+				if ( menu instanceof $.Menu )
+
+					this.menus.push(menu);
+
+				
+
+				menu.menuCollection = this;
+
+			
+
+				var self = this;
+
+				$(menu.target).hover(function(){
+
+					if ( menu.visible )
+
+						return;
+
+
+
+					//when there is an open menu in this collection, hide it and show the new one
+
+					for ( var i = 0; i < self.menus.length; i++ )
+
+					{
+
+						if ( self.menus[i].visible )
+
+						{
+
+							self.menus[i].hide();
+
+							menu.show();
+
+							return;
+
+						}
+
+					}
+
+				}, function(){});
+
+			}
+
+		}
+
+	});
+
+
+
+	
+
+	$.extend({
+
+		Menu : function(target, items, options) {
+
+			this.menuItems = []; //all direct child $.MenuItem objects
+
+			this.subMenus = []; //all subMenus from this.menuItems
+
+			this.visible = false;
+
+			this.active = false; //this menu has hover or one of its submenus is open
+
+			this.parentMenuItem = null;
+
+			this.settings = $.extend({}, defaults, options);
+
+			this.target = target;
+
+			this.$eDIV = null;
+
+			this.$eUL = null;
+
+			this.timer = null;
+
+			this.menuCollection = null;
+
+			this.openTimer = null;
+
+
+
+			this.init();
+
+			if ( items && items.constructor == Array )
+
+				this.addItems(items);
+
+		}
+
+	});
+
+
+
+	$.extend($.Menu, {
+
+		checkMouse : function(e)
+
+		{
+
+			var t = e.target;
+
+
+
+			//the user clicked on the target of the currenty open menu
+
+			if ( visibleMenus.length && t == visibleMenus[0].target )
+
+				return;
+
+			
+
+			//get the last node before the #root-menu-div
+
+			while ( t.parentNode && t.parentNode != $rootDiv[0] )
+
+				t = t.parentNode;
+
+
+
+			//is the found node one of the visible menu elements?
+
+			if ( !$(visibleMenus).filter(function(){ return this.$eDIV[0] == t }).length )
+
+			{
+
+				$.Menu.closeAll();
+
+			}
+
+		},
+
+		checkKey : function(e)
+
+		{
+
+			switch ( e.keyCode )
+
+			{
+
+				case 13: //return
+
+					if ( activeItem )
+
+						activeItem.click(e, activeItem.$eLI[0]);
+
+					break;
+
+				case 27: //ESC
+
+					$.Menu.closeAll();
+
+					break;
+
+				case 37: //left
+
+					if ( !activeMenu )
+
+						activeMenu = visibleMenus[0];
+
+					var a = activeMenu;
+
+					if ( a && a.parentMenuItem ) //select the parent menu and close the submenu
+
+					{
+
+						//unbind the events temporary, as we dont want the hoverout event to fire
+
+						var pmi = a.parentMenuItem;
+
+						pmi.$eLI.unbind('mouseout').unbind('mouseover');
+
+						a.hide();
+
+						pmi.hoverIn(true);
+
+						setTimeout(function(){ //bind again..but delay it
+
+							pmi.bindHover();
+
+						});
+
+					}
+
+					else if ( a && a.menuCollection ) //select the previous menu in the collection
+
+					{
+
+						var pos,
+
+							mcm = a.menuCollection.menus;
+
+						if ( (pos = $.inArray(a, mcm)) > -1 )
+
+						{
+
+							if ( --pos < 0 )
+
+								pos = mcm.length - 1;
+
+							$.Menu.closeAll();
+
+							mcm[pos].show();
+
+							mcm[pos].setActive();
+
+							if ( mcm[pos].menuItems.length ) //select the first item
+
+								mcm[pos].menuItems[0].hoverIn(true);
+
+						}
+
+					}
+
+					break;
+
+				case 38: //up
+
+					if ( activeMenu )
+
+						activeMenu.selectNextItem(-1);
+
+					break;
+
+				case 39: //right
+
+					if ( !activeMenu )
+
+						activeMenu = visibleMenus[0];
+
+					var m,
+
+						a = activeMenu,
+
+						asm = activeItem ? activeItem.subMenu : null;
+
+					if ( a )
+
+					{
+
+						if ( asm && asm.menuItems.length ) //select the submenu
+
+						{
+
+							asm.show();
+
+							asm.menuItems[0].hoverIn();
+
+						}
+
+						else if ( (a = a.inMenuCollection()) ) //select the next menu in the collection
+
+						{
+
+							var pos,
+
+								mcm = a.menuCollection.menus;
+
+							if ( (pos = $.inArray(a, mcm)) > -1 )
+
+							{
+
+								if ( ++pos >= mcm.length )
+
+									pos = 0;
+
+								$.Menu.closeAll();
+
+								mcm[pos].show();
+
+								mcm[pos].setActive();
+
+								if ( mcm[pos].menuItems.length ) //select the first item
+
+									mcm[pos].menuItems[0].hoverIn(true);
+
+							}
+
+						}
+
+					}
+
+					break;
+
+				case 40: //down
+
+					if ( !activeMenu )
+
+					{
+
+						if ( visibleMenus.length && visibleMenus[0].menuItems.length )
+
+							visibleMenus[0].menuItems[0].hoverIn();
+
+					}
+
+					else
+
+						activeMenu.selectNextItem();
+
+					break;
+
+			}
+
+			if ( e.keyCode > 36 && e.keyCode < 41 )
+
+				return false; //this will prevent scrolling
+
+		},
+
+		closeAll : function()
+
+		{
+
+			while ( visibleMenus.length )
+
+				visibleMenus[0].hide();
+
+		},
+
+		setDefaults : function(d)
+
+		{
+
+			$.extend(defaults, d);
+
+		},
+
+		prototype : {
+
+			/**
+
+			 * create / initialize new menu
+
+			 */
+
+			init : function()
+
+			{
+
+				var self = this;
+
+				if ( !this.target )
+
+					return;
+
+				else if ( this.target instanceof $.MenuItem )
+
+				{
+
+					this.parentMenuItem = this.target;
+
+					this.target.addSubMenu(this);
+
+					this.target = this.target.$eLI;
+
+				}
+
+
+
+				menus.push(this);
+
+
+
+				//use the dom methods instead the ones from jquery (faster)
+
+				this.$eDIV = $(menuDIVElement.cloneNode(1));
+
+				this.$eUL = $(menuULElement.cloneNode(1));
+
+				this.$eDIV[0].appendChild(this.$eUL[0]);
+
+				$rootDiv[0].appendChild(this.$eDIV[0]);
+
+
+
+				//bind events
+
+				if ( !this.parentMenuItem )
+
+				{
+
+					$(this.target).click(function(e){
+
+						self.onClick(e);
+
+					}).hover(function(e){
+
+						self.setActive();
+
+
+
+						if ( self.settings.hoverOpenDelay )
+
+						{
+
+							self.openTimer = setTimeout(function(){
+
+								if ( !self.visible )
+
+									self.onClick(e);
+
+							}, self.settings.hoverOpenDelay);
+
+						}
+
+					}, function(){
+
+						if ( !self.visible )
+
+							$(this).removeClass('activetarget');
+
+
+
+						if ( self.openTimer )
+
+							clearTimeout(self.openTimer);
+
+					});
+
+				}
+
+				else
+
+				{
+
+					this.$eDIV.hover(function(){
+
+						self.setActive();
+
+					}, function(){});
+
+				}
+
+			},
+
+			setActive : function()
+
+			{
+
+				if ( !this.parentMenuItem )
+
+					$(this.target).addClass('activetarget');
+
+				else
+
+					this.active = true;
+
+			},
+
+			addItem : function(item)
+
+			{
+
+				if ( item instanceof $.MenuItem )
+
+				{
+
+					if ( $.inArray(item, this.menuItems) == -1 )
+
+					{
+
+						this.$eUL.append(item.$eLI);
+
+						this.menuItems.push(item);
+
+						item.parentMenu = this;
+
+						if ( item.subMenu )
+
+							this.subMenus.push(item.subMenu);
+
+					}
+
+				}
+
+				else
+
+				{
+
+					this.addItem(new $.MenuItem(item, this.settings));
+
+				}
+
+			},
+
+			addItems : function(items)
+
+			{
+
+				for ( var i = 0; i < items.length; i++ )
+
+				{
+
+					this.addItem(items[i]);
+
+				}
+
+			},
+
+			removeItem : function(item)
+
+			{
+
+				var pos = $.inArray(item, this.menuItems);
+
+				if ( pos > -1 )
+
+					this.menuItems.splice(pos, 1);
+
+				item.parentMenu = null;
+
+			},
+
+			hide : function()
+
+			{
+
+				if ( !this.visible )
+
+					return;
+
+				
+
+				var i, 
+
+					pos = $.inArray(this, visibleMenus);
+
+
+
+				this.$eDIV.hide();
+
+
+
+				if ( pos >= 0 )
+
+					visibleMenus.splice(pos, 1);
+
+				this.visible = this.active = false;
+
+
+
+				$(this.target).removeClass('activetarget');
+
+
+
+				//hide all submenus
+
+				for ( i = 0; i < this.subMenus.length; i++ )
+
+				{
+
+					this.subMenus[i].hide();
+
+				}
+
+
+
+				//set all items inactive (e.g. remove hover class..)
+
+				for ( i = 0; i < this.menuItems.length; i++ )
+
+				{
+
+					if ( this.menuItems[i].active )
+
+						this.menuItems[i].setInactive();
+
+				}
+
+
+
+				if ( !visibleMenus.length ) //unbind events when the last menu was closed
+
+					$(document).unbind('mousedown', $.Menu.checkMouse).unbind('keydown', $.Menu.checkKey);
+
+
+
+				if ( activeMenu == this )
+
+					activeMenu = null;
+
+					
+
+				if ( this.settings.onClose )
+
+					this.settings.onClose.call(this);
+
+			},
+
+			show : function(e)
+
+			{
+
+				if ( this.visible )
+
+					return;
+
+
+
+				var zi, 
+
+					pmi = this.parentMenuItem;
+
+
+
+				if ( this.menuItems.length ) //show only when it has items
+
+				{
+
+					if ( pmi ) //set z-index
+
+					{
+
+						zi = parseInt(pmi.parentMenu.$eDIV.css('z-index'));
+
+						this.$eDIV.css('z-index', (isNaN(zi) ? 1 : zi + 1));
+
+					}
+
+					this.$eDIV.css({visibility: 'hidden', display:'block'});
+
+
+
+					//set min-width
+
+					if ( this.settings.minWidth )
+
+					{
+
+						if ( this.$eDIV.width() < this.settings.minWidth )
+
+							this.$eDIV.css('width', this.settings.minWidth);
+
+					}
+
+					
+
+					this.setPosition();
+
+					this.$eDIV.css({display:'none', visibility: ''}).show();
+
+
+
+					//IEs default width: auto is bad! ie6 and ie7 have are producing different errors.. (7 = 5px shadowbox + 2px border)
+
+					if ( $.browser.msie )
+
+						this.$eUL.css('width', parseInt($.browser.version) == 6 ? this.$eDIV.width() - 7 : this.$eUL.width());
+
+
+
+					if ( this.settings.onOpen )
+
+						this.settings.onOpen.call(this);
+
+				}
+
+				if ( visibleMenus.length == 0 )
+
+					$(document).bind('mousedown', $.Menu.checkMouse).bind('keydown', $.Menu.checkKey);
+
+
+
+				this.visible = true;
+
+				visibleMenus.push(this);
+
+			},
+
+			setPosition : function()
+
+			{
+
+				var $t, o, posX, posY, 
+
+					pmo, //parent menu offset
+
+					wst, //window scroll top
+
+					wsl, //window scroll left
+
+					ww = $(window).width(), 
+
+					wh = $(window).height(),
+
+					pmi = this.parentMenuItem,
+
+					height = this.$eDIV[0].clientHeight,
+
+					width = this.$eDIV[0].clientWidth,
+
+					pheight; //parent height
+
+
+
+				if ( pmi )
+
+				{
+
+					//position on the right side of the parent menu item
+
+					o = pmi.$eLI.offset();
+
+					posX = o.left + pmi.$eLI.width();
+
+					posY = o.top;
+
+				}
+
+				else
+
+				{
+
+					//position right below the target
+
+					$t = $(this.target);
+
+					o = $t.offset();
+
+					posX = o.left + this.settings.offsetLeft;
+
+					posY = o.top + $t.height() + this.settings.offsetTop;
+
+				}
+
+
+
+				//y-pos
+
+				if ( $.fn.scrollTop )
+
+				{
+
+					wst = $(window).scrollTop();
+
+					if ( wh < height ) //menu is bigger than the window
+
+					{
+
+						//position the menu at the top of the visible area
+
+						posY = wst;
+
+					}
+
+					else if ( wh + wst < posY + height ) //outside on the bottom?
+
+					{
+
+						if ( pmi )
+
+						{
+
+							pmo = pmi.parentMenu.$eDIV.offset();
+
+							pheight = pmi.parentMenu.$eDIV[0].clientHeight;
+
+							if ( height <= pheight )
+
+							{
+
+								//bottom position = parentmenu-bottom position
+
+								posY = pmo.top + pheight - height;
+
+							}
+
+							else
+
+							{
+
+								//top position = parentmenu-top position
+
+								posY = pmo.top;
+
+							}
+
+							//still outside on the bottom?
+
+							if ( wh + wst < posY + height )
+
+							{
+
+								//shift the menu upwards till the bottom is visible
+
+								posY -= posY + height - (wh + wst);
+
+							}
+
+						}
+
+						else
+
+						{
+
+							//shift the menu upwards till the bottom is visible
+
+							posY -= posY + height - (wh + wst);
+
+						}
+
+					}
+
+				}
+
+				//x-pos
+
+				if ( $.fn.scrollLeft )
+
+				{
+
+					wsl = $(window).scrollLeft();
+
+					if ( ww + wsl < posX + width )
+
+					{
+
+						if ( pmi )
+
+						{
+
+							//display the menu not on the right side but on the left side
+
+							posX -= pmi.$eLI.width() + width;
+
+							//outside on the left now?
+
+							if ( posX < wsl )
+
+								posX = wsl;
+
+						}
+
+						else
+
+						{
+
+							//shift the menu to the left until it fits
+
+							posX -= posX + width - (ww + wsl);
+
+						}
+
+					}
+
+				}
+
+
+
+				//set position
+
+				this.$eDIV.css({left: posX, top: posY});
+
+			},
+
+			onClick : function(e)
+
+			{
+
+				if ( this.visible )
+
+				{
+
+					this.hide();
+
+					this.setActive(); //the class is removed in the hide() method..add it again
+
+				}
+
+				else
+
+				{
+
+					//close all open menus
+
+					$.Menu.closeAll();
+
+					this.show(e);
+
+				}
+
+			},
+
+			addTimer : function(callback, delay)
+
+			{
+
+				var self = this;
+
+				this.timer = setTimeout(function(){
+
+					callback.call(self);
+
+					self.timer = null;
+
+				}, delay);
+
+			},
+
+			removeTimer : function()
+
+			{
+
+				if ( this.timer )
+
+				{
+
+					clearTimeout(this.timer);
+
+					this.timer = null;
+
+				}
+
+			},
+
+			selectNextItem : function(offset)
+
+			{
+
+				var i, pos = 0,
+
+					mil = this.menuItems.length,
+
+					o = offset || 1;
+
+				
+
+				//get current pos
+
+				for ( i = 0; i < mil; i++ )
+
+				{
+
+					if ( this.menuItems[i].active )
+
+					{
+
+						pos = i;
+
+						break;
+
+					}
+
+				}
+
+				this.menuItems[pos].hoverOut();
+
+
+
+				do //jump over the separators
+
+				{
+
+					pos += o;
+
+					if ( pos >= mil )
+
+						pos = 0;
+
+					else if ( pos < 0 )
+
+						pos = mil - 1;
+
+				} while ( this.menuItems[pos].separator );
+
+				this.menuItems[pos].hoverIn(true);
+
+			},
+
+			inMenuCollection : function()
+
+			{
+
+				var m = this;
+
+				while ( m.parentMenuItem )
+
+					m = m.parentMenuItem.parentMenu;
+
+				return m.menuCollection ? m : null;
+
+			},
+
+			destroy : function() //delete menu
+
+			{
+
+				var pos, item;
+
+
+
+				this.hide();
+
+
+
+				//unbind events
+
+				if ( !this.parentMenuItem )
+
+					$(this.target).unbind('click').unbind('mouseover').unbind('mouseout');
+
+				else
+
+					this.$eDIV.unbind('mouseover').unbind('mouseout');
+
+
+
+				//destroy all items
+
+				while ( this.menuItems.length )
+
+				{
+
+					item = this.menuItems[0];
+
+					item.destroy();
+
+					delete item;
+
+				}
+
+
+
+				if ( (pos = $.inArray(this, menus)) > -1 )
+
+					menus.splice(pos, 1);
+
+
+
+				if ( this.menuCollection )
+
+				{
+
+					if ( (pos = $.inArray(this, this.menuCollection.menus)) > -1 )
+
+						this.menuCollection.menus.splice(pos, 1);
+
+				}
+
+					
+
+				this.$eDIV.remove();
+
+			}
+
+		}
+
+	});
+
+
+
+	$.extend({
+
+		MenuItem : function(obj, options)
+
+		{
+
+			if ( typeof obj == 'string' )
+
+				obj = {src: obj};
+
+
+
+			this.src = obj.src || '';
+
+			this.url = obj.url || null;
+
+			this.urlTarget = obj.target || null;
+
+			this.addClass = obj.addClass || null;
+
+			this.data = obj.data || null;
+
+
+
+			this.$eLI = null;
+
+			this.parentMenu = null;
+
+			this.subMenu = null;
+
+			this.settings = $.extend({}, defaults, options);
+
+			this.active = false;
+
+			this.enabled = true;
+
+			this.separator = false;
+
+
+
+			this.init();
+
+			
+
+			if ( obj.subMenu )
+
+				new $.Menu(this, obj.subMenu, options);
+
+		}
+
+	});
+
+
+
+	$.extend($.MenuItem, {
+
+		prototype : {
+
+			init : function()
+
+			{
+
+				var i, isStr,
+
+					src = this.src,
+
+					self = this;
+
+
+
+				this.$eLI = $(menuItemElement.cloneNode(1));
+
+
+
+				if ( this.addClass )
+
+					this.$eLI[0].setAttribute('class', this.addClass);
+
+
+
+				if ( this.settings.addExpando && this.data )
+
+					this.$eLI[0].menuData = this.data;
+
+					
+
+				if ( src == '' )
+
+				{
+
+					this.$eLI.addClass('menu-separator');
+
+					this.separator = true;
+
+				}
+
+				else
+
+				{
+
+					isStr = typeof src == 'string';
+
+					if ( isStr && this.url ) //create a link node, when we have an url
+
+						src = $('<a href="' + this.url + '"' + (this.urlTarget ? 'target="' + this.urlTarget + '"' : '') + '>' + src + '</a>');
+
+					else if ( isStr || !src.length )
+
+						src = [src];
+
+					//go through the passed DOM-Elements (or jquery objects or text nodes.) and append them to the menus list item
+
+					//this.$eLI.append(this.src) is really slow when having a lot(!!!) of items
+
+					for ( i = 0; i < src.length; i++ )
+
+					{
+
+						if ( typeof src[i] == 'string' )
+
+						{
+
+							//we cant use createTextNode, as html entities won't be displayed correctly (eg. &copy;)
+
+							elem = document.createElement('span');
+
+							elem.innerHTML = src[i];
+
+							this.$eLI[0].firstChild.appendChild(elem);
+
+						}
+
+						else
+
+							this.$eLI[0].firstChild.appendChild(src[i].cloneNode(1));
+
+					}
+
+				}
+
+
+
+				this.$eLI.click(function(e){
+
+					self.click(e, this);
+
+				});
+
+				this.bindHover();
+
+			},
+
+			click : function(e, scope)
+
+			{
+
+				if ( this.enabled && this.settings.onClick )
+
+					this.settings.onClick.call(scope, e, this);
+
+			},
+
+			bindHover : function()
+
+			{
+
+				var self = this;
+
+				this.$eLI.hover(function(){
+
+						self.hoverIn();
+
+					}, function(){
+
+						self.hoverOut();
+
+				});
+
+			},
+
+			hoverIn : function(noSubMenu)
+
+			{
+
+				this.removeTimer();
+
+
+
+				var i, 
+
+					pms = this.parentMenu.subMenus,
+
+					pmi = this.parentMenu.menuItems,
+
+					self = this;
+
+
+
+				//remove the timer from the parent item, when there is one (e.g. to close the menu)
+
+				if ( this.parentMenu.timer )
+
+					this.parentMenu.removeTimer();
+
+
+
+				if ( !this.enabled )
+
+					return;
+
+					
+
+				//deactivate all menuItems on the same level
+
+				for ( i = 0; i < pmi.length; i++ )
+
+				{
+
+					if ( pmi[i].active )
+
+						pmi[i].setInactive();
+
+				}
+
+
+
+				this.setActive();
+
+				activeMenu = this.parentMenu;
+
+
+
+				//are there open submenus on the same level? close them!
+
+				for ( i = 0; i < pms.length; i++ )
+
+				{
+
+					if ( pms[i].visible && pms[i] != this.subMenu && !pms[i].timer ) //close if there is no closetimer running already
+
+						pms[i].addTimer(function(){
+
+							this.hide();
+
+						}, pms[i].settings.hideDelay);
+
+				}
+
+
+
+				if ( this.subMenu && !noSubMenu )
+
+				{
+
+					//set timeout to show menu
+
+					this.subMenu.addTimer(function(){
+
+						this.show();
+
+					}, this.subMenu.settings.showDelay);
+
+				}
+
+			},
+
+			hoverOut : function()
+
+			{
+
+				this.removeTimer();
+
+
+
+				if ( !this.enabled )
+
+					return;
+
+				
+
+				if ( !this.subMenu || !this.subMenu.visible )
+
+					this.setInactive();
+
+			},
+
+			removeTimer : function()
+
+			{
+
+				if ( this.subMenu )
+
+				{
+
+					this.subMenu.removeTimer();
+
+				}
+
+			},
+
+			setActive : function()
+
+			{
+
+				this.active = true;
+
+				this.$eLI.addClass('active');
+
+
+
+				//set the parent menu item active too if necessary
+
+				var pmi = this.parentMenu.parentMenuItem;
+
+				if ( pmi && !pmi.active )
+
+					pmi.setActive();
+
+
+
+				activeItem = this;
+
+			},
+
+			setInactive : function()
+
+			{
+
+				this.active = false;
+
+				this.$eLI.removeClass('active');
+
+				if ( this == activeItem )
+
+					activeItem = null;
+
+			},
+
+			enable : function()
+
+			{
+
+				this.$eLI.removeClass('disabled');
+
+				this.enabled = true;
+
+			},
+
+			disable : function()
+
+			{
+
+				this.$eLI.addClass('disabled');
+
+				this.enabled = false;
+
+			},
+
+			destroy : function()
+
+			{
+
+				this.removeTimer();
+
+
+
+				this.$eLI.remove();
+
+
+
+				//unbind events
+
+				this.$eLI.unbind('mouseover').unbind('mouseout').unbind('click');
+
+				//delete submenu
+
+				if ( this.subMenu )
+
+				{
+
+					this.subMenu.destroy();
+
+					delete this.subMenu;
+
+				}
+
+				this.parentMenu.removeItem(this);
+
+			},
+
+			addSubMenu : function(menu)
+
+			{
+
+				if ( this.subMenu )
+
+					return;
+
+				this.subMenu = menu;
+
+				if ( this.parentMenu && $.inArray(menu, this.parentMenu.subMenus) == -1 )
+
+					this.parentMenu.subMenus.push(menu);
+
+				if ( this.settings.arrowSrc )
+
+				{
+
+					var a = arrowElement.cloneNode(0);
+
+					a.setAttribute('src', this.settings.arrowSrc);
+
+					this.$eLI[0].firstChild.appendChild(a);
+
+				}
+
+			}
+
+		}
+
+	});
+
+	
+
+	
+
+	$.extend($.fn, {
+
+		menuFromElement : function(options, list, bar)
+
+		{
+
+			var createItems = function(ul)
+
+			{
+
+				var menuItems = [], 
+
+					subItems,
+
+					menuItem,
+
+					lis, $li, i, subUL, submenu, target, 
+
+					classNames = null;
+
+
+
+				lis = getAllChilds(ul, 'LI');
+
+				for ( i = 0; i < lis.length; i++ )
+
+				{
+
+					subItems = [];
+
+
+
+					if ( !lis[i].childNodes.length ) //empty item? add separator
+
+					{
+
+						menuItems.push(new $.MenuItem('', options));
+
+						continue;
+
+					}
+
+
+
+					if ( (subUL = getOneChild(lis[i], 'UL')) )
+
+					{
+
+						subItems = createItems(subUL);
+
+						//remove subUL from DOM
+
+						$(subUL).remove();
+
+					}
+
+
+
+					//select the target...get the elements inside the li
+
+					$li = $(lis[i]);
+
+					if ( $li[0].childNodes.length == 1 && $li[0].childNodes[0].nodeType == 3 )
+
+						target = $li[0].childNodes[0].nodeValue;
+
+					else
+
+						target = $li[0].childNodes;
+
+
+
+					if ( options && options.copyClassAttr )
+
+						classNames = $li.attr('class');
+
+						
+
+					//create item
+
+					menuItem = new $.MenuItem({src: target, addClass: classNames}, options);
+
+					menuItems.push(menuItem);
+
+					//add submenu
+
+					if ( subItems.length )
+
+						new $.Menu(menuItem, subItems, options);
+
+					
+
+				}
+
+				return menuItems;
+
+			};
+
+			return this.each(function()
+
+			{
+
+				var ul, m;
+
+				//get the list element
+
+				if ( list || (ul = getOneChild(this, 'UL')) )
+
+				{
+
+					//if a specific list element is used, clone it, as we probably need it more than once
+
+					ul = list ? $(list).clone(true)[0] : ul;
+
+					menuItems = createItems(ul);
+
+					if ( menuItems.length )
+
+					{
+
+						m = new $.Menu(this, menuItems, options);
+
+						if ( bar )
+
+							bar.addMenu(m);
+
+					}
+
+					$(ul).hide();
+
+				}
+
+			});
+
+		},
+
+		menuBarFromUL : function(options)
+
+		{
+
+			return this.each(function()
+
+			{
+
+				var i,
+
+					lis = getAllChilds(this, 'LI');
+
+
+
+				if ( lis.length )
+
+				{
+
+					bar = new $.MenuCollection();
+
+					for ( i = 0; i < lis.length; i++ )
+
+						$(lis[i]).menuFromElement(options, null, bar);
+
+				}
+
+			});
+
+		},
+
+		menu : function(options, items)
+
+		{
+
+			return this.each(function()
+
+			{
+
+				if ( items && items.constructor == Array )
+
+					new $.Menu(this, items, options);
+
+				else
+
+				{
+
+					if ( this.nodeName.toUpperCase() == 'UL' )
+
+						$(this).menuBarFromUL(options);
+
+					else
+
+						$(this).menuFromElement(options, items);
+
+				}
+
+			});
+
+		}
+
+	});
+
+
+
+	//faster than using jquery
+
+	var getOneChild = function(elem, name)
+
+	{
+
+		if ( !elem )
+
+			return null;
+
+
+
+		var n = elem.firstChild;
+
+		for ( ; n; n = n.nextSibling ) 
+
+		{
+
+			if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name )
+
+				return n;
+
+		}
+
+		return null;
+
+	};
+
+	//faster than using jquery
+
+	var getAllChilds = function(elem, name)
+
+	{
+
+		if ( !elem )
+
+			return [];
+
+
+
+		var r = [],
+
+			n = elem.firstChild;
+
+		for ( ; n; n = n.nextSibling ) 
+
+		{
+
+			if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name )
+
+				r[r.length] = n;
+
+		}
+
+		return r;
+
+	};
+
+
+
+})(jQuery);

diff --git a/plugins/jquery.menu.pack.js b/plugins/jquery.menu.pack.js
new file mode 100644
index 0000000..7ed40b8
--- /dev/null
+++ b/plugins/jquery.menu.pack.js
@@ -0,0 +1,31 @@
+/*
+
+ * jQuery Menu plugin
+
+ * Version: 0.0.9
+
+ *
+
+ * Copyright (c) 2007 Roman Weich
+
+ * http://p.sohei.org
+
+ *
+
+ * Dual licensed under the MIT and GPL licenses 
+
+ * (This means that you can choose the license that best suits your project, and use it accordingly):
+
+ *   http://www.opensource.org/licenses/mit-license.php
+
+ *   http://www.gnu.org/licenses/gpl.html
+
+ *
+
+ * Changelog: 
+
+ * v 0.0.9 - 2008-01-19
+
+ */
+
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(5($){8 g=[],B=[],G=12=k,3c=$(\'<E Y="1g-E 3x" 24="1Z:3h;1i:0;1w:0;2r:2X;"><E Y="3G"></E><E Y="3E"></E><E Y="3C"></E></E>\')[0],2L=$(\'<29 Y="1g-29 3q"></29>\')[0],2H=$(\'<X 24="1Z:3j;"><E Y="1g-1k"></E></X>\')[0],3g=$(\'<3Q Y="1g-1k-3P" />\')[0],$1V=$(\'<E 3N="3M-1g-E" 24="1Z:3h;1i:0;1w:0;"></E>\'),1P={34:31,2Y:31,2p:0,2V:0,2U:0,1J:0,2B:k,2i:k,1r:k,2d:k,2M:Q,2J:Q};$(5(){$1V.3s(\'3p\')});$.R({25:5(a){2.11=[];2.1s(a)}});$.R($.25,{1X:{1s:5(a){4(a&&a.9){J(8 i=0;i<a.9;i++){2.2y(a[i]);a[i].V=2}}},2y:5(a){4(a 2x $.C)2.11.16(a);a.V=2;8 b=2;$(a.q).1T(5(){4(a.M)s;J(8 i=0;i<b.11.9;i++){4(b.11[i].M){b.11[i].W();a.14();s}}},5(){})}}});$.R({C:5(a,b,c){2.l=[];2.1c=[];2.M=Q;2.O=Q;2.N=k;2.u=$.R({},1P,c);2.q=a;2.$w=k;2.$1q=k;2.17=k;2.V=k;2.1L=k;2.1s();4(b&&b.2T==2S)2.2Q(b)}});$.R($.C,{2m:5(e){8 t=e.q;4(B.9&&t==B[0].q)s;1y(t.2t&&t.2t!=$1V[0])t=t.2t;4(!$(B).3B(5(){s 2.$w[0]==t}).9){$.C.1n()}},2f:5(e){3A(e.2c){1m 13:4(12)12.1a(e,12.$p[0]);1h;1m 27:$.C.1n();1h;1m 37:4(!G)G=B[0];8 a=G;4(a&&a.N){8 b=a.N;b.$p.K(\'1F\').K(\'1E\');a.W();b.15(P);2l(5(){b.23()})}v 4(a&&a.V){8 c,F=a.V.11;4((c=$.18(a,F))>-1){4(--c<0)c=F.9-1;$.C.1n();F[c].14();F[c].Z();4(F[c].l.9)F[c].l[0].15(P)}}1h;1m 38:4(G)G.1Y(-1);1h;1m 39:4(!G)G=B[0];8 m,a=G,1t=12?12.x:k;4(a){4(1t&&1t.l.9){1t.14();1t.l[0].15()}v 4((a=a.2C())){8 c,F=a.V.11;4((c=$.18(a,F))>-1){4(++c>=F.9)c=0;$.C.1n();F[c].14();F[c].Z();4(F[c].l.9)F[c].l[0].15(P)}}}1h;1m 3i:4(!G){4(B.9&&B[0].l.9)B[0].l[0].15()}v G.1Y();1h}4(e.2c>36&&e.2c<3S)s Q},1n:5(){1y(B.9)B[0].W()},3R:5(d){$.R(1P,d)},1X:{1s:5(){8 a=2;4(!2.q)s;v 4(2.q 2x $.1j){2.N=2.q;2.q.3f(2);2.q=2.q.$p}g.16(2);2.$w=$(3c.1A(1));2.$1q=$(2L.1A(1));2.$w[0].1z(2.$1q[0]);$1V[0].1z(2.$w[0]);4(!2.N){$(2.q).1a(5(e){a.1r(e)}).1T(5(e){a.Z();4(a.u.2p){a.1L=2l(5(){4(!a.M)a.1r(e)},a.u.2p)}},5(){4(!a.M)$(2).1W(\'2w\');4(a.1L)3e(a.1L)})}v{2.$w.1T(5(){a.Z()},5(){})}},Z:5(){4(!2.N)$(2.q).10(\'2w\');v 2.O=P},2v:5(a){4(a 2x $.1j){4($.18(a,2.l)==-1){2.$1q.3O(a.$p);2.l.16(a);a.A=2;4(a.x)2.1c.16(a.x)}}v{2.2v(19 $.1j(a,2.u))}},2Q:5(a){J(8 i=0;i<a.9;i++){2.2v(a[i])}},3d:5(a){8 b=$.18(a,2.l);4(b>-1)2.l.1U(b,1);a.A=k},W:5(){4(!2.M)s;8 i,I=$.18(2,B);2.$w.W();4(I>=0)B.1U(I,1);2.M=2.O=Q;$(2.q).1W(\'2w\');J(i=0;i<2.1c.9;i++){2.1c[i].W()}J(i=0;i<2.l.9;i++){4(2.l[i].O)2.l[i].1S()}4(!B.9)$(2u).K(\'3b\',$.C.2m).K(\'3a\',$.C.2f);4(G==2)G=k;4(2.u.2i)2.u.2i.1R(2)},14:5(e){4(2.M)s;8 a,D=2.N;4(2.l.9){4(D){a=35(D.A.$w.1e(\'z-33\'));2.$w.1e(\'z-33\',(3L(a)?1:a+1))}2.$w.1e({32:\'3K\',2r:\'3J\'});4(2.u.1J){4(2.$w.L()<2.u.1J)2.$w.1e(\'L\',2.u.1J)}2.30();2.$w.1e({2r:\'2X\',32:\'\'}).14();4($.2Z.3I)2.$1q.1e(\'L\',35($.2Z.3H)==6?2.$w.L()-7:2.$1q.L());4(2.u.2B)2.u.2B.1R(2)}4(B.9==0)$(2u).2W(\'3b\',$.C.2m).2W(\'3a\',$.C.2f);2.M=P;B.16(2)},30:5(){8 a,o,S,H,1N,1b,1o,2o=$(1K).L(),1p=$(1K).T(),D=2.N,T=2.$w[0].2R,L=2.$w[0].3F,1M;4(D){o=D.$p.2q();S=o.1w+D.$p.L();H=o.1i}v{a=$(2.q);o=a.2q();S=o.1w+2.u.2U;H=o.1i+a.T()+2.u.2V}4($.2s.2P){1b=$(1K).2P();4(1p<T){H=1b}v 4(1p+1b<H+T){4(D){1N=D.A.$w.2q();1M=D.A.$w[0].2R;4(T<=1M){H=1N.1i+1M-T}v{H=1N.1i}4(1p+1b<H+T){H-=H+T-(1p+1b)}}v{H-=H+T-(1p+1b)}}}4($.2s.2O){1o=$(1K).2O();4(2o+1o<S+L){4(D){S-=D.$p.L()+L;4(S<1o)S=1o}v{S-=S+L-(2o+1o)}}}2.$w.1e({1w:S,1i:H})},1r:5(e){4(2.M){2.W();2.Z()}v{$.C.1n();2.14(e)}},2k:5(a,b){8 c=2;2.17=2l(5(){a.1R(c);c.17=k},b)},1d:5(){4(2.17){3e(2.17);2.17=k}},1Y:5(a){8 i,I=0,1O=2.l.9,o=a||1;J(i=0;i<1O;i++){4(2.l[i].O){I=i;1h}}2.l[I].2j();3D{I+=o;4(I>=1O)I=0;v 4(I<0)I=1O-1}1y(2.l[I].1Q);2.l[I].15(P)},2C:5(){8 m=2;1y(m.N)m=m.N.A;s m.V?m:k},1I:5(){8 a,1k;2.W();4(!2.N)$(2.q).K(\'1a\').K(\'1E\').K(\'1F\');v 2.$w.K(\'1E\').K(\'1F\');1y(2.l.9){1k=2.l[0];1k.1I();2N 1k}4((a=$.18(2,g))>-1)g.1U(a,1);4(2.V){4((a=$.18(2,2.V.11))>-1)2.V.11.1U(a,1)}2.$w.2h()}}});$.R({1j:5(a,b){4(2g a==\'2e\')a={y:a};2.y=a.y||\'\';2.1D=a.1D||k;2.20=a.q||k;2.10=a.10||k;2.1B=a.1B||k;2.$p=k;2.A=k;2.x=k;2.u=$.R({},1P,b);2.O=Q;2.1l=P;2.1Q=Q;2.1s();4(a.x)19 $.C(2,a.x,b)}});$.R($.1j,{1X:{1s:5(){8 i,1C,y=2.y,2b=2;2.$p=$(2H.1A(1));4(2.10)2.$p[0].2K(\'Y\',2.10);4(2.u.2M&&2.1B)2.$p[0].3z=2.1B;4(y==\'\'){2.$p.10(\'1g-1Q\');2.1Q=P}v{1C=2g y==\'2e\';4(1C&&2.1D)y=$(\'<a 3y="\'+2.1D+\'"\'+(2.20?\'q="\'+2.20+\'"\':\'\')+\'>\'+y+\'</a>\');v 4(1C||!y.9)y=[y];J(i=0;i<y.9;i++){4(2g y[i]==\'2e\'){2a=2u.3w(\'3v\');2a.3t=y[i];2.$p[0].1u.1z(2a)}v 2.$p[0].1u.1z(y[i].1A(1))}}2.$p.1a(5(e){2b.1a(e,2)});2.23()},1a:5(e,a){4(2.1l&&2.u.1r)2.u.1r.1R(a,e,2)},23:5(){8 a=2;2.$p.1T(5(){a.15()},5(){a.2j()})},15:5(a){2.1d();8 i,1f=2.A.1c,D=2.A.l,2b=2;4(2.A.17)2.A.1d();4(!2.1l)s;J(i=0;i<D.9;i++){4(D[i].O)D[i].1S()}2.Z();G=2.A;J(i=0;i<1f.9;i++){4(1f[i].M&&1f[i]!=2.x&&!1f[i].17)1f[i].2k(5(){2.W()},1f[i].u.2Y)}4(2.x&&!a){2.x.2k(5(){2.14()},2.x.u.34)}},2j:5(){2.1d();4(!2.1l)s;4(!2.x||!2.x.M)2.1S()},1d:5(){4(2.x){2.x.1d()}},Z:5(){2.O=P;2.$p.10(\'O\');8 a=2.A.N;4(a&&!a.O)a.Z();12=2},1S:5(){2.O=Q;2.$p.1W(\'O\');4(2==12)12=k},3r:5(){2.$p.1W(\'2I\');2.1l=P},3u:5(){2.$p.10(\'2I\');2.1l=Q},1I:5(){2.1d();2.$p.2h();2.$p.K(\'1E\').K(\'1F\').K(\'1a\');4(2.x){2.x.1I();2N 2.x}2.A.3d(2)},3f:5(b){4(2.x)s;2.x=b;4(2.A&&$.18(b,2.A.1c)==-1)2.A.1c.16(b);4(2.u.2d){8 a=3g.1A(0);a.2K(\'y\',2.u.2d);2.$p[0].1u.1z(a)}}}});$.R($.2s,{28:5(c,d,e){8 f=5(a){8 b=[],1v,1G,U,$X,i,1H,3o,q,26=k;U=j(a,\'2G\');J(i=0;i<U.9;i++){1v=[];4(!U[i].1x.9){b.16(19 $.1j(\'\',c));3n}4((1H=h(U[i],\'2n\'))){1v=f(1H);$(1H).2h()}$X=$(U[i]);4($X[0].1x.9==1&&$X[0].1x[0].22==3)q=$X[0].1x[0].3m;v q=$X[0].1x;4(c&&c.2J)26=$X.3l(\'Y\');1G=19 $.1j({y:q,10:26},c);b.16(1G);4(1v.9)19 $.C(1G,1v,c)}s b};s 2.2z(5(){8 a,m;4(d||(a=h(2,\'2n\'))){a=d?$(d).3k(P)[0]:a;l=f(a);4(l.9){m=19 $.C(2,l,c);4(e)e.2y(m)}$(a).W()}})},2F:5(a){s 2.2z(5(){8 i,U=j(2,\'2G\');4(U.9){2E=19 $.25();J(i=0;i<U.9;i++)$(U[i]).28(a,k,2E)}})},1g:5(a,b){s 2.2z(5(){4(b&&b.2T==2S)19 $.C(2,b,a);v{4(2.21.2A()==\'2n\')$(2).2F(a);v $(2).28(a,b)}})}});8 h=5(a,b){4(!a)s k;8 n=a.1u;J(;n;n=n.2D){4(n.22==1&&n.21.2A()==b)s n}s k};8 j=5(a,b){4(!a)s[];8 r=[],n=a.1u;J(;n;n=n.2D){4(n.22==1&&n.21.2A()==b)r[r.9]=n}s r}})(3T);',62,242,'||this||if|function|||var|length|||||||||||null|menuItems||||eLI|target||return||settings|else|eDIV|subMenu|src||parentMenu|visibleMenus|Menu|pmi|div|mcm|activeMenu|posY|pos|for|unbind|width|visible|parentMenuItem|active|true|false|extend|posX|height|lis|menuCollection|hide|li|class|setActive|addClass|menus|activeItem||show|hoverIn|push|timer|inArray|new|click|wst|subMenus|removeTimer|css|pms|menu|break|top|MenuItem|item|enabled|case|closeAll|wsl|wh|eUL|onClick|init|asm|firstChild|subItems|left|childNodes|while|appendChild|cloneNode|data|isStr|url|mouseover|mouseout|menuItem|subUL|destroy|minWidth|window|openTimer|pheight|pmo|mil|defaults|separator|call|setInactive|hover|splice|rootDiv|removeClass|prototype|selectNextItem|position|urlTarget|nodeName|nodeType|bindHover|style|MenuCollection|classNames||menuFromElement|ul|elem|self|keyCode|arrowSrc|string|checkKey|typeof|remove|onClose|hoverOut|addTimer|setTimeout|checkMouse|UL|ww|hoverOpenDelay|offset|display|fn|parentNode|document|addItem|activetarget|instanceof|addMenu|each|toUpperCase|onOpen|inMenuCollection|nextSibling|bar|menuBarFromUL|LI|menuItemElement|disabled|copyClassAttr|setAttribute|menuULElement|addExpando|delete|scrollLeft|scrollTop|addItems|clientHeight|Array|constructor|offsetLeft|offsetTop|bind|none|hideDelay|browser|setPosition|200|visibility|index|showDelay|parseInt|||||keydown|mousedown|menuDIVElement|removeItem|clearTimeout|addSubMenu|arrowElement|absolute|40|relative|clone|attr|nodeValue|continue|submenu|body|innerbox|enable|appendTo|innerHTML|disable|span|createElement|outerbox|href|menuData|switch|filter|shadowbox3|do|shadowbox2|clientWidth|shadowbox1|version|msie|block|hidden|isNaN|root|id|append|arrow|img|setDefaults|41|jQuery'.split('|'),0,{}));

diff --git a/plugins/jquery.mousewheel.js b/plugins/jquery.mousewheel.js
new file mode 100644
index 0000000..b05af8c
--- /dev/null
+++ b/plugins/jquery.mousewheel.js
@@ -0,0 +1,135 @@
+/* Copyright (c) 2006 Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
+ * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
+ *
+ * $LastChangedDate: 2007-06-20 16:24:37 -0500 (Wed, 20 Jun 2007) $
+ * $Rev: 2124 $
+ *
+ * Version: 2.2
+ */
+
+(function($) {
+	
+$.fn.extend({
+	
+	/**
+	 * Apply the mousewheel event to the elements in the jQuery object.
+	 * The handler function should be prepared to take the event object
+	 * and a param called 'delta'. The 'delta' param is a number
+	 * either > 0 or < 0. > 0 = up and < 0 = down.
+	 *
+	 * The pageX, pageY, clientX and clientY event properties
+	 * are fixed in Firefox.
+	 *
+	 * @example $("p").mousewheel(function(event, delta){
+	 *   if (delta > 0)
+	 *     // do something on mousewheel scroll up
+	 *   else if (delta < 0)
+	 *     //do something on mousewheel scroll down
+	 * });
+	 *
+	 * @name mousewheel
+	 * @type jQuery
+	 * @param Function handler A function to call when onmousewheel fires. Should take two params: event and delta.
+	 * @cat Plugins/Mousewheel
+	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
+	 */
+	mousewheel: function(f) {
+		if (!f.guid) f.guid = $.event.guid++;
+		if (!$.event._mwCache) $.event._mwCache = [];
+		
+		return this.each( function() {
+			if (this._mwHandlers) return this._mwHandlers.push(f);
+			else this._mwHandlers = [];
+			
+			this._mwHandlers.push(f);
+			
+			var s = this;
+			
+			this._mwHandler = function(e) {
+				e = $.event.fix(e || window.event);
+				$.extend(e, this._mwCursorPos || {});
+				var delta = 0, returnValue = true;
+				
+				if (e.wheelDelta)  delta = e.wheelDelta/120;
+				if (e.detail)      delta = -e.detail/3;
+				if (window.opera)  delta = -e.wheelDelta;
+				
+				for (var i=0; i<s._mwHandlers.length; i++)
+					if (s._mwHandlers[i])
+						if ( s._mwHandlers[i].call(s, e, delta) === false ) {
+							returnValue = false;
+							e.preventDefault();
+							e.stopPropagation();
+						}
+				
+				return returnValue;
+			};
+			
+			if ($.browser.mozilla && !this._mwFixCursorPos) {
+				// fix pageX, pageY, clientX and clientY for mozilla
+				this._mwFixCursorPos = function(e) {
+					this._mwCursorPos = {
+						pageX: e.pageX,
+						pageY: e.pageY,
+						clientX: e.clientX,
+						clientY: e.clientY
+					};
+				};
+				$(this).bind('mousemove', this._mwFixCursorPos);
+			}
+			
+			if (this.addEventListener)
+				if ($.browser.mozilla) this.addEventListener('DOMMouseScroll', this._mwHandler, false);
+				else                   this.addEventListener('mousewheel',     this._mwHandler, false);
+			else
+				this.onmousewheel = this._mwHandler;
+			
+			$.event._mwCache.push( $(this) );
+		});
+	},
+	
+	/**
+	 * This method removes one or all applied mousewheel events from the elements.
+	 * You can remove a single handler function by passing it as the first param.
+	 * If you do not pass anything, it will remove all handlers.
+	 *
+	 * @name unmousewheel
+	 * @param Function handler The handler function to remove from the mousewheel event.
+	 * @type jQuery
+	 * @cat Plugins/Mousewheel
+	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
+	 */
+	unmousewheel: function(f) {
+		return this.each( function() {
+			if ( f && this._mwHandlers ) {
+				for (var i=0; i<this._mwHandlers.length; i++)
+					if (this._mwHandlers[i] && this._mwHandlers[i].guid == f.guid)
+						delete this._mwHandlers[i];
+			} else {
+				if ($.browser.mozilla && !this._mwFixCursorPos)
+					$(this).unbind('mousemove', this._mwFixCursorPos);
+					
+				if (this.addEventListener)
+					if ($.browser.mozilla) this.removeEventListener('DOMMouseScroll', this._mwHandler, false);
+					else                   this.removeEventListener('mousewheel',     this._mwHandler, false);
+				else
+					this.onmousewheel = null;
+					
+				this._mwHandlers = this._mwHandler = this._mwFixCursorPos = this._mwCursorPos = null;
+			}
+		});
+	}
+});
+
+// clean-up
+$(window)
+	.one('unload', function() {
+		var els = $.event._mwCache || [];
+		for (var i=0; i<els.length; i++)
+			els[i].unmousewheel();
+	});
+	
+})(jQuery);
+

diff --git a/plugins/jquery.mousewheel.pack.js b/plugins/jquery.mousewheel.pack.js
new file mode 100644
index 0000000..fc3af99
--- /dev/null
+++ b/plugins/jquery.mousewheel.pack.js
@@ -0,0 +1,12 @@
+/* Copyright (c) 2006 Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
+ * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
+ *
+ * $LastChangedDate: 2007-06-20 16:24:37 -0500 (Wed, 20 Jun 2007) $
+ * $Rev: 2124 $
+ *
+ * Version: 2.2
+ */
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(5($){$.V.x({t:5(f){2(!f.d)f.d=$.7.d++;2(!$.7.l)$.7.l=[];j 1.A(5(){2(1.4)j 1.4.n(f);8 1.4=[];1.4.n(f);c s=1;1.6=5(e){e=$.7.R(e||o.7);$.x(e,1.p||{});c a=0,m=P;2(e.u)a=e.u/M;2(e.H)a=-e.H/3;2(o.I)a=-e.u;r(c i=0;i<s.4.q;i++)2(s.4[i])2(s.4[i].U(s,e,a)===9){m=9;e.T();e.S()}j m};2($.k.h&&!1.b){1.b=5(e){1.p={F:e.F,E:e.E,D:e.D,C:e.C}};$(1).Q(\'B\',1.b)}2(1.g)2($.k.h)1.g(\'z\',1.6,9);8 1.g(\'t\',1.6,9);8 1.y=1.6;$.7.l.n($(1))})},v:5(f){j 1.A(5(){2(f&&1.4){r(c i=0;i<1.4.q;i++)2(1.4[i]&&1.4[i].d==f.d)O 1.4[i]}8{2($.k.h&&!1.b)$(1).N(\'B\',1.b);2(1.g)2($.k.h)1.w(\'z\',1.6,9);8 1.w(\'t\',1.6,9);8 1.y=G;1.4=1.6=1.b=1.p=G}})}});$(o).L(\'K\',5(){c a=$.7.l||[];r(c i=0;i<a.q;i++)a[i].v()})})(J);',58,58,'|this|if||_mwHandlers|function|_mwHandler|event|else|false||_mwFixCursorPos|var|guid|||addEventListener|mozilla||return|browser|_mwCache|returnValue|push|window|_mwCursorPos|length|for||mousewheel|wheelDelta|unmousewheel|removeEventListener|extend|onmousewheel|DOMMouseScroll|each|mousemove|clientY|clientX|pageY|pageX|null|detail|opera|jQuery|unload|one|120|unbind|delete|true|bind|fix|stopPropagation|preventDefault|call|fn'.split('|'),0,{}))
+

diff --git a/plugins/jquery.mustache.js b/plugins/jquery.mustache.js
new file mode 100644
index 0000000..d0fd93b
--- /dev/null
+++ b/plugins/jquery.mustache.js
@@ -0,0 +1,559 @@
+/*
+Shameless port of a shameless port
+@defunkt => @janl => @aq
+ 
+See http://github.com/defunkt/mustache for more info.
+*/
+ 
+;(function($) {
+
+/*!
+ * mustache.js - Logic-less {{mustache}} templates with JavaScript
+ * http://github.com/janl/mustache.js
+ */
+var Mustache = (typeof module !== "undefined" && module.exports) || {};
+
+(function (exports) {
+
+  exports.name = "mustache.js";
+  exports.version = "0.5.0-dev";
+  exports.tags = ["{{", "}}"];
+  exports.parse = parse;
+  exports.compile = compile;
+  exports.render = render;
+  exports.clearCache = clearCache;
+
+  // This is here for backwards compatibility with 0.4.x.
+  exports.to_html = function (template, view, partials, send) {
+    var result = render(template, view, partials);
+
+    if (typeof send === "function") {
+      send(result);
+    } else {
+      return result;
+    }
+  };
+
+  var _toString = Object.prototype.toString;
+  var _isArray = Array.isArray;
+  var _forEach = Array.prototype.forEach;
+  var _trim = String.prototype.trim;
+
+  var isArray;
+  if (_isArray) {
+    isArray = _isArray;
+  } else {
+    isArray = function (obj) {
+      return _toString.call(obj) === "[object Array]";
+    };
+  }
+
+  var forEach;
+  if (_forEach) {
+    forEach = function (obj, callback, scope) {
+      return _forEach.call(obj, callback, scope);
+    };
+  } else {
+    forEach = function (obj, callback, scope) {
+      for (var i = 0, len = obj.length; i < len; ++i) {
+        callback.call(scope, obj[i], i, obj);
+      }
+    };
+  }
+
+  var spaceRe = /^\s*$/;
+
+  function isWhitespace(string) {
+    return spaceRe.test(string);
+  }
+
+  var trim;
+  if (_trim) {
+    trim = function (string) {
+      return string == null ? "" : _trim.call(string);
+    };
+  } else {
+    var trimLeft, trimRight;
+
+    if (isWhitespace("\xA0")) {
+      trimLeft = /^\s+/;
+      trimRight = /\s+$/;
+    } else {
+      // IE doesn't match non-breaking spaces with \s, thanks jQuery.
+      trimLeft = /^[\s\xA0]+/;
+      trimRight = /[\s\xA0]+$/;
+    }
+
+    trim = function (string) {
+      return string == null ? "" :
+        String(string).replace(trimLeft, "").replace(trimRight, "");
+    };
+  }
+
+  var escapeMap = {
+    "&": "&amp;",
+    "<": "&lt;",
+    ">": "&gt;",
+    '"': '&quot;',
+    "'": '&#39;'
+  };
+
+  function escapeHTML(string) {
+    return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
+      return escapeMap[s] || s;
+    });
+  }
+
+  /**
+   * Adds the `template`, `line`, and `file` properties to the given error
+   * object and alters the message to provide more useful debugging information.
+   */
+  function debug(e, template, line, file) {
+    file = file || "<template>";
+
+    var lines = template.split("\n"),
+        start = Math.max(line - 3, 0),
+        end = Math.min(lines.length, line + 3),
+        context = lines.slice(start, end);
+
+    var c;
+    for (var i = 0, len = context.length; i < len; ++i) {
+      c = i + start + 1;
+      context[i] = (c === line ? " >> " : "    ") + context[i];
+    }
+
+    e.template = template;
+    e.line = line;
+    e.file = file;
+    e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n");
+
+    return e;
+  }
+
+  /**
+   * Looks up the value of the given `name` in the given context `stack`.
+   */
+  function lookup(name, stack, defaultValue) {
+    if (name === ".") {
+      return stack[stack.length - 1];
+    }
+
+    var names = name.split(".");
+    var lastIndex = names.length - 1;
+    var target = names[lastIndex];
+
+    var value, context, i = stack.length, j, localStack;
+    while (i) {
+      localStack = stack.slice(0);
+      context = stack[--i];
+
+      j = 0;
+      while (j < lastIndex) {
+        context = context[names[j++]];
+
+        if (context == null) {
+          break;
+        }
+
+        localStack.push(context);
+      }
+
+      if (context && typeof context === "object" && target in context) {
+        value = context[target];
+        break;
+      }
+    }
+
+    // If the value is a function, call it in the current context.
+    if (typeof value === "function") {
+      value = value.call(localStack[localStack.length - 1]);
+    }
+
+    if (value == null)  {
+      return defaultValue;
+    }
+
+    return value;
+  }
+
+  function renderSection(name, stack, callback, inverted) {
+    var buffer = "";
+    var value =  lookup(name, stack);
+
+    if (inverted) {
+      // From the spec: inverted sections may render text once based on the
+      // inverse value of the key. That is, they will be rendered if the key
+      // doesn't exist, is false, or is an empty list.
+      if (value == null || value === false || (isArray(value) && value.length === 0)) {
+        buffer += callback();
+      }
+    } else if (isArray(value)) {
+      forEach(value, function (value) {
+        stack.push(value);
+        buffer += callback();
+        stack.pop();
+      });
+    } else if (typeof value === "object") {
+      stack.push(value);
+      buffer += callback();
+      stack.pop();
+    } else if (typeof value === "function") {
+      var scope = stack[stack.length - 1];
+      var scopedRender = function (template) {
+        return render(template, scope);
+      };
+      buffer += value.call(scope, callback(), scopedRender) || "";
+    } else if (value) {
+      buffer += callback();
+    }
+
+    return buffer;
+  }
+
+  /**
+   * Parses the given `template` and returns the source of a function that,
+   * with the proper arguments, will render the template. Recognized options
+   * include the following:
+   *
+   *   - file     The name of the file the template comes from (displayed in
+   *              error messages)
+   *   - tags     An array of open and close tags the `template` uses. Defaults
+   *              to the value of Mustache.tags
+   *   - debug    Set `true` to log the body of the generated function to the
+   *              console
+   *   - space    Set `true` to preserve whitespace from lines that otherwise
+   *              contain only a {{tag}}. Defaults to `false`
+   */
+  function parse(template, options) {
+    options = options || {};
+
+    var tags = options.tags || exports.tags,
+        openTag = tags[0],
+        closeTag = tags[tags.length - 1];
+
+    var code = [
+      'var buffer = "";', // output buffer
+      "\nvar line = 1;", // keep track of source line number
+      "\ntry {",
+      '\nbuffer += "'
+    ];
+
+    var spaces = [],      // indices of whitespace in code on the current line
+        hasTag = false,   // is there a {{tag}} on the current line?
+        nonSpace = false; // is there a non-space char on the current line?
+
+    // Strips all space characters from the code array for the current line
+    // if there was a {{tag}} on it and otherwise only spaces.
+    var stripSpace = function () {
+      if (hasTag && !nonSpace && !options.space) {
+        while (spaces.length) {
+          code.splice(spaces.pop(), 1);
+        }
+      } else {
+        spaces = [];
+      }
+
+      hasTag = false;
+      nonSpace = false;
+    };
+
+    var sectionStack = [], updateLine, nextOpenTag, nextCloseTag;
+
+    var setTags = function (source) {
+      tags = trim(source).split(/\s+/);
+      nextOpenTag = tags[0];
+      nextCloseTag = tags[tags.length - 1];
+    };
+
+    var includePartial = function (source) {
+      code.push(
+        '";',
+        updateLine,
+        '\nvar partial = partials["' + trim(source) + '"];',
+        '\nif (partial) {',
+        '\n  buffer += render(partial,stack[stack.length - 1],partials);',
+        '\n}',
+        '\nbuffer += "'
+      );
+    };
+
+    var openSection = function (source, inverted) {
+      var name = trim(source);
+
+      if (name === "") {
+        throw debug(new Error("Section name may not be empty"), template, line, options.file);
+      }
+
+      sectionStack.push({name: name, inverted: inverted});
+
+      code.push(
+        '";',
+        updateLine,
+        '\nvar name = "' + name + '";',
+        '\nvar callback = (function () {',
+        '\n  return function () {',
+        '\n    var buffer = "";',
+        '\nbuffer += "'
+      );
+    };
+
+    var openInvertedSection = function (source) {
+      openSection(source, true);
+    };
+
+    var closeSection = function (source) {
+      var name = trim(source);
+      var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name;
+
+      if (!openName || name != openName) {
+        throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file);
+      }
+
+      var section = sectionStack.pop();
+
+      code.push(
+        '";',
+        '\n    return buffer;',
+        '\n  };',
+        '\n})();'
+      );
+
+      if (section.inverted) {
+        code.push("\nbuffer += renderSection(name,stack,callback,true);");
+      } else {
+        code.push("\nbuffer += renderSection(name,stack,callback);");
+      }
+
+      code.push('\nbuffer += "');
+    };
+
+    var sendPlain = function (source) {
+      code.push(
+        '";',
+        updateLine,
+        '\nbuffer += lookup("' + trim(source) + '",stack,"");',
+        '\nbuffer += "'
+      );
+    };
+
+    var sendEscaped = function (source) {
+      code.push(
+        '";',
+        updateLine,
+        '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));',
+        '\nbuffer += "'
+      );
+    };
+
+    var line = 1, c, callback;
+    for (var i = 0, len = template.length; i < len; ++i) {
+      if (template.slice(i, i + openTag.length) === openTag) {
+        i += openTag.length;
+        c = template.substr(i, 1);
+        updateLine = '\nline = ' + line + ';';
+        nextOpenTag = openTag;
+        nextCloseTag = closeTag;
+        hasTag = true;
+
+        switch (c) {
+        case "!": // comment
+          i++;
+          callback = null;
+          break;
+        case "=": // change open/close tags, e.g. {{=<% %>=}}
+          i++;
+          closeTag = "=" + closeTag;
+          callback = setTags;
+          break;
+        case ">": // include partial
+          i++;
+          callback = includePartial;
+          break;
+        case "#": // start section
+          i++;
+          callback = openSection;
+          break;
+        case "^": // start inverted section
+          i++;
+          callback = openInvertedSection;
+          break;
+        case "/": // end section
+          i++;
+          callback = closeSection;
+          break;
+        case "{": // plain variable
+          closeTag = "}" + closeTag;
+          // fall through
+        case "&": // plain variable
+          i++;
+          nonSpace = true;
+          callback = sendPlain;
+          break;
+        default: // escaped variable
+          nonSpace = true;
+          callback = sendEscaped;
+        }
+
+        var end = template.indexOf(closeTag, i);
+
+        if (end === -1) {
+          throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file);
+        }
+
+        var source = template.substring(i, end);
+
+        if (callback) {
+          callback(source);
+        }
+
+        // Maintain line count for \n in source.
+        var n = 0;
+        while (~(n = source.indexOf("\n", n))) {
+          line++;
+          n++;
+        }
+
+        i = end + closeTag.length - 1;
+        openTag = nextOpenTag;
+        closeTag = nextCloseTag;
+      } else {
+        c = template.substr(i, 1);
+
+        switch (c) {
+        case '"':
+        case "\\":
+          nonSpace = true;
+          code.push("\\" + c);
+          break;
+        case "\r":
+          // Ignore carriage returns.
+          break;
+        case "\n":
+          spaces.push(code.length);
+          code.push("\\n");
+          stripSpace(); // Check for whitespace on the current line.
+          line++;
+          break;
+        default:
+          if (isWhitespace(c)) {
+            spaces.push(code.length);
+          } else {
+            nonSpace = true;
+          }
+
+          code.push(c);
+        }
+      }
+    }
+
+    if (sectionStack.length != 0) {
+      throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file);
+    }
+
+    // Clean up any whitespace from a closing {{tag}} that was at the end
+    // of the template without a trailing \n.
+    stripSpace();
+
+    code.push(
+      '";',
+      "\nreturn buffer;",
+      "\n} catch (e) { throw {error: e, line: line}; }"
+    );
+
+    // Ignore `buffer += "";` statements.
+    var body = code.join("").replace(/buffer \+= "";\n/g, "");
+
+    if (options.debug) {
+      if (typeof console != "undefined" && console.log) {
+        console.log(body);
+      } else if (typeof print === "function") {
+        print(body);
+      }
+    }
+
+    return body;
+  }
+
+  /**
+   * Used by `compile` to generate a reusable function for the given `template`.
+   */
+  function _compile(template, options) {
+    var args = "view,partials,stack,lookup,escapeHTML,renderSection,render";
+    var body = parse(template, options);
+    var fn = new Function(args, body);
+
+    // This anonymous function wraps the generated function so we can do
+    // argument coercion, setup some variables, and handle any errors
+    // encountered while executing it.
+    return function (view, partials) {
+      partials = partials || {};
+
+      var stack = [view]; // context stack
+
+      try {
+        return fn(view, partials, stack, lookup, escapeHTML, renderSection, render);
+      } catch (e) {
+        throw debug(e.error, template, e.line, options.file);
+      }
+    };
+  }
+
+  // Cache of pre-compiled templates.
+  var _cache = {};
+
+  /**
+   * Clear the cache of compiled templates.
+   */
+  function clearCache() {
+    _cache = {};
+  }
+
+  /**
+   * Compiles the given `template` into a reusable function using the given
+   * `options`. In addition to the options accepted by Mustache.parse,
+   * recognized options include the following:
+   *
+   *   - cache    Set `false` to bypass any pre-compiled version of the given
+   *              template. Otherwise, a given `template` string will be cached
+   *              the first time it is parsed
+   */
+  function compile(template, options) {
+    options = options || {};
+
+    // Use a pre-compiled version from the cache if we have one.
+    if (options.cache !== false) {
+      if (!_cache[template]) {
+        _cache[template] = _compile(template, options);
+      }
+
+      return _cache[template];
+    }
+
+    return _compile(template, options);
+  }
+
+  /**
+   * High-level function that renders the given `template` using the given
+   * `view` and `partials`. If you need to use any of the template options (see
+   * `compile` above), you must compile in a separate step, and then call that
+   * compiled function.
+   */
+  function render(template, view, partials) {
+    return compile(template)(view, partials);
+  }
+
+})(Mustache);
+
+  $.mustache = function (template, view, partials) {
+    return Mustache.render(template, view, partials);
+  };
+
+  $.fn.mustache = function (view, partials) {
+    return $(this).map(function (i, elm) {
+      var template = $(elm).html().trim();
+      var output = $.mustache(template, view, partials);
+      return $(output).get();
+    });
+  };
+
+})(jQuery);
diff --git a/plugins/jquery.tableHeader.js b/plugins/jquery.tableHeader.js
new file mode 100644
index 0000000..1aad945
--- /dev/null
+++ b/plugins/jquery.tableHeader.js
@@ -0,0 +1,152 @@
+// +--------------------------------------------------------------------+
+// | CiviCRM version 4.2                                                |
+// +--------------------------------------------------------------------+
+// | Copyright CiviCRM LLC (c) 2004-2012                                |
+// +--------------------------------------------------------------------+
+// | This file is a part of CiviCRM.                                    |
+// |                                                                    |
+// | CiviCRM is free software; you can copy, modify, and distribute it  |
+// | under the terms of the GNU Affero General Public License           |
+// | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+// |                                                                    |
+// | CiviCRM 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 Affero General Public License for more details.        |
+// |                                                                    |
+// | You should have received a copy of the GNU Affero General Public   |
+// | License and the CiviCRM Licensing Exception along                  |
+// | with this program; if not, contact CiviCRM LLC                     |
+// | at info[AT]civicrm[DOT]org. If you have questions about the        |
+// | GNU Affero General Public License or the licensing of CiviCRM,     |
+// | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+// +--------------------------------------------------------------------+
+(function($) {
+
+// Keep track of all header cells.
+var cells = [];
+
+// Attach to all headers.
+$(document).ready(function() {
+  $('table thead.sticky').each(function () {
+    // Make all absolute positioned elements relative to the table.
+    var height = $(this).parent('table').css('position', 'relative').height();
+    
+    // Find all header cells.
+    $('th', this).each(function () {
+      
+      // Ensure each cell has an element in it.
+      var html = $(this).html();
+      if (html == ' ') {
+	html = '&nbsp;';
+      }
+      if ($(this).children().size() == 0) {
+	html = '<span>'+ html +'</span>';
+      }
+      
+      // Clone and wrap cell contents in sticky wrapper that overlaps the cell's padding.
+      $('<div class="sticky-header" style="position: fixed; display: none; top: 0px;">'+ html +'</div>').prependTo(this);
+      var div = $('div.sticky-header', this).css({
+	  'marginLeft': '-'+ $(this).css('paddingLeft'),
+          'marginRight': '-'+ $(this).css('paddingRight'),
+          'paddingLeft': $(this).css('paddingLeft'),
+          'paddingTop': $(this).css('paddingTop'),
+          'paddingRight': $(this).css('paddingRight'),
+          'paddingBottom': $(this).css('paddingBottom')
+	})[0];
+      // Adjust width to fit cell and hide.
+      
+      //CRM-6467
+      var length = $(this).width() - $(div).width();
+      if ( length < 0 ) length = $(div).width() - $(this).width();
+      $(div).css('paddingRight', parseInt($(div).css('paddingRight')) + length +'px');
+
+      cells.push(div);
+      
+      // Get position.
+      div.cell = this;
+      div.table = $(this).parent('table')[0];
+      div.stickyMax = height;
+      div.stickyPosition = $(this).y();
+    });
+  });
+});
+
+// Track scrolling.
+var scroll = function() {
+  $(cells).each(function () {
+    // Fetch scrolling position.
+    var scroll = document.documentElement.scrollTop || document.body.scrollTop;
+    var offset = scroll - this.stickyPosition - 4;
+    if (offset > 0 && offset < this.stickyMax - 100) {
+      $(this).css({display:'block'});
+    }
+    else {
+      $(this).css('display', 'none');
+    }
+  });
+};
+$(window).scroll(scroll);
+$(document.documentElement).scroll(scroll);
+
+// Track resizing.
+var resize = function () {
+  $(cells).each(function () {
+    // Get position.
+    $(this).css({ 'position': 'relative', 'top': '0'});
+    this.stickyPosition = $(this.cell).y();
+    this.stickyMax = $(this.table).height();
+  });
+};
+$(window).resize(resize);
+
+// Track the element positions
+$.fn.x = function(n) {
+  var result = null;
+  this.each(function() {
+    var o = this;
+    if (n === undefined) {
+      var x = 0;
+      if (o.offsetParent) {
+	while (o.offsetParent) {
+	  x += o.offsetLeft;
+	  o = o.offsetParent;
+	}
+      }
+      if (result === null) {
+	result = x;
+      } else {
+	result = Math.min(result, x);
+      }
+    } else {
+      o.style.left = n + 'px';
+    }
+  });
+  return result;
+};
+
+$.fn.y = function(n) {
+  var result = null;
+  this.each(function() {
+    var o = this;
+    if (n === undefined) {
+      var y = 0;
+      if (o.offsetParent) {
+	while (o.offsetParent) {
+	  y += o.offsetTop;
+	  o = o.offsetParent;
+	}
+      }
+      if (result === null) {
+	result = y;
+      } else {
+	result = Math.min(result, y);
+      }
+    } else {
+      o.style.top = n + 'px';
+    }
+  });
+  return result;
+};
+
+})(jQuery);

diff --git a/plugins/jquery.timeentry.js b/plugins/jquery.timeentry.js
new file mode 100644
index 0000000..1dbc245
--- /dev/null
+++ b/plugins/jquery.timeentry.js
@@ -0,0 +1,976 @@
+/* http://keith-wood.name/timeEntry.html
+   Time entry for jQuery v1.4.9.
+   Written by Keith Wood (kbwood{at}iinet.com.au) June 2007.
+   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
+   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
+   Please attribute the author if you use it. */
+
+/* Turn an input field into an entry point for a time value.
+   The time can be entered via directly typing the value,
+   via the arrow keys, or via spinner buttons.
+   It is configurable to show 12 or 24-hour time, to show or hide seconds,
+   to enforce a minimum and/or maximum time, to change the spinner image,
+   and to constrain the time to steps, e.g. only on the quarter hours.
+   Attach it with $('input selector').timeEntry(); for default settings,
+   or configure it with options like:
+   $('input selector').timeEntry(
+      {spinnerImage: 'spinnerSquare.png', spinnerSize: [20, 20, 0]}); */
+
+(function($) { // Hide scope, no $ conflict
+
+/* TimeEntry manager.
+   Use the singleton instance of this class, $.timeEntry, to interact with the time entry
+   functionality. Settings for (groups of) fields are maintained in an instance object
+   (TimeEntryInstance), allowing multiple different settings on the same page. */
+function TimeEntry() {
+	this._disabledInputs = []; // List of time entry inputs that have been disabled
+	this.regional = []; // Available regional settings, indexed by language code
+	this.regional[''] = { // Default regional settings
+		show24Hours: false, // True to use 24 hour time, false for 12 hour (AM/PM)
+		separator: ':', // The separator between time fields
+		ampmPrefix: '', // The separator before the AM/PM text
+		ampmNames: ['AM', 'PM'], // Names of morning/evening markers
+		spinnerTexts: ['Now', 'Previous field', 'Next field', 'Increment', 'Decrement']
+		// The popup texts for the spinner image areas
+	};
+	this._defaults = {
+		appendText: '', // Display text following the input box, e.g. showing the format
+		showSeconds: false, // True to show seconds as well, false for hours/minutes only
+		timeSteps: [1, 1, 1], // Steps for each of hours/minutes/seconds when incrementing/decrementing
+		initialField: 0, // The field to highlight initially, 0 = hours, 1 = minutes, ...
+		useMouseWheel: true, // True to use mouse wheel for increment/decrement if possible,
+			// false to never use it
+		defaultTime: null, // The time to use if none has been set, leave at null for now
+		minTime: null, // The earliest selectable time, or null for no limit
+		maxTime: null, // The latest selectable time, or null for no limit
+		spinnerImage: 'spinnerDefault.png', // The URL of the images to use for the time spinner
+			// Seven images packed horizontally for normal, each button pressed, and disabled
+		spinnerSize: [20, 20, 8], // The width and height of the spinner image,
+			// and size of centre button for current time
+		spinnerBigImage: '', // The URL of the images to use for the expanded time spinner
+			// Seven images packed horizontally for normal, each button pressed, and disabled
+		spinnerBigSize: [40, 40, 16], // The width and height of the expanded spinner image,
+			// and size of centre button for current time
+		spinnerIncDecOnly: false, // True for increment/decrement buttons only, false for all
+		spinnerRepeat: [500, 250], // Initial and subsequent waits in milliseconds
+			// for repeats on the spinner buttons
+		beforeShow: null, // Function that takes an input field and
+			// returns a set of custom settings for the time entry
+		beforeSetTime: null // Function that runs before updating the time,
+			// takes the old and new times, and minimum and maximum times as parameters,
+			// and returns an adjusted time if necessary
+	};
+	$.extend(this._defaults, this.regional['']);
+}
+
+var PROP_NAME = 'timeEntry';
+
+$.extend(TimeEntry.prototype, {
+	/* Class name added to elements to indicate already configured with time entry. */
+	markerClassName: 'hasTimeEntry',
+
+	/* Override the default settings for all instances of the time entry.
+	   @param  options  (object) the new settings to use as defaults (anonymous object)
+	   @return  (DateEntry) this object */
+	setDefaults: function(options) {
+		extendRemove(this._defaults, options || {});
+		return this;
+	},
+
+	/* Attach the time entry handler to an input field.
+	   @param  target   (element) the field to attach to
+	   @param  options  (object) custom settings for this instance */
+	_connectTimeEntry: function(target, options) {
+		var input = $(target);
+		if (input.hasClass(this.markerClassName)) {
+			return;
+		}
+		var inst = {};
+		inst.options = $.extend({}, options);
+		inst._selectedHour = 0; // The currently selected hour
+		inst._selectedMinute = 0; // The currently selected minute
+		inst._selectedSecond = 0; // The currently selected second
+		inst._field = 0; // The selected subfield
+		inst.input = $(target); // The attached input field
+		$.data(target, PROP_NAME, inst);
+		var spinnerImage = this._get(inst, 'spinnerImage');
+		var spinnerText = this._get(inst, 'spinnerText');
+		var spinnerSize = this._get(inst, 'spinnerSize');
+		var appendText = this._get(inst, 'appendText');
+		var spinner = (!spinnerImage ? null : 
+			$('<span class="timeEntry_control" style="display: inline-block; ' +
+			'background: url(\'' + spinnerImage + '\') 0 0 no-repeat; ' +
+			'width: ' + spinnerSize[0] + 'px; height: ' + spinnerSize[1] + 'px;' +
+			($.browser.mozilla && $.browser.version < '1.9' ? // FF 2- (Win)
+			' padding-left: ' + spinnerSize[0] + 'px; padding-bottom: ' +
+			(spinnerSize[1] - 18) + 'px;' : '') + '"></span>'));
+		input.wrap('<span class="timeEntry_wrap"></span>').
+			after(appendText ? '<span class="timeEntry_append">' + appendText + '</span>' : '').
+			after(spinner || '');
+		input.addClass(this.markerClassName).bind('focus.timeEntry', this._doFocus).
+			bind('blur.timeEntry', this._doBlur).bind('click.timeEntry', this._doClick).
+			bind('keydown.timeEntry', this._doKeyDown).bind('keypress.timeEntry', this._doKeyPress);
+		// Check pastes
+		if ($.browser.mozilla) {
+			input.bind('input.timeEntry', function(event) { $.timeEntry._parseTime(inst); });
+		}
+		if ($.browser.msie) {
+			input.bind('paste.timeEntry', 
+				function(event) { setTimeout(function() { $.timeEntry._parseTime(inst); }, 1); });
+		}
+		// Allow mouse wheel usage
+		if (this._get(inst, 'useMouseWheel') && $.fn.mousewheel) {
+			input.mousewheel(this._doMouseWheel);
+		}
+		if (spinner) {
+			spinner.mousedown(this._handleSpinner).mouseup(this._endSpinner).
+				mouseover(this._expandSpinner).mouseout(this._endSpinner).
+				mousemove(this._describeSpinner);
+		}
+	},
+
+	/* Enable a time entry input and any associated spinner.
+	   @param  input  (element) single input field */
+	_enableTimeEntry: function(input) {
+		this._enableDisable(input, false);
+	},
+
+	/* Disable a time entry input and any associated spinner.
+	   @param  input  (element) single input field */
+	_disableTimeEntry: function(input) {
+		this._enableDisable(input, true);
+	},
+
+	/* Enable or disable a time entry input and any associated spinner.
+	   @param  input    (element) single input field
+	   @param  disable  (boolean) true to disable, false to enable */
+	_enableDisable: function(input, disable) {
+		var inst = $.data(input, PROP_NAME);
+		if (!inst) {
+			return;
+		}
+		input.disabled = disable;
+		if (input.nextSibling && input.nextSibling.nodeName.toLowerCase() == 'span') {
+			$.timeEntry._changeSpinner(inst, input.nextSibling, (disable ? 5 : -1));
+		}
+		$.timeEntry._disabledInputs = $.map($.timeEntry._disabledInputs,
+			function(value) { return (value == input ? null : value); }); // Delete entry
+		if (disable) {
+			$.timeEntry._disabledInputs.push(input);
+		}
+	},
+
+	/* Check whether an input field has been disabled.
+	   @param  input  (element) input field to check
+	   @return  (boolean) true if this field has been disabled, false if it is enabled */
+	_isDisabledTimeEntry: function(input) {
+		return $.inArray(input, this._disabledInputs) > -1;
+	},
+
+	/* Reconfigure the settings for a time entry field.
+	   @param  input    (element) input field to change
+	   @param  options  (object) new settings to add or
+	                    (string) an individual setting name
+	   @param  value    (any) the individual setting's value */
+	_changeTimeEntry: function(input, options, value) {
+		var inst = $.data(input, PROP_NAME);
+		if (inst) {
+			if (typeof options == 'string') {
+				var name = options;
+				options = {};
+				options[name] = value;
+			}
+			var currentTime = this._extractTime(inst);
+			extendRemove(inst.options, options || {});
+			if (currentTime) {
+				this._setTime(inst, new Date(0, 0, 0,
+					currentTime[0], currentTime[1], currentTime[2]));
+			}
+		}
+		$.data(input, PROP_NAME, inst);
+	},
+
+	/* Remove the time entry functionality from an input.
+	   @param  input  (element) input field to affect */
+	_destroyTimeEntry: function(input) {
+		$input = $(input);
+		if (!$input.hasClass(this.markerClassName)) {
+			return;
+		}
+		$input.removeClass(this.markerClassName).unbind('.timeEntry');
+		if ($.fn.mousewheel) {
+			$input.unmousewheel();
+		}
+		this._disabledInputs = $.map(this._disabledInputs,
+			function(value) { return (value == input ? null : value); }); // Delete entry
+		$input.parent().replaceWith($input);
+		$.removeData(input, PROP_NAME);
+	},
+
+	/* Initialise the current time for a time entry input field.
+	   @param  input  (element) input field to update
+	   @param  time   (Date) the new time (year/month/day ignored) or null for now */
+	_setTimeTimeEntry: function(input, time) {
+		var inst = $.data(input, PROP_NAME);
+		if (inst) {
+			if (time === null || time === '') {
+				inst.input.val('');
+			}
+			else {
+				this._setTime(inst, time ? (typeof time == 'object' ?
+					new Date(time.getTime()) : time) : null);
+			}
+		}
+	},
+
+	/* Retrieve the current time for a time entry input field.
+	   @param  input  (element) input field to examine
+	   @return  (Date) current time (year/month/day zero) or null if none */
+	_getTimeTimeEntry: function(input) {
+		var inst = $.data(input, PROP_NAME);
+		var currentTime = (inst ? this._extractTime(inst) : null);
+		return (!currentTime ? null :
+			new Date(0, 0, 0, currentTime[0], currentTime[1], currentTime[2]));
+	},
+
+	/* Retrieve the millisecond offset for the current time.
+	   @param  input  (element) input field to examine
+	   @return  (number) the time as milliseconds offset or zero if none */
+	_getOffsetTimeEntry: function(input) {
+		var inst = $.data(input, PROP_NAME);
+		var currentTime = (inst ? this._extractTime(inst) : null);
+		return (!currentTime ? 0 :
+			(currentTime[0] * 3600 + currentTime[1] * 60 + currentTime[2]) * 1000);
+	},
+
+	/* Initialise time entry.
+	   @param  target  (element) the input field or
+	                   (event) the focus event */
+	_doFocus: function(target) {
+		var input = (target.nodeName && target.nodeName.toLowerCase() == 'input' ? target : this);
+		if ($.timeEntry._lastInput == input || $.timeEntry._isDisabledTimeEntry(input)) {
+			$.timeEntry._focussed = false;
+			return;
+		}
+		var inst = $.data(input, PROP_NAME);
+		$.timeEntry._focussed = true;
+		$.timeEntry._lastInput = input;
+		$.timeEntry._blurredInput = null;
+		var beforeShow = $.timeEntry._get(inst, 'beforeShow');
+		extendRemove(inst.options, (beforeShow ? beforeShow.apply(input, [input]) : {}));
+		$.data(input, PROP_NAME, inst);
+		$.timeEntry._parseTime(inst);
+		setTimeout(function() { $.timeEntry._showField(inst); }, 10);
+	},
+
+	/* Note that the field has been exited.
+	   @param  event  (event) the blur event */
+	_doBlur: function(event) {
+		$.timeEntry._blurredInput = $.timeEntry._lastInput;
+		$.timeEntry._lastInput = null;
+	},
+
+	/* Select appropriate field portion on click, if already in the field.
+	   @param  event  (event) the click event */
+	_doClick: function(event) {
+		var input = event.target;
+		var inst = $.data(input, PROP_NAME);
+		if (!$.timeEntry._focussed) {
+			var fieldSize = $.timeEntry._get(inst, 'separator').length + 2;
+			inst._field = 0;
+			if (input.selectionStart != null) { // Use input select range
+				for (var field = 0; field <= Math.max(1, inst._secondField, inst._ampmField); field++) {
+					var end = (field != inst._ampmField ? (field * fieldSize) + 2 :
+						(inst._ampmField * fieldSize) + $.timeEntry._get(inst, 'ampmPrefix').length +
+						$.timeEntry._get(inst, 'ampmNames')[0].length);
+					inst._field = field;
+					if (input.selectionStart < end) {
+						break;
+					}
+				}
+			}
+			else if (input.createTextRange) { // Check against bounding boxes
+				var src = $(event.srcElement);
+				var range = input.createTextRange();
+				var convert = function(value) {
+					return {thin: 2, medium: 4, thick: 6}[value] || value;
+				};
+				var offsetX = event.clientX + document.documentElement.scrollLeft -
+					(src.offset().left + parseInt(convert(src.css('border-left-width')), 10)) -
+					range.offsetLeft; // Position - left edge - alignment
+				for (var field = 0; field <= Math.max(1, inst._secondField, inst._ampmField); field++) {
+					var end = (field != inst._ampmField ? (field * fieldSize) + 2 :
+						(inst._ampmField * fieldSize) + $.timeEntry._get(inst, 'ampmPrefix').length +
+						$.timeEntry._get(inst, 'ampmNames')[0].length);
+					range.collapse();
+					range.moveEnd('character', end);
+					inst._field = field;
+					if (offsetX < range.boundingWidth) { // And compare
+						break;
+					}
+				}
+			}
+		}
+		$.data(input, PROP_NAME, inst);
+		$.timeEntry._showField(inst);
+		$.timeEntry._focussed = false;
+	},
+
+	/* Handle keystrokes in the field.
+	   @param  event  (event) the keydown event
+	   @return  (boolean) true to continue, false to stop processing */
+	_doKeyDown: function(event) {
+		if (event.keyCode >= 48) { // >= '0'
+			return true;
+		}
+		var inst = $.data(event.target, PROP_NAME);
+		switch (event.keyCode) {
+			case 9: return (event.shiftKey ?
+						// Move to previous time field, or out if at the beginning
+						$.timeEntry._changeField(inst, -1, true) :
+						// Move to next time field, or out if at the end
+						$.timeEntry._changeField(inst, +1, true));
+			case 35: if (event.ctrlKey) { // Clear time on ctrl+end
+						$.timeEntry._setValue(inst, '');
+					}
+					else { // Last field on end
+						inst._field = Math.max(1, inst._secondField, inst._ampmField);
+						$.timeEntry._adjustField(inst, 0);
+					}
+					break;
+			case 36: if (event.ctrlKey) { // Current time on ctrl+home
+						$.timeEntry._setTime(inst);
+					}
+					else { // First field on home
+						inst._field = 0;
+						$.timeEntry._adjustField(inst, 0);
+					}
+					break;
+			case 37: $.timeEntry._changeField(inst, -1, false); break; // Previous field on left
+			case 38: $.timeEntry._adjustField(inst, +1); break; // Increment time field on up
+			case 39: $.timeEntry._changeField(inst, +1, false); break; // Next field on right
+			case 40: $.timeEntry._adjustField(inst, -1); break; // Decrement time field on down
+			case 46: $.timeEntry._setValue(inst, ''); break; // Clear time on delete
+		}
+		return false;
+	},
+
+	/* Disallow unwanted characters.
+	   @param  event  (event) the keypress event
+	   @return  (boolean) true to continue, false to stop processing */
+	_doKeyPress: function(event) {
+		var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
+		if (chr < ' ') {
+			return true;
+		}
+		var inst = $.data(event.target, PROP_NAME);
+		$.timeEntry._handleKeyPress(inst, chr);
+		return false;
+	},
+
+	/* Increment/decrement on mouse wheel activity.
+	   @param  event  (event) the mouse wheel event
+	   @param  delta  (number) the amount of change */
+	_doMouseWheel: function(event, delta) {
+		if ($.timeEntry._isDisabledTimeEntry(event.target)) {
+			return;
+		}
+		delta = ($.browser.opera ? -delta / Math.abs(delta) :
+			($.browser.safari ? delta / Math.abs(delta) : delta));
+		var inst = $.data(event.target, PROP_NAME);
+		inst.input.focus();
+		if (!inst.input.val()) {
+			$.timeEntry._parseTime(inst);
+		}
+		$.timeEntry._adjustField(inst, delta);
+		event.preventDefault();
+	},
+
+	/* Expand the spinner, if possible, to make it easier to use.
+	   @param  event  (event) the mouse over event */
+	_expandSpinner: function(event) {
+		var spinner = $.timeEntry._getSpinnerTarget(event);
+		var inst = $.data($.timeEntry._getInput(spinner), PROP_NAME);
+		if ($.timeEntry._isDisabledTimeEntry(inst.input[0])) {
+			return;
+		}
+		var spinnerBigImage = $.timeEntry._get(inst, 'spinnerBigImage');
+		if (spinnerBigImage) {
+			inst._expanded = true;
+			var offset = $(spinner).offset();
+			var relative = null;
+			$(spinner).parents().each(function() {
+				var parent = $(this);
+				if (parent.css('position') == 'relative' ||
+						parent.css('position') == 'absolute') {
+					relative = parent.offset();
+				}
+				return !relative;
+			});
+			var spinnerSize = $.timeEntry._get(inst, 'spinnerSize');
+			var spinnerBigSize = $.timeEntry._get(inst, 'spinnerBigSize');
+			$('<div class="timeEntry_expand" style="position: absolute; left: ' +
+				(offset.left - (spinnerBigSize[0] - spinnerSize[0]) / 2 -
+				(relative ? relative.left : 0)) + 'px; top: ' + (offset.top -
+				(spinnerBigSize[1] - spinnerSize[1]) / 2 - (relative ? relative.top : 0)) +
+				'px; width: ' + spinnerBigSize[0] + 'px; height: ' +
+				spinnerBigSize[1] + 'px; background: transparent url(' +
+				spinnerBigImage + ') no-repeat 0px 0px; z-index: 10;"></div>').
+				mousedown($.timeEntry._handleSpinner).mouseup($.timeEntry._endSpinner).
+				mouseout($.timeEntry._endExpand).mousemove($.timeEntry._describeSpinner).
+				insertAfter(spinner);
+		}
+	},
+
+	/* Locate the actual input field from the spinner.
+	   @param  spinner  (element) the current spinner
+	   @return  (element) the corresponding input */
+	_getInput: function(spinner) {
+		return $(spinner).siblings('.' + $.timeEntry.markerClassName)[0];
+	},
+
+	/* Change the title based on position within the spinner.
+	   @param  event  (event) the mouse move event */
+	_describeSpinner: function(event) {
+		var spinner = $.timeEntry._getSpinnerTarget(event);
+		var inst = $.data($.timeEntry._getInput(spinner), PROP_NAME);
+		spinner.title = $.timeEntry._get(inst, 'spinnerTexts')
+			[$.timeEntry._getSpinnerRegion(inst, event)];
+	},
+
+	/* Handle a click on the spinner.
+	   @param  event  (event) the mouse click event */
+	_handleSpinner: function(event) {
+		var spinner = $.timeEntry._getSpinnerTarget(event);
+		var input = $.timeEntry._getInput(spinner);
+		if ($.timeEntry._isDisabledTimeEntry(input)) {
+			return;
+		}
+		if (input == $.timeEntry._blurredInput) {
+			$.timeEntry._lastInput = input;
+			$.timeEntry._blurredInput = null;
+		}
+		var inst = $.data(input, PROP_NAME);
+		$.timeEntry._doFocus(input);
+		var region = $.timeEntry._getSpinnerRegion(inst, event);
+		$.timeEntry._changeSpinner(inst, spinner, region);
+		$.timeEntry._actionSpinner(inst, region);
+		$.timeEntry._timer = null;
+		$.timeEntry._handlingSpinner = true;
+		var spinnerRepeat = $.timeEntry._get(inst, 'spinnerRepeat');
+		if (region >= 3 && spinnerRepeat[0]) { // Repeat increment/decrement
+			$.timeEntry._timer = setTimeout(
+				function() { $.timeEntry._repeatSpinner(inst, region); },
+				spinnerRepeat[0]);
+			$(spinner).one('mouseout', $.timeEntry._releaseSpinner).
+				one('mouseup', $.timeEntry._releaseSpinner);
+		}
+	},
+
+	/* Action a click on the spinner.
+	   @param  inst    (object) the instance settings
+	   @param  region  (number) the spinner "button" */
+	_actionSpinner: function(inst, region) {
+		if (!inst.input.val()) {
+			$.timeEntry._parseTime(inst);
+		}
+		switch (region) {
+			case 0: this._setTime(inst); break;
+			case 1: this._changeField(inst, -1, false); break;
+			case 2: this._changeField(inst, +1, false); break;
+			case 3: this._adjustField(inst, +1); break;
+			case 4: this._adjustField(inst, -1); break;
+		}
+	},
+
+	/* Repeat a click on the spinner.
+	   @param  inst    (object) the instance settings
+	   @param  region  (number) the spinner "button" */
+	_repeatSpinner: function(inst, region) {
+		if (!$.timeEntry._timer) {
+			return;
+		}
+		$.timeEntry._lastInput = $.timeEntry._blurredInput;
+		this._actionSpinner(inst, region);
+		this._timer = setTimeout(
+			function() { $.timeEntry._repeatSpinner(inst, region); },
+			this._get(inst, 'spinnerRepeat')[1]);
+	},
+
+	/* Stop a spinner repeat.
+	   @param  event  (event) the mouse event */
+	_releaseSpinner: function(event) {
+		clearTimeout($.timeEntry._timer);
+		$.timeEntry._timer = null;
+	},
+
+	/* Tidy up after an expanded spinner.
+	   @param  event  (event) the mouse event */
+	_endExpand: function(event) {
+		$.timeEntry._timer = null;
+		var spinner = $.timeEntry._getSpinnerTarget(event);
+		var input = $.timeEntry._getInput(spinner);
+		var inst = $.data(input, PROP_NAME);
+		$(spinner).remove();
+		inst._expanded = false;
+	},
+
+	/* Tidy up after a spinner click.
+	   @param  event  (event) the mouse event */
+	_endSpinner: function(event) {
+		$.timeEntry._timer = null;
+		var spinner = $.timeEntry._getSpinnerTarget(event);
+		var input = $.timeEntry._getInput(spinner);
+		var inst = $.data(input, PROP_NAME);
+		if (!$.timeEntry._isDisabledTimeEntry(input)) {
+			$.timeEntry._changeSpinner(inst, spinner, -1);
+		}
+		if ($.timeEntry._handlingSpinner) {
+			$.timeEntry._lastInput = $.timeEntry._blurredInput;
+		}
+		if ($.timeEntry._lastInput && $.timeEntry._handlingSpinner) {
+			$.timeEntry._showField(inst);
+		}
+		$.timeEntry._handlingSpinner = false;
+	},
+
+	/* Retrieve the spinner from the event.
+	   @param  event  (event) the mouse click event
+	   @return  (element) the target field */
+	_getSpinnerTarget: function(event) {
+		return event.target || event.srcElement;
+	},
+
+	/* Determine which "button" within the spinner was clicked.
+	   @param  inst   (object) the instance settings
+	   @param  event  (event) the mouse event
+	   @return  (number) the spinner "button" number */
+	_getSpinnerRegion: function(inst, event) {
+		var spinner = this._getSpinnerTarget(event);
+		var pos = ($.browser.opera || $.browser.safari ?
+			$.timeEntry._findPos(spinner) : $(spinner).offset());
+		var scrolled = ($.browser.safari ? $.timeEntry._findScroll(spinner) :
+			[document.documentElement.scrollLeft || document.body.scrollLeft,
+			document.documentElement.scrollTop || document.body.scrollTop]);
+		var spinnerIncDecOnly = this._get(inst, 'spinnerIncDecOnly');
+		var left = (spinnerIncDecOnly ? 99 : event.clientX + scrolled[0] -
+			pos.left - ($.browser.msie ? 2 : 0));
+		var top = event.clientY + scrolled[1] - pos.top - ($.browser.msie ? 2 : 0);
+		var spinnerSize = this._get(inst, (inst._expanded ? 'spinnerBigSize' : 'spinnerSize'));
+		var right = (spinnerIncDecOnly ? 99 : spinnerSize[0] - 1 - left);
+		var bottom = spinnerSize[1] - 1 - top;
+		if (spinnerSize[2] > 0 && Math.abs(left - right) <= spinnerSize[2] &&
+				Math.abs(top - bottom) <= spinnerSize[2]) {
+			return 0; // Centre button
+		}
+		var min = Math.min(left, top, right, bottom);
+		return (min == left ? 1 : (min == right ? 2 : (min == top ? 3 : 4))); // Nearest edge
+	},
+
+	/* Change the spinner image depending on button clicked.
+	   @param  inst     (object) the instance settings
+	   @param  spinner  (element) the spinner control
+	   @param  region   (number) the spinner "button" */
+	_changeSpinner: function(inst, spinner, region) {
+		$(spinner).css('background-position', '-' + ((region + 1) *
+			this._get(inst, (inst._expanded ? 'spinnerBigSize' : 'spinnerSize'))[0]) + 'px 0px');
+	},
+
+	/* Find an object's position on the screen.
+	   @param  obj  (element) the control
+	   @return  (object) position as .left and .top */
+	_findPos: function(obj) {
+		var curLeft = curTop = 0;
+		if (obj.offsetParent) {
+			curLeft = obj.offsetLeft;
+			curTop = obj.offsetTop;
+			while (obj = obj.offsetParent) {
+				var origCurLeft = curLeft;
+				curLeft += obj.offsetLeft;
+				if (curLeft < 0) {
+					curLeft = origCurLeft;
+				}
+				curTop += obj.offsetTop;
+			}
+		}
+		return {left: curLeft, top: curTop};
+	},
+
+	/* Find an object's scroll offset on the screen.
+	   @param  obj  (element) the control
+	   @return  (number[]) offset as [left, top] */
+	_findScroll: function(obj) {
+		var isFixed = false;
+		$(obj).parents().each(function() {
+			isFixed |= $(this).css('position') == 'fixed';
+		});
+		if (isFixed) {
+			return [0, 0];
+		}
+		var scrollLeft = obj.scrollLeft;
+		var scrollTop = obj.scrollTop;
+		while (obj = obj.parentNode) {
+			scrollLeft += obj.scrollLeft || 0;
+			scrollTop += obj.scrollTop || 0;
+		}
+		return [scrollLeft, scrollTop];
+	},
+
+	/* Get a setting value, defaulting if necessary.
+	   @param  inst  (object) the instance settings
+	   @param  name  (string) the setting name
+	   @return  (any) the setting value */
+	_get: function(inst, name) {
+		return (inst.options[name] != null ?
+			inst.options[name] : $.timeEntry._defaults[name]);
+	},
+
+	/* Extract the time value from the input field, or default to now.
+	   @param  inst  (object) the instance settings */
+	_parseTime: function(inst) {
+		var currentTime = this._extractTime(inst);
+		var showSeconds = this._get(inst, 'showSeconds');
+		if (currentTime) {
+			inst._selectedHour = currentTime[0];
+			inst._selectedMinute = currentTime[1];
+			inst._selectedSecond = currentTime[2];
+		}
+		else {
+			var now = this._constrainTime(inst);
+			inst._selectedHour = now[0];
+			inst._selectedMinute = now[1];
+			inst._selectedSecond = (showSeconds ? now[2] : 0);
+		}
+		inst._secondField = (showSeconds ? 2 : -1);
+		inst._ampmField = (this._get(inst, 'show24Hours') ? -1 : (showSeconds ? 3 : 2));
+		inst._lastChr = '';
+		inst._field = Math.max(0, Math.min(
+			Math.max(1, inst._secondField, inst._ampmField), this._get(inst, 'initialField')));
+		if (inst.input.val() != '') {
+			this._showTime(inst);
+		}
+	},
+
+	/* Extract the time value from a string as an array of values, or default to null.
+	   @param  inst   (object) the instance settings
+	   @param  value  (string) the time value to parse
+	   @return  (number[3]) the time components (hours, minutes, seconds)
+	            or null if no value */
+	_extractTime: function(inst, value) {
+		value = value || inst.input.val();
+		var separator = this._get(inst, 'separator');
+		var currentTime = value.split(separator);
+		if (separator == '' && value != '') {
+			currentTime[0] = value.substring(0, 2);
+			currentTime[1] = value.substring(2, 4);
+			currentTime[2] = value.substring(4, 6);
+		}
+		var ampmNames = this._get(inst, 'ampmNames');
+		var show24Hours = this._get(inst, 'show24Hours');
+		if (currentTime.length >= 2) {
+			var isAM = !show24Hours && (value.indexOf(ampmNames[0]) > -1);
+			var isPM = !show24Hours && (value.indexOf(ampmNames[1]) > -1);
+			var hour = parseInt(currentTime[0], 10);
+			hour = (isNaN(hour) ? 0 : hour);
+			hour = ((isAM || isPM) && hour == 12 ? 0 : hour) + (isPM ? 12 : 0);
+			var minute = parseInt(currentTime[1], 10);
+			minute = (isNaN(minute) ? 0 : minute);
+			var second = (currentTime.length >= 3 ?
+				parseInt(currentTime[2], 10) : 0);
+			second = (isNaN(second) || !this._get(inst, 'showSeconds') ? 0 : second);
+			return this._constrainTime(inst, [hour, minute, second]);
+		} 
+		return null;
+	},
+
+	/* Constrain the given/current time to the time steps.
+	   @param  inst    (object) the instance settings
+	   @param  fields  (number[3]) the current time components (hours, minutes, seconds)
+	   @return  (number[3]) the constrained time components (hours, minutes, seconds) */
+	_constrainTime: function(inst, fields) {
+		var specified = (fields != null);
+		if (!specified) {
+			var now = this._determineTime(inst, this._get(inst, 'defaultTime')) || new Date();
+			fields = [now.getHours(), now.getMinutes(), now.getSeconds()];
+		}
+		var reset = false;
+		var timeSteps = this._get(inst, 'timeSteps');
+		for (var i = 0; i < timeSteps.length; i++) {
+			if (reset) {
+				fields[i] = 0;
+			}
+			else if (timeSteps[i] > 1) {
+				fields[i] = Math.round(fields[i] / timeSteps[i]) * timeSteps[i];
+				reset = true;
+			}
+		}
+		return fields;
+	},
+
+	/* Set the selected time into the input field.
+	   @param  inst  (object) the instance settings */
+	_showTime: function(inst) {
+		var show24Hours = this._get(inst, 'show24Hours');
+		var separator = this._get(inst, 'separator');
+		var currentTime = (this._formatNumber(show24Hours ? inst._selectedHour :
+			((inst._selectedHour + 11) % 12) + 1) + separator +
+			this._formatNumber(inst._selectedMinute) +
+			(this._get(inst, 'showSeconds') ? separator +
+			this._formatNumber(inst._selectedSecond) : '') +
+			(show24Hours ?  '' : this._get(inst, 'ampmPrefix') +
+			this._get(inst, 'ampmNames')[(inst._selectedHour < 12 ? 0 : 1)]));
+		this._setValue(inst, currentTime);
+		this._showField(inst);
+	},
+
+	/* Highlight the current time field.
+	   @param  inst  (object) the instance settings */
+	_showField: function(inst) {
+		var input = inst.input[0];
+		if (inst.input.is(':hidden') || $.timeEntry._lastInput != input) {
+			return;
+		}
+		var separator = this._get(inst, 'separator');
+		var fieldSize = separator.length + 2;
+		var start = (inst._field != inst._ampmField ? (inst._field * fieldSize) :
+			(inst._ampmField * fieldSize) - separator.length + this._get(inst, 'ampmPrefix').length);
+		var end = start + (inst._field != inst._ampmField ? 2 : this._get(inst, 'ampmNames')[0].length);
+		if (input.setSelectionRange) { // Mozilla
+			input.setSelectionRange(start, end);
+		}
+		else if (input.createTextRange) { // IE
+			var range = input.createTextRange();
+			range.moveStart('character', start);
+			range.moveEnd('character', end - inst.input.val().length);
+			range.select();
+		}
+		if (!input.disabled) {
+			input.focus();
+		}
+	},
+
+	/* Ensure displayed single number has a leading zero.
+	   @param  value  (number) current value
+	   @return  (string) number with at least two digits */
+	_formatNumber: function(value) {
+		return (value < 10 ? '0' : '') + value;
+	},
+
+	/* Update the input field and notify listeners.
+	   @param  inst   (object) the instance settings
+	   @param  value  (string) the new value */
+	_setValue: function(inst, value) {
+		if (value != inst.input.val()) {
+			inst.input.val(value).trigger('change');
+		}
+	},
+
+	/* Move to previous/next field, or out of field altogether if appropriate.
+	   @param  inst     (object) the instance settings
+	   @param  offset   (number) the direction of change (-1, +1)
+	   @param  moveOut  (boolean) true if can move out of the field
+	   @return  (boolean) true if exitting the field, false if not */
+	_changeField: function(inst, offset, moveOut) {
+		var atFirstLast = (inst.input.val() == '' || inst._field ==
+			(offset == -1 ? 0 : Math.max(1, inst._secondField, inst._ampmField)));
+		if (!atFirstLast) {
+			inst._field += offset;
+		}
+		this._showField(inst);
+		inst._lastChr = '';
+		$.data(inst.input[0], PROP_NAME, inst);
+		return (atFirstLast && moveOut);
+	},
+
+	/* Update the current field in the direction indicated.
+	   @param  inst    (object) the instance settings
+	   @param  offset  (number) the amount to change by */
+	_adjustField: function(inst, offset) {
+		if (inst.input.val() == '') {
+			offset = 0;
+		}
+		var timeSteps = this._get(inst, 'timeSteps');
+		this._setTime(inst, new Date(0, 0, 0,
+			inst._selectedHour + (inst._field == 0 ? offset * timeSteps[0] : 0) +
+			(inst._field == inst._ampmField ? offset * 12 : 0),
+			inst._selectedMinute + (inst._field == 1 ? offset * timeSteps[1] : 0),
+			inst._selectedSecond + (inst._field == inst._secondField ? offset * timeSteps[2] : 0)));
+	},
+
+	/* Check against minimum/maximum and display time.
+	   @param  inst  (object) the instance settings
+	   @param  time  (Date) an actual time or
+	                 (number) offset in seconds from now or
+					 (string) units and periods of offsets from now */
+	_setTime: function(inst, time) {
+		time = this._determineTime(inst, time);
+		var fields = this._constrainTime(inst, time ?
+			[time.getHours(), time.getMinutes(), time.getSeconds()] : null);
+		time = new Date(0, 0, 0, fields[0], fields[1], fields[2]);
+		// Normalise to base date
+		var time = this._normaliseTime(time);
+		var minTime = this._normaliseTime(this._determineTime(inst, this._get(inst, 'minTime')));
+		var maxTime = this._normaliseTime(this._determineTime(inst, this._get(inst, 'maxTime')));
+		// Ensure it is within the bounds set
+		time = (minTime && time < minTime ? minTime :
+			(maxTime && time > maxTime ? maxTime : time));
+		var beforeSetTime = this._get(inst, 'beforeSetTime');
+		// Perform further restrictions if required
+		if (beforeSetTime) {
+			time = beforeSetTime.apply(inst.input[0],
+				[this._getTimeTimeEntry(inst.input[0]), time, minTime, maxTime]);
+		}
+		inst._selectedHour = time.getHours();
+		inst._selectedMinute = time.getMinutes();
+		inst._selectedSecond = time.getSeconds();
+		this._showTime(inst);
+		$.data(inst.input[0], PROP_NAME, inst);
+	},
+
+	/* Normalise time object to a common date.
+	   @param  time  (Date) the original time
+	   @return  (Date) the normalised time */
+	_normaliseTime: function(time) {
+		if (!time) {
+			return null;
+		}
+		time.setFullYear(1900);
+		time.setMonth(0);
+		time.setDate(0);
+		return time;
+	},
+
+	/* A time may be specified as an exact value or a relative one.
+	   @param  inst     (object) the instance settings
+	   @param  setting  (Date) an actual time or
+	                    (number) offset in seconds from now or
+	                    (string) units and periods of offsets from now
+	   @return  (Date) the calculated time */
+	_determineTime: function(inst, setting) {
+		var offsetNumeric = function(offset) { // E.g. +300, -2
+			var time = new Date();
+			time.setTime(time.getTime() + offset * 1000);
+			return time;
+		};
+		var offsetString = function(offset) { // E.g. '+2m', '-4h', '+3h +30m' or '12:34:56PM'
+			var fields = $.timeEntry._extractTime(inst, offset); // Actual time?
+			var time = new Date();
+			var hour = (fields ? fields[0] : time.getHours());
+			var minute = (fields ? fields[1] : time.getMinutes());
+			var second = (fields ? fields[2] : time.getSeconds());
+			if (!fields) {
+				var pattern = /([+-]?[0-9]+)\s*(s|S|m|M|h|H)?/g;
+				var matches = pattern.exec(offset);
+				while (matches) {
+					switch (matches[2] || 's') {
+						case 's' : case 'S' :
+							second += parseInt(matches[1], 10); break;
+						case 'm' : case 'M' :
+							minute += parseInt(matches[1], 10); break;
+						case 'h' : case 'H' :
+							hour += parseInt(matches[1], 10); break;
+					}
+					matches = pattern.exec(offset);
+				}
+			}
+			time = new Date(0, 0, 10, hour, minute, second, 0);
+			if (/^!/.test(offset)) { // No wrapping
+				if (time.getDate() > 10) {
+					time = new Date(0, 0, 10, 23, 59, 59);
+				}
+				else if (time.getDate() < 10) {
+					time = new Date(0, 0, 10, 0, 0, 0);
+				}
+			}
+			return time;
+		};
+		return (setting ? (typeof setting == 'string' ? offsetString(setting) :
+			(typeof setting == 'number' ? offsetNumeric(setting) : setting)) : null);
+	},
+
+	/* Update time based on keystroke entered.
+	   @param  inst  (object) the instance settings
+	   @param  chr   (ch) the new character */
+	_handleKeyPress: function(inst, chr) {
+		if (chr == this._get(inst, 'separator')) {
+			this._changeField(inst, +1, false);
+		}
+		else if (chr >= '0' && chr <= '9') { // Allow direct entry of time
+			var key = parseInt(chr, 10);
+			var value = parseInt(inst._lastChr + chr, 10);
+			var show24Hours = this._get(inst, 'show24Hours');
+			var hour = (inst._field != 0 ? inst._selectedHour :
+				(show24Hours ? (value < 24 ? value : key) :
+				(value >= 1 && value <= 12 ? value :
+				(key > 0 ? key : inst._selectedHour)) % 12 +
+				(inst._selectedHour >= 12 ? 12 : 0)));
+			var minute = (inst._field != 1 ? inst._selectedMinute :
+				(value < 60 ? value : key));
+			var second = (inst._field != inst._secondField ? inst._selectedSecond :
+				(value < 60 ? value : key));
+			var fields = this._constrainTime(inst, [hour, minute, second]);
+			this._setTime(inst, new Date(0, 0, 0, fields[0], fields[1], fields[2]));
+			inst._lastChr = chr;
+		}
+		else if (!this._get(inst, 'show24Hours')) { // Set am/pm based on first char of names
+			chr = chr.toLowerCase();
+			var ampmNames = this._get(inst, 'ampmNames');
+			if ((chr == ampmNames[0].substring(0, 1).toLowerCase() &&
+					inst._selectedHour >= 12) ||
+					(chr == ampmNames[1].substring(0, 1).toLowerCase() &&
+					inst._selectedHour < 12)) {
+				var saveField = inst._field;
+				inst._field = inst._ampmField;
+				this._adjustField(inst, +1);
+				inst._field = saveField;
+				this._showField(inst);
+			}
+		}
+	}
+});
+
+/* jQuery extend now ignores nulls!
+   @param  target  (object) the object to update
+   @param  props   (object) the new settings 
+   @return  (object) the updated object */
+function extendRemove(target, props) {
+	$.extend(target, props);
+	for (var name in props) {
+		if (props[name] == null) {
+			target[name] = null;
+		}
+	}
+	return target;
+}
+
+// Commands that don't return a jQuery object
+var getters = ['getOffset', 'getTime', 'isDisabled'];
+
+/* Attach the time entry functionality to a jQuery selection.
+   @param  command  (string) the command to run (optional, default 'attach')
+   @param  options  (object) the new settings to use for these countdown instances (optional)
+   @return  (jQuery) for chaining further calls */
+$.fn.timeEntry = function(options) {
+	var otherArgs = Array.prototype.slice.call(arguments, 1);
+	if (typeof options == 'string' && $.inArray(options, getters) > -1) {
+		return $.timeEntry['_' + options + 'TimeEntry'].apply($.timeEntry, [this[0]].concat(otherArgs));
+	}
+	return this.each(function() {
+		var nodeName = this.nodeName.toLowerCase();
+		if (nodeName == 'input') {
+			if (typeof options == 'string') {
+				$.timeEntry['_' + options + 'TimeEntry'].apply($.timeEntry, [this].concat(otherArgs));
+			}
+			else {
+				// Check for settings on the control itself
+				var inlineSettings = ($.fn.metadata ? $(this).metadata() : {});
+				$.timeEntry._connectTimeEntry(this, $.extend(inlineSettings, options));
+			}
+		} 
+	});
+};
+
+/* Initialise the time entry functionality. */
+$.timeEntry = new TimeEntry(); // Singleton instance
+
+})(jQuery);
diff --git a/plugins/jquery.timeentry.pack.js b/plugins/jquery.timeentry.pack.js
new file mode 100644
index 0000000..df068e4
--- /dev/null
+++ b/plugins/jquery.timeentry.pack.js
@@ -0,0 +1,14 @@
+/* http://keith-wood.name/timeEntry.html
+
+   Time entry for jQuery v1.4.9.
+
+   Written by Keith Wood (kbwood{at}iinet.com.au) June 2007.
+
+   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
+
+   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
+
+   Please attribute the author if you use it. */
+
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(r($){r 1o(){p.Y=[];p.26=[];p.26[\'\']={1c:B,1d:\':\',1p:\'\',Z:[\'3J\',\'3K\'],2C:[\'3L\',\'3M 2D\',\'3N 2D\',\'3O\',\'3P\']};p.1B={2E:\'\',1C:B,27:[1,1,1],2F:0,2G:N,2H:v,2I:v,2J:v,2K:\'3Q.3R\',1q:[20,20,8],2L:\'\',1D:[40,40,16],2M:B,28:[3S,3T],2N:v,2O:v};$.1r(p.1B,p.26[\'\'])}7 m=\'o\';$.1r(1o.2P,{1e:\'3U\',3V:r(a){1E(p.1B,a||{});u p},2Q:r(b,c){7 d=$(b);q(d.2R(p.1e)){u}7 e={};e.1s=$.1r({},c);e.E=0;e.13=0;e.14=0;e.x=0;e.w=$(b);$.y(b,m,e);7 f=p.t(e,\'2K\');7 g=p.t(e,\'3W\');7 h=p.t(e,\'1q\');7 i=p.t(e,\'2E\');7 j=(!f?v:$(\'<15 1F="3X" 2S="3Y: 3Z-41; \'+\'29: 2T(\\\'\'+f+\'\\\') 0 0 2U-2V; \'+\'2a: \'+h[0]+\'P; 2W: \'+h[1]+\'P;\'+($.K.2X&&$.K.42<\'1.9\'?\' 2Y-U: \'+h[0]+\'P; 2Y-43: \'+(h[1]-18)+\'P;\':\'\')+\'"></15>\'));d.44(\'<15 1F="45"></15>\').2Z(i?\'<15 1F="47">\'+i+\'</15>\':\'\').2Z(j||\'\');d.49(p.1e).17(\'2b.o\',p.2c).17(\'4a.o\',p.30).17(\'4b.o\',p.31).17(\'4c.o\',p.32).17(\'4d.o\',p.33);q($.K.2X){d.17(\'w.o\',r(a){$.o.1f(e)})}q($.K.2d){d.17(\'4e.o\',r(a){1G(r(){$.o.1f(e)},1)})}q(p.t(e,\'2G\')&&$.1H.2e){d.2e(p.34)}q(j){j.3a(p.2f).2g(p.1I).4f(p.3b).2h(p.1I).3c(p.2i)}},4g:r(a){p.2j(a,B)},4h:r(a){p.2j(a,N)},2j:r(b,c){7 d=$.y(b,m);q(!d){u}b.3d=c;q(b.2k&&b.2k.1J.1g()==\'15\'){$.o.1K(d,b.2k,(c?5:-1))}$.o.Y=$.3e($.o.Y,r(a){u(a==b?v:a)});q(c){$.o.Y.4i(b)}},1h:r(a){u $.3f(a,p.Y)>-1},4j:r(a,b,c){7 d=$.y(a,m);q(d){q(1i b==\'1L\'){7 e=b;b={};b[e]=c}7 f=p.1j(d);1E(d.1s,b||{});q(f){p.19(d,F J(0,0,0,f[0],f[1],f[2]))}}$.y(a,m,d)},4k:r(b){$w=$(b);q(!$w.2R(p.1e)){u}$w.4l(p.1e).4m(\'.o\');q($.1H.2e){$w.4n()}p.Y=$.3e(p.Y,r(a){u(a==b?v:a)});$w.4o().4p($w);$.4q(b,m)},4r:r(a,b){7 c=$.y(a,m);q(c){q(b===v||b===\'\'){c.w.O(\'\')}L{p.19(c,b?(1i b==\'4s\'?F J(b.2l()):b):v)}}},3g:r(a){7 b=$.y(a,m);7 c=(b?p.1j(b):v);u(!c?v:F J(0,0,0,c[0],c[1],c[2]))},4t:r(a){7 b=$.y(a,m);7 c=(b?p.1j(b):v);u(!c?0:(c[0]*4u+c[1]*2m+c[2])*3h)},2c:r(a){7 b=(a.1J&&a.1J.1g()==\'w\'?a:p);q($.o.Q==b||$.o.1h(b)){$.o.1M=B;u}7 c=$.y(b,m);$.o.1M=N;$.o.Q=b;$.o.1k=v;7 d=$.o.t(c,\'2N\');1E(c.1s,(d?d.1N(b,[b]):{}));$.y(b,m,c);$.o.1f(c);1G(r(){$.o.1a(c)},10)},30:r(a){$.o.1k=$.o.Q;$.o.Q=v},31:r(b){7 c=b.1l;7 d=$.y(c,m);q(!$.o.1M){7 e=$.o.t(d,\'1d\').G+2;d.x=0;q(c.3i!=v){1O(7 f=0;f<=I.1m(1,d.V,d.D);f++){7 g=(f!=d.D?(f*e)+2:(d.D*e)+$.o.t(d,\'1p\').G+$.o.t(d,\'Z\')[0].G);d.x=f;q(c.3i<g){C}}}L q(c.1P){7 h=$(b.3j);7 i=c.1P();7 j=r(a){u{4v:2,4w:4,4x:6}[a]||a};7 k=b.3k+1t.2n.1u-(h.1Q().U+R(j(h.1v(\'4y-U-2a\')),10))-i.2o;1O(7 f=0;f<=I.1m(1,d.V,d.D);f++){7 g=(f!=d.D?(f*e)+2:(d.D*e)+$.o.t(d,\'1p\').G+$.o.t(d,\'Z\')[0].G);i.4z();i.3l(\'2p\',g);d.x=f;q(k<i.4A){C}}}}$.y(c,m,d);$.o.1a(d);$.o.1M=B},32:r(a){q(a.2q>=48){u N}7 b=$.y(a.1l,m);2r(a.2q){A 9:u(a.4B?$.o.W(b,-1,N):$.o.W(b,+1,N));A 35:q(a.3m){$.o.1R(b,\'\')}L{b.x=I.1m(1,b.V,b.D);$.o.T(b,0)}C;A 36:q(a.3m){$.o.19(b)}L{b.x=0;$.o.T(b,0)}C;A 37:$.o.W(b,-1,B);C;A 38:$.o.T(b,+1);C;A 39:$.o.W(b,+1,B);C;A 40:$.o.T(b,-1);C;A 46:$.o.1R(b,\'\');C}u B},33:r(a){7 b=4C.4D(a.3n==4E?a.2q:a.3n);q(b<\' \'){u N}7 c=$.y(a.1l,m);$.o.3o(c,b);u B},34:r(a,b){q($.o.1h(a.1l)){u}b=($.K.3p?-b/I.1S(b):($.K.2s?b/I.1S(b):b));7 c=$.y(a.1l,m);c.w.2b();q(!c.w.O()){$.o.1f(c)}$.o.T(c,b);a.4F()},3b:r(b){7 c=$.o.1b(b);7 d=$.y($.o.1n(c),m);q($.o.1h(d.w[0])){u}7 e=$.o.t(d,\'2L\');q(e){d.1T=N;7 f=$(c).1Q();7 g=v;$(c).3q().2t(r(){7 a=$(p);q(a.1v(\'1w\')==\'4G\'||a.1v(\'1w\')==\'3r\'){g=a.1Q()}u!g});7 h=$.o.t(d,\'1q\');7 i=$.o.t(d,\'1D\');$(\'<3s 1F="4H" 2S="1w: 3r; U: \'+(f.U-(i[0]-h[0])/2-(g?g.U:0))+\'P; 1x: \'+(f.1x-(i[1]-h[1])/2-(g?g.1x:0))+\'P; 2a: \'+i[0]+\'P; 2W: \'+i[1]+\'P; 29: 4I 2T(\'+e+\') 2U-2V 2u 2u; z-4J: 10;"></3s>\').3a($.o.2f).2g($.o.1I).2h($.o.3t).3c($.o.2i).4K(c)}},1n:r(a){u $(a).4L(\'.\'+$.o.1e)[0]},2i:r(a){7 b=$.o.1b(a);7 c=$.y($.o.1n(b),m);b.4M=$.o.t(c,\'2C\')[$.o.2v(c,a)]},2f:r(a){7 b=$.o.1b(a);7 c=$.o.1n(b);q($.o.1h(c)){u}q(c==$.o.1k){$.o.Q=c;$.o.1k=v}7 d=$.y(c,m);$.o.2c(c);7 e=$.o.2v(d,a);$.o.1K(d,b,e);$.o.2w(d,e);$.o.X=v;$.o.1U=N;7 f=$.o.t(d,\'28\');q(e>=3&&f[0]){$.o.X=1G(r(){$.o.2x(d,e)},f[0]);$(b).3u(\'2h\',$.o.2y).3u(\'2g\',$.o.2y)}},2w:r(a,b){q(!a.w.O()){$.o.1f(a)}2r(b){A 0:p.19(a);C;A 1:p.W(a,-1,B);C;A 2:p.W(a,+1,B);C;A 3:p.T(a,+1);C;A 4:p.T(a,-1);C}},2x:r(a,b){q(!$.o.X){u}$.o.Q=$.o.1k;p.2w(a,b);p.X=1G(r(){$.o.2x(a,b)},p.t(a,\'28\')[1])},2y:r(a){4N($.o.X);$.o.X=v},3t:r(a){$.o.X=v;7 b=$.o.1b(a);7 c=$.o.1n(b);7 d=$.y(c,m);$(b).4O();d.1T=B},1I:r(a){$.o.X=v;7 b=$.o.1b(a);7 c=$.o.1n(b);7 d=$.y(c,m);q(!$.o.1h(c)){$.o.1K(d,b,-1)}q($.o.1U){$.o.Q=$.o.1k}q($.o.Q&&$.o.1U){$.o.1a(d)}$.o.1U=B},1b:r(a){u a.1l||a.3j},2v:r(a,b){7 c=p.1b(b);7 d=($.K.3p||$.K.2s?$.o.3v(c):$(c).1Q());7 e=($.K.2s?$.o.3w(c):[1t.2n.1u||1t.3x.1u,1t.2n.1V||1t.3x.1V]);7 f=p.t(a,\'2M\');7 g=(f?3y:b.3k+e[0]-d.U-($.K.2d?2:0));7 h=b.4P+e[1]-d.1x-($.K.2d?2:0);7 i=p.t(a,(a.1T?\'1D\':\'1q\'));7 j=(f?3y:i[0]-1-g);7 k=i[1]-1-h;q(i[2]>0&&I.1S(g-j)<=i[2]&&I.1S(h-k)<=i[2]){u 0}7 l=I.3z(g,h,j,k);u(l==g?1:(l==j?2:(l==h?3:4)))},1K:r(a,b,c){$(b).1v(\'29-1w\',\'-\'+((c+1)*p.t(a,(a.1T?\'1D\':\'1q\'))[0])+\'P 2u\')},3v:r(a){7 b=1W=0;q(a.3A){b=a.2o;1W=a.3B;2z(a=a.3A){7 c=b;b+=a.2o;q(b<0){b=c}1W+=a.3B}}u{U:b,1x:1W}},3w:r(a){7 b=B;$(a).3q().2t(r(){b|=$(p).1v(\'1w\')==\'4Q\'});q(b){u[0,0]}7 c=a.1u;7 d=a.1V;2z(a=a.4R){c+=a.1u||0;d+=a.1V||0}u[c,d]},t:r(a,b){u(a.1s[b]!=v?a.1s[b]:$.o.1B[b])},1f:r(a){7 b=p.1j(a);7 c=p.t(a,\'1C\');q(b){a.E=b[0];a.13=b[1];a.14=b[2]}L{7 d=p.1y(a);a.E=d[0];a.13=d[1];a.14=(c?d[2]:0)}a.V=(c?2:-1);a.D=(p.t(a,\'1c\')?-1:(c?3:2));a.1X=\'\';a.x=I.1m(0,I.3z(I.1m(1,a.V,a.D),p.t(a,\'2F\')));q(a.w.O()!=\'\'){p.2A(a)}},1j:r(a,b){b=b||a.w.O();7 c=p.t(a,\'1d\');7 d=b.4S(c);q(c==\'\'&&b!=\'\'){d[0]=b.1z(0,2);d[1]=b.1z(2,4);d[2]=b.1z(4,6)}7 e=p.t(a,\'Z\');7 f=p.t(a,\'1c\');q(d.G>=2){7 g=!f&&(b.3C(e[0])>-1);7 h=!f&&(b.3C(e[1])>-1);7 i=R(d[0],10);i=(2B(i)?0:i);i=((g||h)&&i==12?0:i)+(h?12:0);7 j=R(d[1],10);j=(2B(j)?0:j);7 k=(d.G>=3?R(d[2],10):0);k=(2B(k)||!p.t(a,\'1C\')?0:k);u p.1y(a,[i,j,k])}u v},1y:r(a,b){7 c=(b!=v);q(!c){7 d=p.1A(a,p.t(a,\'2H\'))||F J();b=[d.1Y(),d.1Z(),d.21()]}7 e=B;7 f=p.t(a,\'27\');1O(7 i=0;i<f.G;i++){q(e){b[i]=0}L q(f[i]>1){b[i]=I.4T(b[i]/f[i])*f[i];e=N}}u b},2A:r(a){7 b=p.t(a,\'1c\');7 c=p.t(a,\'1d\');7 d=(p.22(b?a.E:((a.E+11)%12)+1)+c+p.22(a.13)+(p.t(a,\'1C\')?c+p.22(a.14):\'\')+(b?\'\':p.t(a,\'1p\')+p.t(a,\'Z\')[(a.E<12?0:1)]));p.1R(a,d);p.1a(a)},1a:r(a){7 b=a.w[0];q(a.w.4U(\':4V\')||$.o.Q!=b){u}7 c=p.t(a,\'1d\');7 d=c.G+2;7 e=(a.x!=a.D?(a.x*d):(a.D*d)-c.G+p.t(a,\'1p\').G);7 f=e+(a.x!=a.D?2:p.t(a,\'Z\')[0].G);q(b.3D){b.3D(e,f)}L q(b.1P){7 g=b.1P();g.4W(\'2p\',e);g.3l(\'2p\',f-a.w.O().G);g.4X()}q(!b.3d){b.2b()}},22:r(a){u(a<10?\'0\':\'\')+a},1R:r(a,b){q(b!=a.w.O()){a.w.O(b).4Y(\'4Z\')}},W:r(a,b,c){7 d=(a.w.O()==\'\'||a.x==(b==-1?0:I.1m(1,a.V,a.D)));q(!d){a.x+=b}p.1a(a);a.1X=\'\';$.y(a.w[0],m,a);u(d&&c)},T:r(a,b){q(a.w.O()==\'\'){b=0}7 c=p.t(a,\'27\');p.19(a,F J(0,0,0,a.E+(a.x==0?b*c[0]:0)+(a.x==a.D?b*12:0),a.13+(a.x==1?b*c[1]:0),a.14+(a.x==a.V?b*c[2]:0)))},19:r(a,b){b=p.1A(a,b);7 c=p.1y(a,b?[b.1Y(),b.1Z(),b.21()]:v);b=F J(0,0,0,c[0],c[1],c[2]);7 b=p.25(b);7 d=p.25(p.1A(a,p.t(a,\'2I\')));7 e=p.25(p.1A(a,p.t(a,\'2J\')));b=(d&&b<d?d:(e&&b>e?e:b));7 f=p.t(a,\'2O\');q(f){b=f.1N(a.w[0],[p.3g(a.w[0]),b,d,e])}a.E=b.1Y();a.13=b.1Z();a.14=b.21();p.2A(a);$.y(a.w[0],m,a)},25:r(a){q(!a){u v}a.50(51);a.52(0);a.53(0);u a},1A:r(i,j){7 k=r(a){7 b=F J();b.54(b.2l()+a*3h);u b};7 l=r(a){7 b=$.o.1j(i,a);7 c=F J();7 d=(b?b[0]:c.1Y());7 e=(b?b[1]:c.1Z());7 f=(b?b[2]:c.21());q(!b){7 g=/([+-]?[0-9]+)\\s*(s|S|m|M|h|H)?/g;7 h=g.3E(a);2z(h){2r(h[2]||\'s\'){A\'s\':A\'S\':f+=R(h[1],10);C;A\'m\':A\'M\':e+=R(h[1],10);C;A\'h\':A\'H\':d+=R(h[1],10);C}h=g.3E(a)}}c=F J(0,0,10,d,e,f,0);q(/^!/.55(a)){q(c.3F()>10){c=F J(0,0,10,23,59,59)}L q(c.3F()<10){c=F J(0,0,10,0,0,0)}}u c};u(j?(1i j==\'1L\'?l(j):(1i j==\'56\'?k(j):j)):v)},3o:r(a,b){q(b==p.t(a,\'1d\')){p.W(a,+1,B)}L q(b>=\'0\'&&b<=\'9\'){7 c=R(b,10);7 d=R(a.1X+b,10);7 e=p.t(a,\'1c\');7 f=(a.x!=0?a.E:(e?(d<24?d:c):(d>=1&&d<=12?d:(c>0?c:a.E))%12+(a.E>=12?12:0)));7 g=(a.x!=1?a.13:(d<2m?d:c));7 h=(a.x!=a.V?a.14:(d<2m?d:c));7 i=p.1y(a,[f,g,h]);p.19(a,F J(0,0,0,i[0],i[1],i[2]));a.1X=b}L q(!p.t(a,\'1c\')){b=b.1g();7 j=p.t(a,\'Z\');q((b==j[0].1z(0,1).1g()&&a.E>=12)||(b==j[1].1z(0,1).1g()&&a.E<12)){7 k=a.x;a.x=a.D;p.T(a,+1);a.x=k;p.1a(a)}}}});r 1E(a,b){$.1r(a,b);1O(7 c 57 b){q(b[c]==v){a[c]=v}}u a}7 n=[\'58\',\'2l\',\'5a\'];$.1H.o=r(c){7 d=5b.2P.5c.5d(5e,1);q(1i c==\'1L\'&&$.3f(c,n)>-1){u $.o[\'3G\'+c+\'1o\'].1N($.o,[p[0]].3H(d))}u p.2t(r(){7 a=p.1J.1g();q(a==\'w\'){q(1i c==\'1L\'){$.o[\'3G\'+c+\'1o\'].1N($.o,[p].3H(d))}L{7 b=($.1H.3I?$(p).3I():{});$.o.2Q(p,$.1r(b,c))}}})};$.o=F 1o()})(5f);',62,326,'|||||||var|||||||||||||||||timeEntry|this|if|function||_get|return|null|input|_field|data||case|false|break|_ampmField|_selectedHour|new|length||Math|Date|browser|else||true|val|px|_lastInput|parseInt||_adjustField|left|_secondField|_changeField|_timer|_disabledInputs|ampmNames||||_selectedMinute|_selectedSecond|span||bind||_setTime|_showField|_getSpinnerTarget|show24Hours|separator|markerClassName|_parseTime|toLowerCase|_isDisabledTimeEntry|typeof|_extractTime|_blurredInput|target|max|_getInput|TimeEntry|ampmPrefix|spinnerSize|extend|options|document|scrollLeft|css|position|top|_constrainTime|substring|_determineTime|_defaults|showSeconds|spinnerBigSize|extendRemove|class|setTimeout|fn|_endSpinner|nodeName|_changeSpinner|string|_focussed|apply|for|createTextRange|offset|_setValue|abs|_expanded|_handlingSpinner|scrollTop|curTop|_lastChr|getHours|getMinutes||getSeconds|_formatNumber|||_normaliseTime|regional|timeSteps|spinnerRepeat|background|width|focus|_doFocus|msie|mousewheel|_handleSpinner|mouseup|mouseout|_describeSpinner|_enableDisable|nextSibling|getTime|60|documentElement|offsetLeft|character|keyCode|switch|safari|each|0px|_getSpinnerRegion|_actionSpinner|_repeatSpinner|_releaseSpinner|while|_showTime|isNaN|spinnerTexts|field|appendText|initialField|useMouseWheel|defaultTime|minTime|maxTime|spinnerImage|spinnerBigImage|spinnerIncDecOnly|beforeShow|beforeSetTime|prototype|_connectTimeEntry|hasClass|style|url|no|repeat|height|mozilla|padding|after|_doBlur|_doClick|_doKeyDown|_doKeyPress|_doMouseWheel||||||mousedown|_expandSpinner|mousemove|disabled|map|inArray|_getTimeTimeEntry|1000|selectionStart|srcElement|clientX|moveEnd|ctrlKey|charCode|_handleKeyPress|opera|parents|absolute|div|_endExpand|one|_findPos|_findScroll|body|99|min|offsetParent|offsetTop|indexOf|setSelectionRange|exec|getDate|_|concat|metadata|AM|PM|Now|Previous|Next|Increment|Decrement|spinnerDefault|png|500|250|hasTimeEntry|setDefaults|spinnerText|timeEntry_control|display|inline||block|version|bottom|wrap|timeEntry_wrap||timeEntry_append||addClass|blur|click|keydown|keypress|paste|mouseover|_enableTimeEntry|_disableTimeEntry|push|_changeTimeEntry|_destroyTimeEntry|removeClass|unbind|unmousewheel|parent|replaceWith|removeData|_setTimeTimeEntry|object|_getOffsetTimeEntry|3600|thin|medium|thick|border|collapse|boundingWidth|shiftKey|String|fromCharCode|undefined|preventDefault|relative|timeEntry_expand|transparent|index|insertAfter|siblings|title|clearTimeout|remove|clientY|fixed|parentNode|split|round|is|hidden|moveStart|select|trigger|change|setFullYear|1900|setMonth|setDate|setTime|test|number|in|getOffset||isDisabled|Array|slice|call|arguments|jQuery'.split('|'),0,{}))
+

diff --git a/plugins/jquery.tokeninput.js b/plugins/jquery.tokeninput.js
new file mode 100644
index 0000000..9e099bf
--- /dev/null
+++ b/plugins/jquery.tokeninput.js
@@ -0,0 +1,794 @@
+/*
+ * jQuery Plugin: Tokenizing Autocomplete Text Entry
+ * Version 1.5.0
+ *
+ * Copyright (c) 2009 James Smith (http://loopj.com)
+ * Licensed jointly under the GPL and MIT licenses,
+ * choose which one suits your project best!
+ *
+ */
+
+(function ($) {
+// Default settings
+var DEFAULT_SETTINGS = {
+    hintText: "Type in a search term",
+    noResultsText: "No results",
+    searchingText: "Searching...",
+    deleteText: "&times;",
+    searchDelay: 300,
+    minChars: 1,
+    tokenLimit: null,
+    jsonContainer: null,
+    method: "GET",
+    contentType: "json",
+    queryParam: "name",
+    tokenDelimiter: ",",
+    preventDuplicates: false,
+    prePopulate: null,
+    processPrePopulate: false,
+    animateDropdown: true,
+    onResult: null,
+    onAdd: null,
+    onDelete: null,
+    idPrefix: "token-input-"
+};
+
+// Default classes to use when theming
+var DEFAULT_CLASSES = {
+    tokenList: "token-input-list",
+    token: "token-input-token",
+    tokenDelete: "token-input-delete-token",
+    selectedToken: "token-input-selected-token",
+    highlightedToken: "token-input-highlighted-token",
+    dropdown: "token-input-dropdown",
+    dropdownItem: "token-input-dropdown-item",
+    dropdownItem2: "token-input-dropdown-item2",
+    selectedDropdownItem: "token-input-selected-dropdown-item",
+    inputToken: "token-input-input-token"
+};
+
+// Input box position "enum"
+var POSITION = {
+    BEFORE: 0,
+    AFTER: 1,
+    END: 2
+};
+
+// Keys "enum"
+var KEY = {
+    BACKSPACE: 8,
+    TAB: 9,
+    ENTER: 13,
+    ESCAPE: 27,
+    SPACE: 32,
+    PAGE_UP: 33,
+    PAGE_DOWN: 34,
+    END: 35,
+    HOME: 36,
+    LEFT: 37,
+    UP: 38,
+    RIGHT: 39,
+    DOWN: 40,
+    NUMPAD_ENTER: 108,
+    COMMA: 188
+};
+
+// Additional public (exposed) methods
+var methods = {
+    init: function(url_or_data_or_function, options) {
+        var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
+
+        return this.each(function () {
+            $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
+        });
+    },
+    clear: function() {
+        this.data("tokenInputObject").clear();
+        return this;
+    },
+    add: function(item) {
+        this.data("tokenInputObject").add(item);
+        return this;
+    },
+    remove: function(item) {
+        this.data("tokenInputObject").remove(item);
+        return this;
+    }
+}
+
+// Expose the .tokenInput function to jQuery as a plugin
+$.fn.tokenInput = function (method) {
+    // Method calling and initialization logic
+    if(methods[method]) {
+        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+    } else {
+        return methods.init.apply(this, arguments);
+    }
+};
+
+// TokenList class for each input
+$.TokenList = function (input, url_or_data, settings) {
+    //
+    // Initialization
+    //
+
+    // Configure the data source
+    if(typeof(url_or_data) === "string") {
+        // Set the url to query against
+        settings.url = url_or_data;
+
+        // Make a smart guess about cross-domain if it wasn't explicitly specified
+        if(settings.crossDomain === undefined) {
+            if(settings.url.indexOf("://") === -1) {
+                settings.crossDomain = false;
+            } else {
+                settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]);
+            }
+        }
+    } else if(typeof(url_or_data) === "object") {
+        // Set the local data to search through
+        settings.local_data = url_or_data;
+    }
+
+    // Build class names
+    if(settings.classes) {
+        // Use custom class names
+        settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
+    } else if(settings.theme) {
+        // Use theme-suffixed default class names
+        settings.classes = {};
+        $.each(DEFAULT_CLASSES, function(key, value) {
+            settings.classes[key] = value + "-" + settings.theme;
+        });
+    } else {
+        settings.classes = DEFAULT_CLASSES;
+    }
+
+
+    // Save the tokens
+    var saved_tokens = [];
+
+    // Keep track of the number of tokens in the list
+    var token_count = 0;
+
+    // Basic cache to save on db hits
+    var cache = new $.TokenList.Cache();
+
+    // Keep track of the timeout, old vals
+    var timeout;
+    var input_val;
+
+    // Create a new text input an attach keyup events
+    var input_box = $("<input type=\"text\" autocomplete=\"off\">")
+        .css({
+            outline: "none"
+        })
+        .attr("id", settings.idPrefix + input.id)
+        .click(function () {
+            if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
+                show_dropdown_hint();
+            }
+        })
+        .blur(function () {
+            hide_dropdown();
+            $(this).val("");
+        })
+        .bind("keyup keydown blur update", resize_input)
+        .keydown(function (event) {
+            var previous_token;
+            var next_token;
+
+            switch(event.keyCode) {
+                case KEY.LEFT:
+                case KEY.RIGHT:
+                case KEY.UP:
+                case KEY.DOWN:
+                    if(!$(this).val()) {
+                        previous_token = input_token.prev();
+                        next_token = input_token.next();
+
+                        if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
+                            // Check if there is a previous/next token and it is selected
+                            if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
+                                deselect_token($(selected_token), POSITION.BEFORE);
+                            } else {
+                                deselect_token($(selected_token), POSITION.AFTER);
+                            }
+                        } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
+                            // We are moving left, select the previous token if it exists
+                            select_token($(previous_token.get(0)));
+                        } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
+                            // We are moving right, select the next token if it exists
+                            select_token($(next_token.get(0)));
+                        }
+                    } else {
+                        var dropdown_item = null;
+
+                        if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
+                            dropdown_item = $(selected_dropdown_item).next();
+                        } else {
+                            dropdown_item = $(selected_dropdown_item).prev();
+                        }
+
+                        if(dropdown_item.length) {
+                            select_dropdown_item(dropdown_item);
+                        }
+                        return false;
+                    }
+                    break;
+
+                case KEY.BACKSPACE:
+                    previous_token = input_token.prev();
+
+                    if(!$(this).val().length) {
+                        if(selected_token) {
+                            delete_token($(selected_token));
+                        } else if(previous_token.length) {
+                            select_token($(previous_token.get(0)));
+                        }
+
+                        return false;
+                    } else if($(this).val().length === 1) {
+                        hide_dropdown();
+                    } else {
+                        // set a timeout just long enough to let this function finish.
+                        setTimeout(function(){do_search();}, 5);
+                    }
+                    break;
+
+                case KEY.TAB:
+                case KEY.ENTER:
+                case KEY.NUMPAD_ENTER:
+                // Comma should NOT select token CRM-8488
+                // case KEY.COMMA:
+                  if(selected_dropdown_item) {
+                    add_token($(selected_dropdown_item).data("tokeninput"));
+                    return false;
+                  }
+                  break;
+
+                case KEY.ESCAPE:
+                  hide_dropdown();
+                  return true;
+
+                default:
+                    if(String.fromCharCode(event.which)) {
+                        // set a timeout just long enough to let this function finish.
+                        setTimeout(function(){do_search();}, 5);
+                    }
+                    break;
+            }
+        });
+
+    // Keep a reference to the original input box
+    var hidden_input = $(input)
+                           .hide()
+                           .val("")
+                           .focus(function () {
+                               input_box.focus();
+                           })
+                           .blur(function () {
+                               input_box.blur();
+                           });
+
+    // Keep a reference to the selected token and dropdown item
+    var selected_token = null;
+    var selected_token_index = 0;
+    var selected_dropdown_item = null;
+
+    // The list to store the token items in
+    var token_list = $("<ul />")
+        .addClass(settings.classes.tokenList)
+        .click(function (event) {
+            var li = $(event.target).closest("li");
+            if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
+                toggle_select_token(li);
+            } else {
+                // Deselect selected token
+                if(selected_token) {
+                    deselect_token($(selected_token), POSITION.END);
+                }
+
+                // Focus input box
+                input_box.focus();
+            }
+        })
+        .mouseover(function (event) {
+            var li = $(event.target).closest("li");
+            if(li && selected_token !== this) {
+                li.addClass(settings.classes.highlightedToken);
+            }
+        })
+        .mouseout(function (event) {
+            var li = $(event.target).closest("li");
+            if(li && selected_token !== this) {
+                li.removeClass(settings.classes.highlightedToken);
+            }
+        })
+        .insertBefore(hidden_input);
+
+    // The token holding the input box
+    var input_token = $("<li />")
+        .addClass(settings.classes.inputToken)
+        .appendTo(token_list)
+        .append(input_box);
+
+    // The list to store the dropdown items in
+    var dropdown = $("<div>")
+        .addClass(settings.classes.dropdown)
+        .appendTo("body")
+        .hide();
+
+    // Magic element to help us resize the text input
+    var input_resizer = $("<tester/>")
+        .insertAfter(input_box)
+        .css({
+            position: "absolute",
+            top: -9999,
+            left: -9999,
+            width: "auto",
+            fontSize: input_box.css("fontSize"),
+            fontFamily: input_box.css("fontFamily"),
+            fontWeight: input_box.css("fontWeight"),
+            letterSpacing: input_box.css("letterSpacing"),
+            whiteSpace: "nowrap"
+        });
+
+    // Pre-populate list if items exist
+    hidden_input.val("");
+    var li_data = settings.prePopulate || hidden_input.data("pre");
+    if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
+        li_data = settings.onResult.call(hidden_input, li_data);
+    }
+    if(li_data && li_data.length) {
+        $.each(li_data, function (index, value) {
+            insert_token(value);
+            checkTokenLimit();
+        });
+    }
+
+
+    //
+    // Public functions
+    //
+
+    this.clear = function() {
+        token_list.children("li").each(function() {
+            if ($(this).children("input").length === 0) {
+                delete_token($(this));
+            }
+        });
+    }
+
+    this.add = function(item) {
+        add_token(item);
+    }
+
+    this.remove = function(item) {
+        token_list.children("li").each(function() {
+            if ($(this).children("input").length === 0) {
+                var currToken = $(this).data("tokeninput");
+                var match = true;
+                for (var prop in item) {
+                    if (item[prop] !== currToken[prop]) {
+                        match = false;
+                        break;
+                    }
+                }
+                if (match) {
+                    delete_token($(this));
+                }
+            }
+        });
+    }
+
+    //
+    // Private functions
+    //
+
+    function checkTokenLimit() {
+        if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
+            input_box.hide();
+            hide_dropdown();
+            return;
+        } else {
+            input_box.focus();
+        }
+    }
+
+    function resize_input() {
+        if(input_val === (input_val = input_box.val())) {return;}
+
+        // Enter new content into resizer and resize input accordingly
+        var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+        input_resizer.html(escaped);
+        input_box.width(input_resizer.width() + 30);
+    }
+
+    function is_printable_character(keycode) {
+        return ((keycode >= 48 && keycode <= 90) ||     // 0-1a-z
+                (keycode >= 96 && keycode <= 111) ||    // numpad 0-9 + - / * .
+                (keycode >= 186 && keycode <= 192) ||   // ; = , - . / ^
+                (keycode >= 219 && keycode <= 222));    // ( \ ) '
+    }
+
+    // Inner function to a token to the list
+    function insert_token(item) {
+        var this_token = $("<li><p>"+ item.name +"</p></li>")
+          .addClass(settings.classes.token)
+          .insertBefore(input_token);
+
+        // The 'delete token' button
+        $("<span>" + settings.deleteText + "</span>")
+            .addClass(settings.classes.tokenDelete)
+            .appendTo(this_token)
+            .click(function () {
+                delete_token($(this).parent());
+                return false;
+            });
+
+        // Store data on the token
+        var token_data = {"id": item.id, "name": item.name};
+        $.data(this_token.get(0), "tokeninput", item);
+
+        // Save this token for duplicate checking
+        saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
+        selected_token_index++;
+
+        // Update the hidden input
+        var token_ids = $.map(saved_tokens, function (el) {
+            return el.id;
+        });
+        hidden_input.val(token_ids.join(settings.tokenDelimiter));
+
+        token_count += 1;
+
+        return this_token;
+    }
+
+    // Add a token to the token list based on user input
+    function add_token (item) {
+        var callback = settings.onAdd;
+
+        // See if the token already exists and select it if we don't want duplicates
+        if(token_count > 0 && settings.preventDuplicates) {
+            var found_existing_token = null;
+            token_list.children().each(function () {
+                var existing_token = $(this);
+                var existing_data = $.data(existing_token.get(0), "tokeninput");
+                if(existing_data && existing_data.id === item.id) {
+                    found_existing_token = existing_token;
+                    return false;
+                }
+            });
+
+            if(found_existing_token) {
+                select_token(found_existing_token);
+                input_token.insertAfter(found_existing_token);
+                //input_box.focus();
+                return;
+            }
+        }
+
+        // Insert the new tokens
+        insert_token(item);
+        checkTokenLimit();
+
+        // Clear input box
+        input_box.val("");
+
+        // Don't show the help dropdown, they've got the idea
+        hide_dropdown();
+
+        // Execute the onAdd callback if defined
+        if($.isFunction(callback)) {
+            callback.call(hidden_input,item);
+        }
+    }
+
+    // Select a token in the token list
+    function select_token (token) {
+        token.addClass(settings.classes.selectedToken);
+        selected_token = token.get(0);
+
+        // Hide input box
+        input_box.val("");
+
+        // Hide dropdown if it is visible (eg if we clicked to select token)
+        hide_dropdown();
+    }
+
+    // Deselect a token in the token list
+    function deselect_token (token, position) {
+        token.removeClass(settings.classes.selectedToken);
+        selected_token = null;
+
+        if(position === POSITION.BEFORE) {
+            input_token.insertBefore(token);
+            selected_token_index--;
+        } else if(position === POSITION.AFTER) {
+            input_token.insertAfter(token);
+            selected_token_index++;
+        } else {
+            input_token.appendTo(token_list);
+            selected_token_index = token_count;
+        }
+
+        // Show the input box and give it focus again
+        input_box.focus();
+    }
+
+    // Toggle selection of a token in the token list
+    function toggle_select_token(token) {
+        var previous_selected_token = selected_token;
+
+        if(selected_token) {
+            deselect_token($(selected_token), POSITION.END);
+        }
+
+        if(previous_selected_token === token.get(0)) {
+            deselect_token(token, POSITION.END);
+        } else {
+            select_token(token);
+        }
+    }
+
+    // Delete a token from the token list
+    function delete_token (token) {
+        // Remove the id from the saved list
+        var token_data = $.data(token.get(0), "tokeninput");
+        var callback = settings.onDelete;
+
+        var index = token.prevAll().length;
+        if(index > selected_token_index) index--;
+
+        // Delete the token
+        token.remove();
+        selected_token = null;
+
+        // Show the input box and give it focus again
+        input_box.focus();
+
+        // Remove this token from the saved list
+        saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
+        if(index < selected_token_index) selected_token_index--;
+
+        // Update the hidden input
+        var token_ids = $.map(saved_tokens, function (el) {
+            return el.id;
+        });
+        hidden_input.val(token_ids.join(settings.tokenDelimiter));
+
+        token_count -= 1;
+
+        if(settings.tokenLimit !== null) {
+            input_box
+                .show()
+                .val("")
+                .focus();
+        }
+
+        // Execute the onDelete callback if defined
+        if($.isFunction(callback)) {
+            callback.call(hidden_input,token_data);
+        }
+    }
+
+    // Hide and clear the results dropdown
+    function hide_dropdown () {
+        dropdown.hide().empty();
+        selected_dropdown_item = null;
+    }
+
+    function show_dropdown() {
+        dropdown
+            .css({
+                position: "absolute",
+                top: $(token_list).offset().top + $(token_list).outerHeight(),
+                left: $(token_list).offset().left,
+                zindex: 999
+            })
+            .show();
+    }
+
+    function show_dropdown_searching () {
+        if(settings.searchingText) {
+            dropdown.html("<p>"+settings.searchingText+"</p>");
+            show_dropdown();
+        }
+    }
+
+    function show_dropdown_hint () {
+        if(settings.hintText) {
+            dropdown.html("<p>"+settings.hintText+"</p>");
+            show_dropdown();
+        }
+    }
+
+    // Highlight the query part of the search term
+    function highlight_term(value, term) {
+        return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
+    }
+
+    // Populate the results dropdown with some results
+    function populate_dropdown (query, results) {
+        if(results && results.length) {
+            dropdown.empty();
+            var dropdown_ul = $("<ul>")
+                .appendTo(dropdown)
+                .mouseover(function (event) {
+                    select_dropdown_item($(event.target).closest("li"));
+                })
+                .mousedown(function (event) {
+                    add_token($(event.target).closest("li").data("tokeninput"));
+                    return false;
+                })
+                .hide();
+
+            $.each(results, function(index, value) {
+                var this_li = $("<li>" + highlight_term(value.name, query) + "</li>")
+                                  .appendTo(dropdown_ul);
+
+                if(index % 2) {
+                    this_li.addClass(settings.classes.dropdownItem);
+                } else {
+                    this_li.addClass(settings.classes.dropdownItem2);
+                }
+
+                if(index === 0) {
+                    select_dropdown_item(this_li);
+                }
+
+                $.data(this_li.get(0), "tokeninput", value);
+            });
+
+            show_dropdown();
+
+            if(settings.animateDropdown) {
+                dropdown_ul.slideDown("fast");
+            } else {
+                dropdown_ul.show();
+            }
+        } else {
+            if(settings.noResultsText) {
+                dropdown.html("<p>"+settings.noResultsText+"</p>");
+                show_dropdown();
+            }
+        }
+    }
+
+    // Highlight an item in the results dropdown
+    function select_dropdown_item (item) {
+        if(item) {
+            if(selected_dropdown_item) {
+                deselect_dropdown_item($(selected_dropdown_item));
+            }
+
+            item.addClass(settings.classes.selectedDropdownItem);
+            selected_dropdown_item = item.get(0);
+        }
+    }
+
+    // Remove highlighting from an item in the results dropdown
+    function deselect_dropdown_item (item) {
+        item.removeClass(settings.classes.selectedDropdownItem);
+        selected_dropdown_item = null;
+    }
+
+    // Do a search and show the "searching" dropdown if the input is longer
+    // than settings.minChars
+    function do_search() {
+        var query = input_box.val().toLowerCase();
+
+        if(query && query.length) {
+            if(selected_token) {
+                deselect_token($(selected_token), POSITION.AFTER);
+            }
+
+            if(query.length >= settings.minChars) {
+                show_dropdown_searching();
+                clearTimeout(timeout);
+
+                timeout = setTimeout(function(){
+                    run_search(query);
+                }, settings.searchDelay);
+            } else {
+                hide_dropdown();
+            }
+        }
+    }
+
+    // Do the actual search
+    function run_search(query) {
+        var cached_results = cache.get(query);
+        if(cached_results) {
+            populate_dropdown(query, cached_results);
+        } else {
+            // Are we doing an ajax search or local data search?
+            if(settings.url) {
+                // Extract exisiting get params
+                var ajax_params = {};
+                ajax_params.data = {};
+                if(settings.url.indexOf("?") > -1) {
+                    var parts = settings.url.split("?");
+                    ajax_params.url = parts[0];
+
+                    var param_array = parts[1].split("&");
+                    $.each(param_array, function (index, value) {
+                        var kv = value.split("=");
+                        ajax_params.data[kv[0]] = kv[1];
+                    });
+                } else {
+                    ajax_params.url = settings.url;
+                }
+
+                // Prepare the request
+                ajax_params.data[settings.queryParam] = query;
+                ajax_params.type = settings.method;
+                ajax_params.dataType = settings.contentType;
+                if(settings.crossDomain) {
+                    ajax_params.dataType = "jsonp";
+                }
+
+                // Attach the success callback
+                ajax_params.success = function(results) {
+                  if($.isFunction(settings.onResult)) {
+                      results = settings.onResult.call(hidden_input, results);
+                  }
+                  cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
+
+                  // only populate the dropdown if the results are associated with the active search query
+                  if(input_box.val().toLowerCase() === query) {
+                      populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
+                  }
+                };
+
+                // Make the request
+                $.ajax(ajax_params);
+            } else if(settings.local_data) {
+                // Do the search through local data
+                var results = $.grep(settings.local_data, function (row) {
+                    return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1;
+                });
+
+                if($.isFunction(settings.onResult)) {
+                    results = settings.onResult.call(hidden_input, results);
+                }
+                cache.add(query, results);
+                populate_dropdown(query, results);
+            }
+        }
+    }
+};
+
+// Really basic cache for the results
+$.TokenList.Cache = function (options) {
+    var settings = $.extend({
+        max_size: 500
+    }, options);
+
+    var data = {};
+    var size = 0;
+
+    var flush = function () {
+        data = {};
+        size = 0;
+    };
+
+    this.add = function (query, results) {
+        if(size > settings.max_size) {
+            flush();
+        }
+
+        if(!data[query]) {
+            size += 1;
+        }
+
+        data[query] = results;
+    };
+
+    this.get = function (query) {
+        return data[query];
+    };
+};
+}(jQuery));
diff --git a/plugins/jquery.toolTip.js b/plugins/jquery.toolTip.js
new file mode 100644
index 0000000..c14b6f2
--- /dev/null
+++ b/plugins/jquery.toolTip.js
@@ -0,0 +1,54 @@
+// +--------------------------------------------------------------------+
+// | CiviCRM version 4.2                                                |
+// +--------------------------------------------------------------------+
+// | Copyright CiviCRM LLC (c) 2004-2012                                |
+// +--------------------------------------------------------------------+
+// | This file is a part of CiviCRM.                                    |
+// |                                                                    |
+// | CiviCRM is free software; you can copy, modify, and distribute it  |
+// | under the terms of the GNU Affero General Public License           |
+// | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+// |                                                                    |
+// | CiviCRM 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 Affero General Public License for more details.        |
+// |                                                                    |
+// | You should have received a copy of the GNU Affero General Public   |
+// | License and the CiviCRM Licensing Exception along                  |
+// | with this program; if not, contact CiviCRM LLC                     |
+// | at info[AT]civicrm[DOT]org. If you have questions about the        |
+// | GNU Affero General Public License or the licensing of CiviCRM,     |
+// | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+// +--------------------------------------------------------------------+
+(function($){ $.fn.toolTip = function(){
+  var clickedElement = null;
+  return this.each(function() {
+    var text = $(this).children().find('div.crm-help').html();
+    if(text != undefined) {
+      $(this).bind( 'click', function(e){
+		$("#crm-toolTip").remove();
+		if ( clickedElement == $(this).children().attr('id') ) { clickedElement = null; return; }
+		 $("body").append('<div id="crm-toolTip" style="z-index: 100;"><div id="hide-tooltip" class="ui-icon ui-icon-close"></div>' + text + "</div>");
+		  if ($.browser.msie && $.browser.version.substr(0,1)<7) {
+		  	$("#crm-toolTip").css('position','absolute');
+			$(window).bind('scroll', function() {
+				var windowheight = $(window).height();
+				var toolTipBottom = $(window).scrollTop() + 30;
+				var posFromTop = windowheight+toolTipBottom;
+				$("#crm-toolTip").css("top", toolTipBottom + "px");
+				});
+			};
+		
+		  $("#crm-toolTip").fadeIn("medium");
+		  clickedElement = cj(this).children().attr('id');
+	      })
+	      .bind( 'mouseout', function() {
+			$('#hide-tooltip').click( function() {
+			  $("#crm-toolTip").hide();
+			});
+	     });
+    	}
+  	});
+}})(jQuery);
+
diff --git a/plugins/jquery.ui.datepicker.validation.pack.js b/plugins/jquery.ui.datepicker.validation.pack.js
new file mode 100644
index 0000000..449289c
--- /dev/null
+++ b/plugins/jquery.ui.datepicker.validation.pack.js
@@ -0,0 +1,14 @@
+﻿/* Datepicker Validation 1.0.1 for jQuery UI Datepicker 1.8.6.
+
+   Requires Jörn Zaefferer's Validation plugin (http://plugins.jquery.com/project/validate).
+
+   Written by Keith Wood (kbwood{at}iinet.com.au).
+
+   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
+
+   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
+
+   Please attribute the author if you use it. */
+
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(5($){7($.U.B){$.2.V=$.2.W;$.C($.2.X[\'\'],{D:\'q r a 1m m\',Y:\'q r a m Z 10 t {0}\',11:\'q r a m Z 10 u {0}\',12:\'q r a m 1n {0} 1o {1}\',13:\'q r a m {0} {1}\',14:\'w\',15:\'1p 1q m\',1r:\'E 16\',1s:\'F E 16\',1t:\'u\',1u:\'t\',1v:\'F t\',1w:\'F u\'});$.C($.2.G,$.2.X[\'\']);$.C($.2,{W:5(a,b){x.V(a,b);3 c=$(a);3 d=x.s(c[0]);7(!d.1x&&$.U.B)c.1y(\'1z\').B().1A(c)},1B:5(a,b){3 c=b.1C(\'.\'+$.2.17);3 d=18;7(c.H==0){c=b.1D(\'.\'+$.2.17);d=(c.H>0)}a[d?\'1E\':\'1F\'](c.H>0?c:b)},y:5(a,b,c){3 d=$.2.8(a,\'z\');$.1G(c,5(i,v){b=b.I(19 1H(\'\\\\{\'+i+\'\\\\}\',\'g\'),$.2.1a(d,v)||\'1I\')});6 b}});3 k=9;$.J.1b(\'K\',5(a,b,c){k=b;3 d=$.2.s(b);3 f=$.2.8(d,\'z\');1c{3 g=$.2.1d(f,a,$.2.L(d));3 h=$.2.A(d,$.2.8(d,\'1e\'),9);3 i=$.2.A(d,$.2.8(d,\'1f\'),9);3 j=$.2.8(d,\'1J\');6 x.1g(b)||!g||((!h||g>=h)&&(!i||g<=i)&&(!j||j.1K(b,[g])[0]))}1h(e){6 18}},5(a){3 b=$.2.s(k);3 c=$.2.A(b,$.2.8(b,\'1e\'),9);3 d=$.2.A(b,$.2.8(b,\'1f\'),9);3 e=$.2.G;6(c&&d?$.2.y(b,e.12,[c,d]):(c?$.2.y(b,e.Y,[c]):(d?$.2.y(b,e.11,[d]):e.D)))});$.J.1L(\'K\',{K:n});3 l={E:\'M\',1M:\'M\',1N:\'N\',1O:\'N\',1P:\'O\',u:\'O\',1Q:\'P\',t:\'P\',1R:\'Q\',1S:\'Q\',1T:\'R\',1U:\'R\'};$.J.1b(\'1V\',5(a,b,c){7(x.1g(b)){6 n}c=S(c);3 d=$(b).2(\'1i\');3 e=T(b,c[1]);7(!d||!e){6 n}k=b;3 f=n;1W(l[c[0]]||c[0]){o\'M\':f=(d.4()==e.4());p;o\'N\':f=(d.4()!=e.4());p;o\'O\':f=(d.4()<e.4());p;o\'P\':f=(d.4()>e.4());p;o\'R\':f=(d.4()<=e.4());p;o\'Q\':f=(d.4()>=e.4());p;1X:f=n}6 f},5(a){3 b=$.2.s(k);3 c=$.2.G;a=S(a);3 d=T(k,a[1],n);d=(a[1]==\'w\'?c.14:(d?$.2.1a($.2.8(b,\'z\'),d,$.2.L(b)):c.15));6 c.13.I(/\\{0\\}/,c[\'D\'+(l[a[0]]||a[0]).1Y()]).I(/\\{1\\}/,d)});5 S(a){7(1j a==\'1k\'){a=a.1Z(\' \')}20 7(!$.21(a)){3 b=[];22(3 c 23 a){b[0]=c;b[1]=a[c]}a=b}6 a}5 T(a,b,c){7(b.24==1l){6 b}3 d=$.2.s(a);3 f=9;1c{7(1j b==\'1k\'&&b!=\'w\'){f=$.2.1d($.2.8(d,\'z\'),b,$.2.L(d))}}1h(e){}f=(f?f:(b==\'w\'?19 1l():(c?9:$(b).2(\'1i\'))));7(f){f.25(0,0,0,0)}6 f}}})(26);',62,131,'||datepicker|var|getTime|function|return|if|_get|null|||||||||||||date|true|case|break|Please|enter|_getInst|after|before||today|this|errorFormat|dateFormat|_determineDate|validate|extend|validateDate|equal|not|_defaults|length|replace|validator|dpDate|_getFormatConfig|eq|ne|lt|gt|ge|le|normaliseParams|extractOtherDate|fn|_selectDate2|_selectDate|regional|validateDateMin|on|or|validateDateMax|validateDateMinMax|validateDateCompare|validateDateToday|validateDateOther|to|_triggerClass|false|new|formatDate|addMethod|try|parseDate|minDate|maxDate|optional|catch|getDate|typeof|string|Date|valid|between|and|the|other|validateDateEQ|validateDateNE|validateDateLT|validateDateGT|validateDateLE|validateDateGE|inline|parents|form|element|errorPlacement|next|prev|insertBefore|insertAfter|each|RegExp|nothing|beforeShowDay|apply|addClassRules|same|notEqual|notSame|lessThan|greaterThan|notLessThan|notBefore|notGreaterThan|notAfter|dpCompareDate|switch|default|toUpperCase|split|else|isArray|for|in|constructor|setHours|jQuery'.split('|'),0,{}))
+


diff --git a/plugins/themes/default/throbber.gif b/plugins/themes/default/throbber.gif
new file mode 100644
index 0000000000000000000000000000000000000000..12dfc8e0213ee9107bd0c1e165d35c0c7c468bf2
GIT binary patch
literal 1849
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
Hh*s-AtGE?>

literal 0
HcmV?d00001

diff --git a/plugins/themes/marker.gif b/plugins/themes/marker.gif
new file mode 100644
index 0000000000000000000000000000000000000000..03c48f4109b7cea9a8ce05b6db776720fe34f89f
GIT binary patch
literal 61
zcmZ?wbhEHb)L>v`XkcJa$tnN;|G(l-7DfgJMg|=QAOOiQFp0SItvubEFlP>rRQKk4
OHkbdqKDZ&mU=0BF2omrB

literal 0
HcmV?d00001

diff --git a/plugins/themes/marker_rtl.gif b/plugins/themes/marker_rtl.gif
new file mode 100644
index 0000000000000000000000000000000000000000..36ab05d481c0f7d82bdf6b3a8ab415d9f14e26e7
GIT binary patch
literal 62
zcmZ?wbhEHb)L>v`XkcJa$tnN;|G(l-7DfgJMg|=QAOOiQFp2i`uRP7ptIWLRR`=#*
O3s3zod~gFOVGRHTl@j;>

literal 0
HcmV?d00001

diff --git a/plugins/themes/plus.gif b/plugins/themes/plus.gif
new file mode 100644
index 0000000000000000000000000000000000000000..fcfe0f53fc3d5f44e692e6a71f24f64af700e7ca
GIT binary patch
literal 52
zcmZ?wbhEHb<Yr)HXkcJa$tnN;|G(l-7DfgJMg|=QAOOiQFmd<rFW<p2KdPf>6%&Is
E07=ITf&c&j

literal 0
HcmV?d00001

diff --git a/plugins/themes/remove.png b/plugins/themes/remove.png
new file mode 100644
index 0000000000000000000000000000000000000000..083d3f3164e338b0b2842adee70415365ba8ef0f
GIT binary patch
literal 720
zcmV;>0x$iEP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt007wp
z+_KsN0007qNkl<Zc$|%rO-NKx6vuz}eawublaKspR2*wsX`41~l7w6clH^Vei*~NU
zpwOM<Miin|u7g1w!BJ@;x)2f)!bM?dE()3)6Y_KLYi64F=Dph@pG;QJfdlv6AOAn+
zo^#+_Jco<t-`sc#zJD<Q=EC7eiGhL5*<kSAdG@BN>i+)F(At7wwEk86s-U1}tG8Fm
z$w`?U88K(e%e!3mx}u^h9gUhY9+&A@?07!UL;y^{!#Y)cQ4$E?tFI@}+|04ZV=WC1
zK2U*%{{D!+rbfqcprV2w>+7kdo}SKI+uJi5fDPA7+a`B#KxTd(mX|4NYttM1`l5<y
z;>+bAkpRCRvRQn^#f622(ZKfhj0%VjSlNNl1*j_oWIBzryo@iM##2@XsT3lU0o%rL
z9NrHPPpz-54aVS51&F~>2Q2MCbrID1Prv4DY#{dc5#{A5mW65C^4-|jRAP1YNjrS|
zqmfaloYAzEO(EoWkozDHL1w~XDZ9Jn$Cj24QK-z<D8D)AdebGJ?}{lU7Mx`^9w)oF
zNH7!%8^K^xzC?8oW6)KpX)ldTrocHJkDC&(_xA8h=`C$+Y;D}y`Z|7cvU+9^Rg}91
zL@wC2?ur$_@_4Wu2ldya$R(5bElVw}ufNl@yZbqsO08)CQrGK5AfTDaB#z^t0H)VV
zLQ!Tc(-gIEy9CH(v&eWHyk1Swv|6_U(P`VCj%>R`HH_=tY?h1=Y``R@`B=c*7E~2L
zogX;TbykzfNP2hoSqzTPHgX)QN4?(2v|-3Gn3{l)3nw5n3R4Rpr-g`&L$$lbBCnxh
z0xnx{Q-`<h@a43BF1u>M4ILKSVcli<2Iuecf7nm#JPK(0gG^BX0000<MNUMnLSTYl
C2wIl_

literal 0
HcmV?d00001
