Commits

James Taylor committed 75c6764

Adding select2 patch

Comments (0)

Files changed (3)

select2-for-big-selects

+# HG changeset patch
+# Parent b3a3660604f7d9782ab48b1c96fd8f44635203ca
+diff --git a/static/images/select2-spinner.gif b/static/images/select2-spinner.gif
+new file mode 100755
+index 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4
+GIT binary patch
+literal 1849
+zc$}5hdr(tX9tZI2z31lM+(&YVk%mZ}5P~KlG2s=WSobD81<C?dpgc+<h7t-jP!X0Q
+zk_SQ}Pi1wnSap|eQLqJkOkYHd%xK%Om8sKCUo+UD9c^t3eN1(xtxNice`VU)^XGTY
+zZ{{=KGiQF8+cJ!stPp|7PYCkh!2<+AmX?-$KHtK^LPkbLYisLDQc_Z)P$(uQCj5TC
+zSS%hH836zo42GJT8sz^uQ4i?6^zBcin)1`*H|nuf!RU8sYNO4%$F@t>Y~NEOv({Q$
+z>ULXqZL~jIy&H+T5BSu|ElqGbh(z#anG8kwQD2UsX=d`OX<$4n(z|diFd;g3**b~6
+zl;#S+uQ=IL7saMKCHDsMN4e$K<U8E1a|O}-_imY;<tZnw(^uloh!_dJ`~A)F3h7>q
+z1C*jolK_M?#R6AlG)nRib{FVK0=$Hya{9R>nzluSvg{37?3`^=Uz&-0+XQ>2eRIpR
+zf-v29gvC*#ko2ZnV>lV!bM=lR>oiFohm$Qc8K^VR^n_Oo-K$!nJ8>3(z{X3Tdk#D)
+zuI>v1xoD*v!zl-qV0ITf0R(h+MO!Y9EjJu`zOD6`@py7sa!5)`kn%CJLIG)Hx}5Bv
+z_Icd1@r|1CM@}~6&xyOx4n!=eJs+?v86l7OMik&6Y1|o}CDO>$^IzyC+Lzd+`ix@9
+zcZ-{5a`Q*i6Jk*cDqW~q&hqH$-sij3=Hg17cR$K<l?tz`J*Ux`Y-$XR`J<PQ6IU>v
+zC^BnV{;pcozm~pH(68g%Q{7qt6gd++Ai%GjcEChIPhspYZ)ruuiQ5+(XUS_XTjukX
+zM+~I<*2&}2r5(CoWqguWYk)@uFlDT+LSFaxPTc{u>6yX(hkbO;P>-*|)NU}GG=!wo
+z{qaPSU@vm=>hii3O4nMNneii-_CupS3y54(9gpRuoMLXg6Sm!xe)k5P34dkHcMWTx
+z^!DhtQ??hGuXpUuiLWd!{o(VTBJcUQ+NX<wjoZ_drPDQA{?_TOZjV0XJz^L-932wS
+z9t6CuSpm4@q<dL9r=||1Fpk1=F{o!UTF)CjW;rFBm2S9FHLM&B$A+>wE^^(aM=oCw
+ziKV)@%XTagb_XmoMQ5d6fSJGW7XcyLw9g~2A_&}?XV)=A1;MCPy;_1qa&2|{`2<~W
+z--iH#t+$oMh9vzXNZJNhNC7$d_>YD;bzn@MoPPa?$H`?XmAUJ0IvQ%Z($1H?_umMU
+zF`n;ZxU&G_%@d_68T2^nFeTOB<-B>ek0cv@Zc?ufCy2uJRXoTiEQC;{QZfWaJh#6x
+zdjNEAtt=N18w8p4$z+crw5Iu?232bg%9L|zzw%deQkf`DvT9oP-_q$1wpnYUc$r7f
+z*jgm5(|5D=-L^}H*#KA6wqqOz%ZYDXOsk*nj8O=#zwnJi!chEj0?$OS7CxWdhj6i=
+z4KNHUBp!>Uh`7UAe!4XYtyBF;Gpdoru@^U(MsiLcdUfHGUd_KR{w4s2z3Ju3I`_!w
+z^(jfR-UXN>O7qS|bm(udJD1?HPUy9wgKGvZXd?ylkDi*941vBYZbw$47~(~I7{l5z
+z0uu@;M_Hk~jgR)Aq+l?b8b<j-18y&Eu4-2Qg+`Mzq`rS~4m~)gKS5iMrF?Yo#^kCe
+zjn18x#5>Q9uA>8E=ADRZ=bJ5&eq1wk`DR#zfAHkgyW#%ps~6{$d=YVNX7W)Pph;c~
+zwG|^+5~mp|UJVE#>O*-#yPD?faC|cyj#XgT-q4z+hnl*wGm2Z(bT(`#`%j>ONf@>i
+zl-&~~t#;nq93k-ia|vuKiPFzDn$nCCCUx)lz}sT{v#k7TdzgRGjuZ@mezz%~bF77_
+zJT@i`Ve@$q2n!Mo48DS4?NxRZl6kfIX4az${iPoEvXN=<k5QUTlzt@}B8|#dBeMsL
+z4B3^sw5SgAbsaFD*9=ShI=@~o*+O+Xd(M9vA*z{TircmDtwGDYhR<{z`FT{Lxw{oZ
+qAuokujfHtzG&I%;x5RB1!U`Tr{i_NL9^Jq;JfonvpeE(vF8d$98xD^E
+
+diff --git a/static/images/select2.png b/static/images/select2.png
+new file mode 100644
+index 0000000000000000000000000000000000000000..1d804ffb99699b9e030f1010314de0970b5a000d
+GIT binary patch
+literal 613
+zc$@)c0-F7aP)<h;3K|Lk000e1NJLTq002Ay001Zm1^@s6qPv;@0006kNkl<Zc-rmR
+z&r1|x9LMps2uh5F)-GaQDk#Z_4iR>#WY!I$JQV$)A5aAS1BM||2XVJl=+L1^1S1H%
+zM-&lx?NZpUrHhn>fk<>POqf2sh40}xxGZfc+t+#Eb(q<Oecn6I%nq|DNy|zSyexzN
+z$5AL0?&Dan9pN5UxC<ug5`<4^!bTJ!40{Z2#(M~pn1C$$pw}f`kJpeHM`McNHFyg?
+zK;|>Hy9_3*1(U%t9t)QDnI#YAL(|ACV(>)>6WD-t!8tutHkdb^#3`HzoJG3A2@T`%
+zA|K@o*b!`R#(7)PWrMFn2))Ca3MR4(zaT`Zr61*kZK5NPnZwQszxh$fyv3?&4c>$q
+z2m=+yc0dRXRAsPDxF6sD;@rK4JGdR_``1S~o6Xi@2&aR6hcSrEp9HVRzEqVDqB<bN
+zXhRETAUt&UJ4{pqo<W$!4+ziE?D9@4ilQ3gUO=XZ)oyPsiQA7akeNo#WiVk!@H1G(
+zkAmgh?J@W$gqNtpN{mA2@w%j2(GYBBaKPD;E<cjsBsd99f~(<?&RR6i%jfeou7io}
+z!W3j)q1|OLVRs>n<1%hR=D4e1f^ra^A|34Cjc=Gny{F(o#MrvPYgZuTJOz(n)-F<|
+zj()qR;C={)N<0RRvDZ^@6ND+W*}gh-Lip(MDt!(zMSO)!j2j+*hxgzC-e3$@(O2p*
+zu;+gddm(cZwXTCLx*Ky4THOa*^b^F`woveIeCK^0aR|TJ00000NkvXXu0mjfA#WC6
+
+diff --git a/static/june_2007_style/base.less b/static/june_2007_style/base.less
+--- a/static/june_2007_style/base.less
++++ b/static/june_2007_style/base.less
+@@ -4,6 +4,8 @@
+ 
+ @import "fontawesome/font-awesome.less";
+ 
++@import "select2.less";
++
+ // Mixins
+ 
+ .unselectable {
+diff --git a/static/june_2007_style/blue/base.css b/static/june_2007_style/blue/base.css
+--- a/static/june_2007_style/blue/base.css
++++ b/static/june_2007_style/blue/base.css
+@@ -821,7 +821,69 @@
+ .fa-icon-github-alt:before{content:"\f113";}
+ .fa-icon-folder-close-alt:before{content:"\f114";}
+ .fa-icon-folder-open-alt:before{content:"\f115";}
+-.unselectable{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;}
++.select2-container{position:relative;display:inline-block;zoom:1;*display:inline;vertical-align:top;}
++.select2-container,.select2-drop,.select2-search,.select2-search input{-moz-box-sizing:border-box;-ms-box-sizing:border-box;-webkit-box-sizing:border-box;-khtml-box-sizing:border-box;box-sizing:border-box;}
++.select2-container .select2-choice{background-color:#fff;background-image:-webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, #ffffff));background-image:-webkit-linear-gradient(center bottom, #eeeeee 0%, #ffffff 50%);background-image:-moz-linear-gradient(center bottom, #eeeeee 0%, #ffffff 50%);background-image:-o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%);background-image:-ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);background-image:linear-gradient(top, #eeeeee 0%, #ffffff 50%);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #aaa;display:block;overflow:hidden;white-space:nowrap;position:relative;height:26px;line-height:26px;padding:0 0 0 8px;color:#444;text-decoration:none;}
++.select2-container.select2-drop-above .select2-choice{border-bottom-color:#aaa;-webkit-border-radius:0px 0px 4px 4px;-moz-border-radius:0px 0px 4px 4px;border-radius:0px 0px 4px 4px;background-image:-webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, #ffffff));background-image:-webkit-linear-gradient(center bottom, #eeeeee 0%, #ffffff 90%);background-image:-moz-linear-gradient(center bottom, #eeeeee 0%, #ffffff 90%);background-image:-o-linear-gradient(bottom, #eeeeee 0%, #ffffff 90%);background-image:-ms-linear-gradient(top, #eeeeee 0%, #ffffff 90%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);background-image:linear-gradient(top, #eeeeee 0%, #ffffff 90%);}
++.select2-container .select2-choice span{margin-right:26px;display:block;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;text-overflow:ellipsis;}
++.select2-container .select2-choice abbr{display:block;position:absolute;right:26px;top:8px;width:12px;height:12px;font-size:1px;background:url('../images/select2.png') right top no-repeat;cursor:pointer;text-decoration:none;border:0;outline:0;}
++.select2-container .select2-choice abbr:hover{background-position:right -11px;cursor:pointer;}
++.select2-drop{background:#fff;color:#000;border:1px solid #aaa;border-top:0;position:absolute;top:100%;-webkit-box-shadow:0 4px 5px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 4px 5px rgba(0, 0, 0, 0.15);-o-box-shadow:0 4px 5px rgba(0, 0, 0, 0.15);box-shadow:0 4px 5px rgba(0, 0, 0, 0.15);z-index:9999;width:100%;margin-top:-1px;-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
++.select2-drop.select2-drop-above{-webkit-border-radius:4px 4px 0px 0px;-moz-border-radius:4px 4px 0px 0px;border-radius:4px 4px 0px 0px;margin-top:1px;border-top:1px solid #aaa;border-bottom:0;-webkit-box-shadow:0 -4px 5px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 -4px 5px rgba(0, 0, 0, 0.15);-o-box-shadow:0 -4px 5px rgba(0, 0, 0, 0.15);box-shadow:0 -4px 5px rgba(0, 0, 0, 0.15);}
++.select2-container .select2-choice div{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;background:#ccc;background-image:-webkit-gradient(linear, left bottom, left top, color-stop(0, #cccccc), color-stop(0.6, #eeeeee));background-image:-webkit-linear-gradient(center bottom, #cccccc 0%, #eeeeee 60%);background-image:-moz-linear-gradient(center bottom, #cccccc 0%, #eeeeee 60%);background-image:-o-linear-gradient(bottom, #cccccc 0%, #eeeeee 60%);background-image:-ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#eeeeee', GradientType=0);background-image:linear-gradient(top, #cccccc 0%, #eeeeee 60%);border-left:1px solid #aaa;position:absolute;right:0;top:0;display:block;height:100%;width:18px;}
++.select2-container .select2-choice div b{background:url('../images/select2.png') no-repeat 0 1px;display:block;width:100%;height:100%;}
++.select2-search{display:inline-block;white-space:nowrap;z-index:10000;min-height:26px;width:100%;margin:0;padding-left:4px;padding-right:4px;}
++.select2-search-hidden{display:block;position:absolute;left:-10000px;}
++.select2-search input{background:#ffffff url('../images/select2.png') no-repeat 100% -22px;background:url('../images/select2.png') no-repeat 100% -22px,-webkit-gradient(linear, left bottom, left top, color-stop(0.85, #ffffff), color-stop(0.99, #eeeeee));background:url('../images/select2.png') no-repeat 100% -22px,-webkit-linear-gradient(center bottom, #ffffff 85%, #eeeeee 99%);background:url('../images/select2.png') no-repeat 100% -22px,-moz-linear-gradient(center bottom, #ffffff 85%, #eeeeee 99%);background:url('../images/select2.png') no-repeat 100% -22px,-o-linear-gradient(bottom, #ffffff 85%, #eeeeee 99%);background:url('../images/select2.png') no-repeat 100% -22px,-ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);background:url('../images/select2.png') no-repeat 100% -22px,linear-gradient(top, #ffffff 85%, #eeeeee 99%);padding:4px 20px 4px 5px;outline:0;border:1px solid #aaa;font-family:sans-serif;font-size:1em;width:100%;margin:0;height:auto !important;min-height:26px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0;}
++.select2-drop.select2-drop-above .select2-search input{margin-top:4px;}
++.select2-search input.select2-active{background:#ffffff url('../images/select2-spinner.gif') no-repeat 100%;background:url('../images/select2-spinner.gif') no-repeat 100%,-webkit-gradient(linear, left bottom, left top, color-stop(0.85, #ffffff), color-stop(0.99, #eeeeee));background:url('../images/select2-spinner.gif') no-repeat 100%,-webkit-linear-gradient(center bottom, #ffffff 85%, #eeeeee 99%);background:url('../images/select2-spinner.gif') no-repeat 100%,-moz-linear-gradient(center bottom, #ffffff 85%, #eeeeee 99%);background:url('../images/select2-spinner.gif') no-repeat 100%,-o-linear-gradient(bottom, #ffffff 85%, #eeeeee 99%);background:url('../images/select2-spinner.gif') no-repeat 100%,-ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);background:url('../images/select2-spinner.gif') no-repeat 100%,linear-gradient(top, #ffffff 85%, #eeeeee 99%);}
++.select2-container-active .select2-choice,.select2-container-active .select2-choices{-webkit-box-shadow:0 0 5px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 0 5px rgba(0, 0, 0, 0.3);-o-box-shadow:0 0 5px rgba(0, 0, 0, 0.3);box-shadow:0 0 5px rgba(0, 0, 0, 0.3);border:1px solid #5897fb;outline:none;}
++.select2-dropdown-open .select2-choice{border:1px solid #aaa;border-bottom-color:transparent;-webkit-box-shadow:0 1px 0 #fff inset;-moz-box-shadow:0 1px 0 #fff inset;-o-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset;background-color:#eee;background-image:-webkit-gradient(linear, left bottom, left top, color-stop(0, #ffffff), color-stop(0.5, #eeeeee));background-image:-webkit-linear-gradient(center bottom, #ffffff 0%, #eeeeee 50%);background-image:-moz-linear-gradient(center bottom, #ffffff 0%, #eeeeee 50%);background-image:-o-linear-gradient(bottom, #ffffff 0%, #eeeeee 50%);background-image:-ms-linear-gradient(top, #ffffff 0%, #eeeeee 50%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);background-image:linear-gradient(top, #ffffff 0%, #eeeeee 50%);-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;border-bottom-left-radius:0;border-bottom-right-radius:0;}
++.select2-dropdown-open .select2-choice div{background:transparent;border-left:none;}
++.select2-dropdown-open .select2-choice div b{background-position:-18px 1px;}
++.select2-results{margin:4px 4px 4px 0;padding:0 0 0 4px;position:relative;overflow-x:hidden;overflow-y:auto;max-height:200px;}
++.select2-results ul.select2-result-sub{margin:0 0 0 0;}
++.select2-results ul.select2-result-sub>li .select2-result-label{padding-left:20px;}
++.select2-results ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:40px;}
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:60px;}
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:80px;}
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:100px;}
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:110px;}
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:120px;}
++.select2-results li{list-style:none;display:list-item;}
++.select2-results li.select2-result-with-children>.select2-result-label{font-weight:bold;}
++.select2-results .select2-result-label{padding:3px 7px 4px;margin:0;cursor:pointer;}
++.select2-results .select2-highlighted{background:#3875d7;color:#fff;}
++.select2-results li em{background:#feffde;font-style:normal;}
++.select2-results .select2-highlighted em{background:transparent;}
++.select2-results .select2-no-results,.select2-results .select2-searching,.select2-results .select2-selection-limit{background:#f4f4f4;display:list-item;}
++.select2-results .select2-disabled{display:none;}
++.select2-more-results.select2-active{background:#f4f4f4 url('spinner.gif') no-repeat 100%;}
++.select2-more-results{background:#f4f4f4;display:list-item;}
++.select2-container.select2-container-disabled .select2-choice{background-color:#f4f4f4;background-image:none;border:1px solid #ddd;cursor:default;}
++.select2-container.select2-container-disabled .select2-choice div{background-color:#f4f4f4;background-image:none;border-left:0;}
++.select2-container-multi .select2-choices{background-color:#fff;background-image:-webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));background-image:-webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);background-image:-moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);background-image:-o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);background-image:-ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);background-image:linear-gradient(top, #eeeeee 1%, #ffffff 15%);border:1px solid #aaa;margin:0;padding:0;cursor:text;overflow:hidden;height:auto !important;height:1%;position:relative;}
++.select2-container-multi .select2-choices{min-height:26px;}
++.select2-container-multi.select2-container-active .select2-choices{-webkit-box-shadow:0 0 5px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 0 5px rgba(0, 0, 0, 0.3);-o-box-shadow:0 0 5px rgba(0, 0, 0, 0.3);box-shadow:0 0 5px rgba(0, 0, 0, 0.3);border:1px solid #5897fb;outline:none;}
++.select2-container-multi .select2-choices li{float:left;list-style:none;}
++.select2-container-multi .select2-choices .select2-search-field{white-space:nowrap;margin:0;padding:0;}
++.select2-container-multi .select2-choices .select2-search-field input{color:#666;background:transparent !important;font-family:sans-serif;font-size:100%;height:15px;padding:5px;margin:1px 0;outline:0;border:0;-webkit-box-shadow:none;-moz-box-shadow:none;-o-box-shadow:none;box-shadow:none;}
++.select2-container-multi .select2-choices .select2-search-field input.select2-active{background:#ffffff url('spinner.gif') no-repeat 100% !important;}
++.select2-default{color:#999 !important;}
++.select2-container-multi .select2-choices .select2-search-choice{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;background-color:#e4e4e4;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0);background-image:-webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));background-image:-webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);background-image:-moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);background-image:-o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);background-image:-ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);background-image:linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);-webkit-box-shadow:0 0 2px #ffffff inset,0 1px 0 rgba(0, 0, 0, 0.05);-moz-box-shadow:0 0 2px #ffffff inset,0 1px 0 rgba(0, 0, 0, 0.05);box-shadow:0 0 2px #ffffff inset,0 1px 0 rgba(0, 0, 0, 0.05);color:#333;border:1px solid #aaaaaa;line-height:13px;padding:3px 5px 3px 18px;margin:3px 0 3px 5px;position:relative;cursor:default;}
++.select2-container-multi .select2-choices .select2-search-choice span{cursor:default;}
++.select2-container-multi .select2-choices .select2-search-choice-focus{background:#d4d4d4;}
++.select2-search-choice-close{display:block;position:absolute;right:3px;top:4px;width:12px;height:13px;font-size:1px;background:url('../images/select2.png') right top no-repeat;outline:none;}
++.select2-container-multi .select2-search-choice-close{left:3px;}
++.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover{background-position:right -11px;}
++.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close{background-position:right -11px;}
++.select2-container-multi.select2-container-disabled .select2-choices{background-color:#f4f4f4;background-image:none;border:1px solid #ddd;cursor:default;}
++.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice{background-image:none;background-color:#f4f4f4;border:1px solid #ddd;padding:3px 5px 3px 5px;}
++.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close{display:none;}
++.select2-result-selectable .select2-match,.select2-result-unselectable .select2-result-selectable .select2-match{text-decoration:underline;}
++.select2-result-unselectable .select2-match{text-decoration:none;}
++.select2-offscreen{position:absolute;left:-10000px;}
++@media only screen and (-webkit-min-device-pixel-ratio:1.5){.select2-search input,.select2-search-choice-close,.select2-container .select2-choice abbr,.select2-container .select2-choice div b{background-image:url(select2x2.png) !important;background-repeat:no-repeat !important;background-size:60px 40px !important;} .select2-search input{background-position:100% -21px !important;}}.unselectable{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;}
+ .parent-width{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%;*width:90%;}
+ .clear{*zoom:1;}.clear:before,.clear:after{display:table;content:"";line-height:0;}
+ .clear:after{clear:both;}
+diff --git a/static/june_2007_style/select2.less b/static/june_2007_style/select2.less
+new file mode 100644
+--- /dev/null
++++ b/static/june_2007_style/select2.less
+@@ -0,0 +1,524 @@
++/*
++Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012
++*/
++.select2-container {
++    position: relative;
++    display: inline-block;
++    /* inline-block for ie7 */
++    zoom: 1;
++    *display: inline;
++    vertical-align: top;
++}
++
++.select2-container,
++.select2-drop,
++.select2-search,
++.select2-search input{
++  /*
++    Force border-box so that % widths fit the parent
++    container without overlap because of margin/padding.
++
++    More Info : http://www.quirksmode.org/css/box.html
++  */
++  -moz-box-sizing: border-box;    /* firefox */
++  -ms-box-sizing: border-box;     /* ie */
++  -webkit-box-sizing: border-box; /* webkit */
++  -khtml-box-sizing: border-box;  /* konqueror */
++  box-sizing: border-box;         /* css3 */
++}
++
++.select2-container .select2-choice {
++    background-color: #fff;
++    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
++    background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
++    background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
++    background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%);
++    background-image: -ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%);
++    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0);
++    background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%);
++    -webkit-border-radius: 4px;
++    -moz-border-radius: 4px;
++    border-radius: 4px;
++    -moz-background-clip: padding;
++    -webkit-background-clip: padding-box;
++    background-clip: padding-box;
++    border: 1px solid #aaa;
++    display: block;
++    overflow: hidden;
++    white-space: nowrap;
++    position: relative;
++    height: 26px;
++    line-height: 26px;
++    padding: 0 0 0 8px;
++    color: #444;
++    text-decoration: none;
++}
++
++.select2-container.select2-drop-above .select2-choice
++{
++    border-bottom-color: #aaa;
++    -webkit-border-radius:0px 0px 4px 4px;
++    -moz-border-radius:0px 0px 4px 4px;
++    border-radius:0px 0px 4px 4px;
++    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white));
++    background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%);
++    background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%);
++    background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%);
++    background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%);
++    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
++    background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%);
++}
++
++.select2-container .select2-choice span {
++    margin-right: 26px;
++    display: block;
++    overflow: hidden;
++    white-space: nowrap;
++    -o-text-overflow: ellipsis;
++    -ms-text-overflow: ellipsis;
++    text-overflow: ellipsis;
++}
++
++.select2-container .select2-choice abbr {
++  display: block;
++  position: absolute;
++  right: 26px;
++  top: 8px;
++  width: 12px;
++  height: 12px;
++  font-size: 1px;
++  background: url('../images/select2.png') right top no-repeat;
++  cursor: pointer;
++  text-decoration: none;
++  border:0;
++  outline: 0;
++}
++.select2-container .select2-choice abbr:hover {
++  background-position: right -11px;
++  cursor: pointer;
++}
++
++.select2-drop {
++    background: #fff;
++    color: #000;
++    border: 1px solid #aaa;
++    border-top: 0;
++    position: absolute;
++    top: 100%;
++    -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
++    -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
++    -o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
++    box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
++    z-index: 9999;
++    width:100%;
++    margin-top:-1px;
++
++  -webkit-border-radius: 0 0 4px 4px;
++  -moz-border-radius: 0 0 4px 4px;
++  border-radius: 0 0 4px 4px;
++}
++
++.select2-drop.select2-drop-above {
++    -webkit-border-radius: 4px 4px 0px 0px;
++    -moz-border-radius: 4px 4px 0px 0px;
++    border-radius: 4px 4px 0px 0px;
++    margin-top:1px;
++    border-top: 1px solid #aaa;
++    border-bottom: 0;
++
++    -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
++    -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
++    -o-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
++    box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
++}
++
++.select2-container .select2-choice div {
++    -webkit-border-radius: 0 4px 4px 0;
++    -moz-border-radius: 0 4px 4px 0;
++    border-radius: 0 4px 4px 0;
++    -moz-background-clip: padding;
++    -webkit-background-clip: padding-box;
++    background-clip: padding-box;
++    background: #ccc;
++    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
++    background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
++    background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
++    background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
++    background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%);
++    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0);
++    background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%);
++    border-left: 1px solid #aaa;
++    position: absolute;
++    right: 0;
++    top: 0;
++    display: block;
++    height: 100%;
++    width: 18px;
++}
++
++.select2-container .select2-choice div b {
++    background: url('../images/select2.png') no-repeat 0 1px;
++    display: block;
++    width: 100%;
++    height: 100%;
++}
++
++.select2-search {
++  display: inline-block;
++    white-space: nowrap;
++    z-index: 10000;
++  min-height: 26px;
++  width: 100%;
++  margin: 0;
++  padding-left: 4px;
++  padding-right: 4px;
++}
++
++.select2-search-hidden {
++  display: block;
++  position: absolute;
++  left: -10000px;
++}
++
++.select2-search input {
++    background: #fff url('../images/select2.png') no-repeat 100% -22px;
++    background: url('../images/select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
++    background: url('../images/select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
++    background: url('../images/select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
++    background: url('../images/select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
++    background: url('../images/select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
++    background: url('../images/select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
++    padding: 4px 20px 4px 5px;
++    outline: 0;
++    border: 1px solid #aaa;
++    font-family: sans-serif;
++    font-size: 1em;
++    width:100%;
++    margin:0;
++    height:auto !important;
++    min-height: 26px;
++    -webkit-box-shadow: none;
++    -moz-box-shadow: none;
++    box-shadow: none;
++    border-radius: 0;
++    -moz-border-radius: 0;
++    -webkit-border-radius: 0;
++}
++
++.select2-drop.select2-drop-above .select2-search input
++{
++    margin-top:4px;
++}
++
++.select2-search input.select2-active {
++    background: #fff url('../images/select2-spinner.gif') no-repeat 100%;
++    background: url('../images/select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
++    background: url('../images/select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
++    background: url('../images/select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
++    background: url('../images/select2-spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
++    background: url('../images/select2-spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
++    background: url('../images/select2-spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
++}
++
++
++.select2-container-active .select2-choice,
++.select2-container-active .select2-choices {
++    -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
++    -moz-box-shadow   : 0 0 5px rgba(0,0,0,.3);
++    -o-box-shadow     : 0 0 5px rgba(0,0,0,.3);
++    box-shadow        : 0 0 5px rgba(0,0,0,.3);
++    border: 1px solid #5897fb;
++    outline: none;
++}
++
++.select2-dropdown-open .select2-choice {
++  border: 1px solid #aaa;
++  border-bottom-color: transparent;
++  -webkit-box-shadow: 0 1px 0 #fff inset;
++  -moz-box-shadow   : 0 1px 0 #fff inset;
++  -o-box-shadow     : 0 1px 0 #fff inset;
++  box-shadow        : 0 1px 0 #fff inset;
++  background-color: #eee;
++  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
++  background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
++  background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
++  background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
++  background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
++  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
++  background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
++  -webkit-border-bottom-left-radius : 0;
++  -webkit-border-bottom-right-radius: 0;
++  -moz-border-radius-bottomleft : 0;
++  -moz-border-radius-bottomright: 0;
++  border-bottom-left-radius : 0;
++  border-bottom-right-radius: 0;
++}
++
++.select2-dropdown-open .select2-choice div {
++  background: transparent;
++  border-left: none;
++}
++.select2-dropdown-open .select2-choice div b {
++  background-position: -18px 1px;
++}
++
++/* results */
++.select2-results {
++  margin: 4px 4px 4px 0;
++  padding: 0 0 0 4px;
++  position: relative;
++  overflow-x: hidden;
++  overflow-y: auto;
++  max-height: 200px;
++}
++
++.select2-results ul.select2-result-sub {
++  margin: 0 0 0 0;
++}
++
++.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px }
++.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px }
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px }
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px }
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px }
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px }
++.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px }
++
++.select2-results li {
++  list-style: none;
++  display: list-item;
++}
++
++.select2-results li.select2-result-with-children > .select2-result-label {
++  font-weight: bold;
++}
++
++.select2-results .select2-result-label {
++  padding: 3px 7px 4px;
++  margin: 0;
++  cursor: pointer;
++}
++
++.select2-results .select2-highlighted {
++  background: #3875d7;
++  color: #fff;
++}
++.select2-results li em {
++  background: #feffde;
++  font-style: normal;
++}
++.select2-results .select2-highlighted em {
++  background: transparent;
++}
++.select2-results .select2-no-results,
++.select2-results .select2-searching,
++.select2-results .select2-selection-limit {
++  background: #f4f4f4;
++  display: list-item;
++}
++
++/*
++disabled look for already selected choices in the results dropdown
++.select2-results .select2-disabled.select2-highlighted {
++    color: #666;
++    background: #f4f4f4;
++    display: list-item;
++    cursor: default;
++}
++.select2-results .select2-disabled {
++  background: #f4f4f4;
++  display: list-item;
++  cursor: default;
++}
++*/
++.select2-results .select2-disabled {
++    display: none;
++}
++
++.select2-more-results.select2-active {
++    background: #f4f4f4 url('spinner.gif') no-repeat 100%;
++}
++
++.select2-more-results {
++  background: #f4f4f4;
++  display: list-item;
++}
++
++/* disabled styles */
++
++.select2-container.select2-container-disabled .select2-choice {
++    background-color: #f4f4f4;
++    background-image: none;
++    border: 1px solid #ddd;
++    cursor: default;
++}
++
++.select2-container.select2-container-disabled .select2-choice div {
++    background-color: #f4f4f4;
++    background-image: none;
++    border-left: 0;
++}
++
++
++/* multiselect */
++
++.select2-container-multi .select2-choices {
++    background-color: #fff;
++      background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
++      background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
++      background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
++      background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
++      background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
++      background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
++      border: 1px solid #aaa;
++      margin: 0;
++      padding: 0;
++      cursor: text;
++      overflow: hidden;
++      height: auto !important;
++      height: 1%;
++      position: relative;
++}
++
++.select2-container-multi .select2-choices {
++    min-height: 26px;
++}
++
++.select2-container-multi.select2-container-active .select2-choices {
++    -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
++    -moz-box-shadow   : 0 0 5px rgba(0,0,0,.3);
++    -o-box-shadow     : 0 0 5px rgba(0,0,0,.3);
++    box-shadow        : 0 0 5px rgba(0,0,0,.3);
++    border: 1px solid #5897fb;
++    outline: none;
++}
++.select2-container-multi .select2-choices li {
++  float: left;
++  list-style: none;
++}
++.select2-container-multi .select2-choices .select2-search-field {
++  white-space: nowrap;
++  margin: 0;
++  padding: 0;
++}
++
++.select2-container-multi .select2-choices .select2-search-field input {
++  color: #666;
++  background: transparent !important;
++  font-family: sans-serif;
++  font-size: 100%;
++  height: 15px;
++  padding: 5px;
++  margin: 1px 0;
++  outline: 0;
++  border: 0;
++  -webkit-box-shadow: none;
++  -moz-box-shadow   : none;
++  -o-box-shadow     : none;
++  box-shadow        : none;
++}
++
++.select2-container-multi .select2-choices .select2-search-field input.select2-active {
++    background: #fff url('spinner.gif') no-repeat 100% !important;
++}
++
++.select2-default {
++  color: #999 !important;
++}
++
++.select2-container-multi .select2-choices .select2-search-choice {
++  -webkit-border-radius: 3px;
++  -moz-border-radius   : 3px;
++  border-radius        : 3px;
++  -moz-background-clip   : padding;
++  -webkit-background-clip: padding-box;
++  background-clip        : padding-box;
++  background-color: #e4e4e4;
++  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
++  background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
++  background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
++  background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
++  background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
++  background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
++  background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
++  -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
++  -moz-box-shadow   : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
++  box-shadow        : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
++  color: #333;
++  border: 1px solid #aaaaaa;
++  line-height: 13px;
++  padding: 3px 5px 3px 18px;
++  margin: 3px 0 3px 5px;
++  position: relative;
++  cursor: default;
++}
++.select2-container-multi .select2-choices .select2-search-choice span {
++  cursor: default;
++}
++.select2-container-multi .select2-choices .select2-search-choice-focus {
++  background: #d4d4d4;
++}
++
++.select2-search-choice-close {
++  display: block;
++  position: absolute;
++  right: 3px;
++  top: 4px;
++  width: 12px;
++  height: 13px;
++  font-size: 1px;
++  background: url('../images/select2.png') right top no-repeat;
++  outline: none;
++}
++
++.select2-container-multi .select2-search-choice-close {
++  left: 3px;
++}
++
++
++.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
++  background-position: right -11px;
++}
++.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
++  background-position: right -11px;
++}
++
++/* disabled styles */
++
++.select2-container-multi.select2-container-disabled .select2-choices{
++    background-color: #f4f4f4;
++    background-image: none;
++    border: 1px solid #ddd;
++    cursor: default;
++}
++
++.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
++    background-image: none;
++    background-color: #f4f4f4;
++    border: 1px solid #ddd;
++    padding: 3px 5px 3px 5px;
++}
++
++.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close {
++    display: none;
++}
++/* end multiselect */
++
++.select2-result-selectable .select2-match,
++.select2-result-unselectable .select2-result-selectable .select2-match { text-decoration: underline; }
++.select2-result-unselectable .select2-match { text-decoration: none; }
++
++.select2-offscreen { position: absolute; left: -10000px; }
++
++/* Retina-ize icons */
++
++@media only screen and (-webkit-min-device-pixel-ratio: 1.5) {
++	.select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b {
++		background-image: url(select2x2.png) !important;
++		background-repeat: no-repeat !important;
++		background-size: 60px 40px !important;
++	}
++	.select2-search input {
++		background-position: 100% -21px !important;
++	}
++}
+\ No newline at end of file
+diff --git a/static/scripts/galaxy.base.js b/static/scripts/galaxy.base.js
+--- a/static/scripts/galaxy.base.js
++++ b/static/scripts/galaxy.base.js
+@@ -241,8 +241,9 @@
+ 
+ // Replace select box with a text input box + autocomplete.
+ function replace_big_select_inputs(min_length, max_length, select_elts) {
+-    // To do replace, jQuery's autocomplete plugin must be loaded.
+-    if (!jQuery().autocomplete) {
++    // To do replace, the select2 plugin must be loaded.
++
++    if (!jQuery.fn.select2) {
+         return;
+     }
+     
+@@ -255,7 +256,7 @@
+     }
+ 
+     var select_elts = select_elts || $('select');
+-    
++
+     select_elts.each( function() {
+         var select_elt = $(this);
+         // Make sure that options is within range.
+@@ -263,131 +264,21 @@
+         if ( (num_options < min_length) || (num_options > max_length) ) {
+             return;
+         }
+-            
+-        // Skip multi-select because widget cannot handle multi-select.
+-        if (select_elt.attr('multiple') === 'multiple') {
+-            return;
+-        }
+-            
++        
+         if (select_elt.hasClass("no-autocomplete")) {
+             return;
+         }
++
++        /* Replaced jQuery.autocomplete with select2, notes:
++         * - multiple selects are supported 
++         * - the original element is updated with the value, convert_to_values should not be needed
++         * - events are fired when updating the original element, so refresh_on_change should just work
++         *
++         * - should we still sort dbkey fields here? 
++         */
+         
+-        // Replace select with text + autocomplete.
+-        var start_value = select_elt.attr('value');
+-        
+-        // Set up text input + autocomplete element.
+-        var text_input_elt = $("<input type='text' class='text-and-autocomplete-select'></input>");
+-        text_input_elt.attr('size', 40);
+-        text_input_elt.attr('name', select_elt.attr('name'));
+-        text_input_elt.attr('id', select_elt.attr('id'));
+-        text_input_elt.click( function() {
+-            // Show all. Also provide note that load is happening since this can be slow.
+-            var cur_value = $(this).val();
+-            $(this).val('Loading...');
+-            $(this).showAllInCache();
+-            $(this).val(cur_value);
+-            $(this).select();
+-        });
++        select_elt.select2( { width: "resolve" } );
+ 
+-        // Get options for select for autocomplete.
+-        var select_options = [];
+-        var select_mapping = {};
+-        select_elt.children('option').each( function() {
+-            // Get text, value for option.
+-            var text = $(this).text();
+-            var value = $(this).attr('value');
+-
+-            // Set options and mapping. Mapping is (i) [from text to value] AND (ii) [from value to value]. This
+-            // enables a user to type the value directly rather than select the text that represents the value. 
+-            select_options.push( text );
+-            select_mapping[ text ] = value;
+-            select_mapping[ value ] = value;
+-
+-            // If this is the start value, set value of input element.
+-            if ( value == start_value ) {
+-                text_input_elt.attr('value', text);
+-            }
+-        });
+-        
+-        // Set initial text if it's empty.
+-        if ( start_value === '' || start_value === '?') {
+-            text_input_elt.attr('value', 'Click to Search or Select');
+-        }
+-        
+-        // Sort dbkey options list only.
+-        if (select_elt.attr('name') == 'dbkey') {
+-            select_options = select_options.sort(naturalSort);
+-        }
+-        
+-        // Do autocomplete.
+-        var autocomplete_options = { selectFirst: false, autoFill: false, mustMatch: false, matchContains: true, max: max_length, minChars : 0, hideForLessThanMinChars : false };
+-        text_input_elt.autocomplete(select_options, autocomplete_options);
+-
+-        // Replace select with text input.
+-        select_elt.replaceWith(text_input_elt);
+-        
+-        // Set trigger to replace text with value when element's form is submitted. If text doesn't correspond to value, default to start value.
+-        var submit_hook = function() {
+-            // Try to convert text to value.
+-            var cur_value = text_input_elt.attr('value');
+-            var new_value = select_mapping[cur_value];
+-            if (new_value !== null && new_value !== undefined) {
+-                text_input_elt.attr('value', new_value);
+-            } else {
+-                // If there is a non-empty start value, use that; otherwise unknown.
+-                if (start_value !== "") {
+-                    text_input_elt.attr('value', start_value);
+-                } else {
+-                    // This is needed to make the DB key work.
+-                    text_input_elt.attr('value', '?');
+-                }
+-            }
+-        };
+-        text_input_elt.parents('form').submit( function() { submit_hook(); } );
+-        
+-        // Add custom event so that other objects can execute name --> value conversion whenever they want.
+-        $(document).bind("convert_to_values", function() { submit_hook(); } );
+-        
+-        // If select is refresh on change, mirror this behavior.
+-        if (select_elt.attr('refresh_on_change') == 'true') {
+-            // Get refresh vals.
+-            var ref_on_change_vals = select_elt.attr('refresh_on_change_values'),
+-                last_selected_value = select_elt.attr("last_selected_value");
+-            if (ref_on_change_vals !== undefined) {
+-                ref_on_change_vals = ref_on_change_vals.split(',');
+-            }
+-            // Function that attempts to refresh based on the value in the text element.
+-            var try_refresh_fn = function() {
+-                // Get new value and see if it can be matched.
+-                var new_value = select_mapping[text_input_elt.attr('value')];
+-                if (last_selected_value !== new_value && new_value !== null && new_value !== undefined) {
+-                    if (ref_on_change_vals !== undefined && $.inArray(new_value, ref_on_change_vals) === -1 && $.inArray(last_selected_value, ref_on_change_vals) === -1) {
+-                        return;
+-                    }
+-                    text_input_elt.attr('value', new_value);
+-                    $(window).trigger("refresh_on_change");
+-                    text_input_elt.parents('form').submit();
+-                }
+-            };
+-            
+-            // Attempt refresh if (a) result event fired by autocomplete (indicating autocomplete occurred) or (b) on keyup (in which
+-            // case a user may have manually entered a value that needs to be refreshed).
+-            text_input_elt.bind("result", try_refresh_fn);
+-            text_input_elt.keyup( function(e) {
+-                if (e.keyCode === 13) { // Return key
+-                    try_refresh_fn();
+-                }
+-            });
+-            
+-            // Disable return key so that it does not submit the form automatically. This is done because element should behave like a 
+-            // select (enter = select), not text input (enter = submit form).
+-            text_input_elt.keydown( function(e) {
+-                if (e.keyCode === 13) { // Return key
+-                    return false;
+-                }
+-            });
+-        }
+     });
+ }
+ 
+@@ -729,6 +620,7 @@
+ };
+ 
+ $(document).ready( function() {
++
+     $("select[refresh_on_change='true']").change( function() {
+         var select_field = $(this),
+             select_val = select_field.val(),
+diff --git a/static/scripts/libs/jquery/select2.js b/static/scripts/libs/jquery/select2.js
+new file mode 100755
+--- /dev/null
++++ b/static/scripts/libs/jquery/select2.js
+@@ -0,0 +1,2409 @@
++/*
++ Copyright 2012 Igor Vaynberg
++
++ Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012
++
++ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
++ compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
++
++ http://www.apache.org/licenses/LICENSE-2.0
++
++ Unless required by applicable law or agreed to in writing, software distributed under the License is
++ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ See the License for the specific language governing permissions and limitations under the License.
++ */
++ (function ($) {
++ 	if(typeof $.fn.each2 == "undefined"){
++ 		$.fn.extend({
++ 			/*
++			* 4-10 times faster .each replacement
++			* use it carefully, as it overrides jQuery context of element on each iteration
++			*/
++			each2 : function (c) {
++				var j = $([0]), i = -1, l = this.length;
++				while (
++					++i < l
++					&& (j.context = j[0] = this[i])
++					&& c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
++				);
++				return this;
++			}
++ 		});
++ 	}
++})(jQuery);
++
++(function ($, undefined) {
++    "use strict";
++    /*global document, window, jQuery, console */
++
++    if (window.Select2 !== undefined) {
++        return;
++    }
++
++    var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer;
++
++    KEY = {
++        TAB: 9,
++        ENTER: 13,
++        ESC: 27,
++        SPACE: 32,
++        LEFT: 37,
++        UP: 38,
++        RIGHT: 39,
++        DOWN: 40,
++        SHIFT: 16,
++        CTRL: 17,
++        ALT: 18,
++        PAGE_UP: 33,
++        PAGE_DOWN: 34,
++        HOME: 36,
++        END: 35,
++        BACKSPACE: 8,
++        DELETE: 46,
++        isArrow: function (k) {
++            k = k.which ? k.which : k;
++            switch (k) {
++            case KEY.LEFT:
++            case KEY.RIGHT:
++            case KEY.UP:
++            case KEY.DOWN:
++                return true;
++            }
++            return false;
++        },
++        isControl: function (e) {
++            var k = e.which;
++            switch (k) {
++            case KEY.SHIFT:
++            case KEY.CTRL:
++            case KEY.ALT:
++                return true;
++            }
++
++            if (e.metaKey) return true;
++
++            return false;
++        },
++        isFunctionKey: function (k) {
++            k = k.which ? k.which : k;
++            return k >= 112 && k <= 123;
++        }
++    };
++
++    nextUid=(function() { var counter=1; return function() { return counter++; }; }());
++
++    function indexOf(value, array) {
++        var i = 0, l = array.length, v;
++
++        if (typeof value === "undefined") {
++          return -1;
++        }
++
++        if (value.constructor === String) {
++            for (; i < l; i = i + 1) if (value.localeCompare(array[i]) === 0) return i;
++        } else {
++            for (; i < l; i = i + 1) {
++                v = array[i];
++                if (v.constructor === String) {
++                    if (v.localeCompare(value) === 0) return i;
++                } else {
++                    if (v === value) return i;
++                }
++            }
++        }
++        return -1;
++    }
++
++    /**
++     * Compares equality of a and b taking into account that a and b may be strings, in which case localeCompare is used
++     * @param a
++     * @param b
++     */
++    function equal(a, b) {
++        if (a === b) return true;
++        if (a === undefined || b === undefined) return false;
++        if (a === null || b === null) return false;
++        if (a.constructor === String) return a.localeCompare(b) === 0;
++        if (b.constructor === String) return b.localeCompare(a) === 0;
++        return false;
++    }
++
++    /**
++     * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty
++     * strings
++     * @param string
++     * @param separator
++     */
++    function splitVal(string, separator) {
++        var val, i, l;
++        if (string === null || string.length < 1) return [];
++        val = string.split(separator);
++        for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]);
++        return val;
++    }
++
++    function getSideBorderPadding(element) {
++        return element.outerWidth() - element.width();
++    }
++
++    function installKeyUpChangeEvent(element) {
++        var key="keyup-change-value";
++        element.bind("keydown", function () {
++            if ($.data(element, key) === undefined) {
++                $.data(element, key, element.val());
++            }
++        });
++        element.bind("keyup", function () {
++            var val= $.data(element, key);
++            if (val !== undefined && element.val() !== val) {
++                $.removeData(element, key);
++                element.trigger("keyup-change");
++            }
++        });
++    }
++
++    $(document).delegate("body", "mousemove", function (e) {
++        $.data(document, "select2-lastpos", {x: e.pageX, y: e.pageY});
++    });
++
++    /**
++     * filters mouse events so an event is fired only if the mouse moved.
++     *
++     * filters out mouse events that occur when mouse is stationary but
++     * the elements under the pointer are scrolled.
++     */
++    function installFilteredMouseMove(element) {
++	    element.bind("mousemove", function (e) {
++            var lastpos = $.data(document, "select2-lastpos");
++            if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
++                $(e.target).trigger("mousemove-filtered", e);
++            }
++        });
++    }
++
++    /**
++     * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
++     * within the last quietMillis milliseconds.
++     *
++     * @param quietMillis number of milliseconds to wait before invoking fn
++     * @param fn function to be debounced
++     * @param ctx object to be used as this reference within fn
++     * @return debounced version of fn
++     */
++    function debounce(quietMillis, fn, ctx) {
++        ctx = ctx || undefined;
++        var timeout;
++        return function () {
++            var args = arguments;
++            window.clearTimeout(timeout);
++            timeout = window.setTimeout(function() {
++                fn.apply(ctx, args);
++            }, quietMillis);
++        };
++    }
++
++    /**
++     * A simple implementation of a thunk
++     * @param formula function used to lazily initialize the thunk
++     * @return {Function}
++     */
++    function thunk(formula) {
++        var evaluated = false,
++            value;
++        return function() {
++            if (evaluated === false) { value = formula(); evaluated = true; }
++            return value;
++        };
++    };
++
++    function installDebouncedScroll(threshold, element) {
++        var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
++        element.bind("scroll", function (e) {
++            if (indexOf(e.target, element.get()) >= 0) notify(e);
++        });
++    }
++
++    function killEvent(event) {
++        event.preventDefault();
++        event.stopPropagation();
++    }
++
++    function measureTextWidth(e) {
++        if (!sizer){
++        	var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
++        	sizer = $("<div></div>").css({
++	            position: "absolute",
++	            left: "-10000px",
++	            top: "-10000px",
++	            display: "none",
++	            fontSize: style.fontSize,
++	            fontFamily: style.fontFamily,
++	            fontStyle: style.fontStyle,
++	            fontWeight: style.fontWeight,
++	            letterSpacing: style.letterSpacing,
++	            textTransform: style.textTransform,
++	            whiteSpace: "nowrap"
++	        });
++        	$("body").append(sizer);
++        }
++        sizer.text(e.val());
++        return sizer.width();
++    }
++
++    function markMatch(text, term, markup) {
++        var match=text.toUpperCase().indexOf(term.toUpperCase()),
++            tl=term.length;
++
++        if (match<0) {
++            markup.push(text);
++            return;
++        }
++
++        markup.push(text.substring(0, match));
++        markup.push("<span class='select2-match'>");
++        markup.push(text.substring(match, match + tl));
++        markup.push("</span>");
++        markup.push(text.substring(match + tl, text.length));
++    }
++
++    /**
++     * Produces an ajax-based query function
++     *
++     * @param options object containing configuration paramters
++     * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
++     * @param options.url url for the data
++     * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
++     * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified
++     * @param options.traditional a boolean flag that should be true if you wish to use the traditional style of param serialization for the ajax request
++     * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
++     * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2.
++     *      The expected format is an object containing the following keys:
++     *      results array of objects that will be used as choices
++     *      more (optional) boolean indicating whether there are more results available
++     *      Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
++     */
++    function ajax(options) {
++        var timeout, // current scheduled but not yet executed request
++            requestSequence = 0, // sequence used to drop out-of-order responses
++            handler = null,
++            quietMillis = options.quietMillis || 100;
++
++        return function (query) {
++            window.clearTimeout(timeout);
++            timeout = window.setTimeout(function () {
++                requestSequence += 1; // increment the sequence
++                var requestNumber = requestSequence, // this request's sequence number
++                    data = options.data, // ajax data function
++                    transport = options.transport || $.ajax,
++                    traditional = options.traditional || false,
++                    type = options.type || 'GET'; // set type of request (GET or POST)
++
++                data = data.call(this, query.term, query.page, query.context);
++
++                if( null !== handler) { handler.abort(); }
++
++                handler = transport.call(null, {
++                    url: options.url,
++                    dataType: options.dataType,
++                    data: data,
++                    type: type,
++                    traditional: traditional,
++                    success: function (data) {
++                        if (requestNumber < requestSequence) {
++                            return;
++                        }
++                        // TODO 3.0 - replace query.page with query so users have access to term, page, etc.
++                        var results = options.results(data, query.page);
++                        query.callback(results);
++                    }
++                });
++            }, quietMillis);
++        };
++    }
++
++    /**
++     * Produces a query function that works with a local array
++     *
++     * @param options object containing configuration parameters. The options parameter can either be an array or an
++     * object.
++     *
++     * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
++     *
++     * If the object form is used ti is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
++     * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
++     * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
++     * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
++     * the text.
++     */
++    function local(options) {
++        var data = options, // data elements
++            dataText,
++            text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
++
++        if (!$.isArray(data)) {
++            text = data.text;
++            // if text is not a function we assume it to be a key name
++            if (!$.isFunction(text)) {
++              dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
++              text = function (item) { return item[dataText]; };
++            }
++            data = data.results;
++        }
++
++        return function (query) {
++            var t = query.term, filtered = { results: [] }, process;
++            if (t === "") {
++                query.callback({results: data});
++                return;
++            }
++
++            process = function(datum, collection) {
++                var group, attr;
++                datum = datum[0];
++                if (datum.children) {
++                    group = {};
++                    for (attr in datum) {
++                        if (datum.hasOwnProperty(attr)) group[attr]=datum[attr];
++                    }
++                    group.children=[];
++                    $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
++                    if (group.children.length) {
++                        collection.push(group);
++                    }
++                } else {
++                    if (query.matcher(t, text(datum))) {
++                        collection.push(datum);
++                    }
++                }
++            };
++
++            $(data).each2(function(i, datum) { process(datum, filtered.results); });
++            query.callback(filtered);
++        };
++    }
++
++    // TODO javadoc
++    function tags(data) {
++        // TODO even for a function we should probably return a wrapper that does the same object/string check as
++        // the function for arrays. otherwise only functions that return objects are supported.
++        if ($.isFunction(data)) {
++            return data;
++        }
++
++        // if not a function we assume it to be an array
++
++        return function (query) {
++            var t = query.term, filtered = {results: []};
++            $(data).each(function () {
++                var isObject = this.text !== undefined,
++                    text = isObject ? this.text : this;
++                if (t === "" || query.matcher(t, text)) {
++                    filtered.results.push(isObject ? this : {id: this, text: this});
++                }
++            });
++            query.callback(filtered);
++        };
++    }
++
++    /**
++     * Checks if the formatter function should be used.
++     *
++     * Throws an error if it is not a function. Returns true if it should be used,
++     * false if no formatting should be performed.
++     *
++     * @param formatter
++     */
++    function checkFormatter(formatter, formatterName) {
++        if ($.isFunction(formatter)) return true;
++        if (!formatter) return false;
++        throw new Error("formatterName must be a function or a falsy value");
++    }
++
++    function evaluate(val) {
++        return $.isFunction(val) ? val() : val;
++    }
++
++    function countResults(results) {
++        var count = 0;
++        $.each(results, function(i, item) {
++            if (item.children) {
++                count += countResults(item.children);
++            } else {
++                count++;
++            }
++        });
++        return count;
++    }
++
++    /**
++     * Default tokenizer. This function uses breaks the input on substring match of any string from the
++     * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
++     * two options have to be defined in order for the tokenizer to work.
++     *
++     * @param input text user has typed so far or pasted into the search field
++     * @param selection currently selected choices
++     * @param selectCallback function(choice) callback tho add the choice to selection
++     * @param opts select2's opts
++     * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
++     */
++    function defaultTokenizer(input, selection, selectCallback, opts) {
++        var original = input, // store the original so we can compare and know if we need to tell the search to update its text
++            dupe = false, // check for whether a token we extracted represents a duplicate selected choice
++            token, // token
++            index, // position at which the separator was found
++            i, l, // looping variables
++            separator; // the matched separator
++
++        if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
++
++        while (true) {
++            index = -1;
++
++            for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
++                separator = opts.tokenSeparators[i];
++                index = input.indexOf(separator);
++                if (index >= 0) break;
++            }
++
++            if (index < 0) break; // did not find any token separator in the input string, bail
++
++            token = input.substring(0, index);
++            input = input.substring(index + separator.length);
++
++            if (token.length > 0) {
++                token = opts.createSearchChoice(token, selection);
++                if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
++                    dupe = false;
++                    for (i = 0, l = selection.length; i < l; i++) {
++                        if (equal(opts.id(token), opts.id(selection[i]))) {
++                            dupe = true; break;
++                        }
++                    }
++
++                    if (!dupe) selectCallback(token);
++                }
++            }
++        }
++
++        if (original.localeCompare(input) != 0) return input;
++    }
++
++    /**
++     * blurs any Select2 container that has focus when an element outside them was clicked or received focus
++     *
++     * also takes care of clicks on label tags that point to the source element
++     */
++    $(document).ready(function () {
++        $(document).delegate("body", "mousedown touchend", function (e) {
++            var target = $(e.target).closest("div.select2-container").get(0), attr;
++            if (target) {
++                $(document).find("div.select2-container-active").each(function () {
++                    if (this !== target) $(this).data("select2").blur();
++                });
++            } else {
++                target = $(e.target).closest("div.select2-drop").get(0);
++                $(document).find("div.select2-drop-active").each(function () {
++                    if (this !== target) $(this).data("select2").blur();
++                });
++            }
++
++            target=$(e.target);
++            attr = target.attr("for");
++            if ("LABEL" === e.target.tagName && attr && attr.length > 0) {
++                target = $("#"+attr);
++                target = target.data("select2");
++                if (target !== undefined) { target.focus(); e.preventDefault();}
++            }
++        });
++    });
++
++    /**
++     * Creates a new class
++     *
++     * @param superClass
++     * @param methods
++     */
++    function clazz(SuperClass, methods) {
++        var constructor = function () {};
++        constructor.prototype = new SuperClass;
++        constructor.prototype.constructor = constructor;
++        constructor.prototype.parent = SuperClass.prototype;
++        constructor.prototype = $.extend(constructor.prototype, methods);
++        return constructor;
++    }
++
++    AbstractSelect2 = clazz(Object, {
++
++        // abstract
++        bind: function (func) {
++            var self = this;
++            return function () {
++                func.apply(self, arguments);
++            };
++        },
++
++        // abstract
++        init: function (opts) {
++            var results, search, resultsSelector = ".select2-results";
++
++            // prepare options
++            this.opts = opts = this.prepareOpts(opts);
++
++            this.id=opts.id;
++
++            // destroy if called on an existing component
++            if (opts.element.data("select2") !== undefined &&
++                opts.element.data("select2") !== null) {
++                this.destroy();
++            }
++
++            this.enabled=true;
++            this.container = this.createContainer();
++
++            this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
++            this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
++            this.container.attr("id", this.containerId);
++
++            // cache the body so future lookups are cheap
++            this.body = thunk(function() { return opts.element.closest("body"); });
++
++            if (opts.element.attr("class") !== undefined) {
++                this.container.addClass(opts.element.attr("class").replace(/validate\[[\S ]+] ?/, ''));
++            }
++
++            this.container.css(evaluate(opts.containerCss));
++            this.container.addClass(evaluate(opts.containerCssClass));
++
++            // swap container for the element
++            this.opts.element
++                .data("select2", this)
++                .hide()
++                .before(this.container);
++            this.container.data("select2", this);
++
++            this.dropdown = this.container.find(".select2-drop");
++            this.dropdown.addClass(evaluate(opts.dropdownCssClass));
++            this.dropdown.data("select2", this);
++
++            this.results = results = this.container.find(resultsSelector);
++            this.search = search = this.container.find("input.select2-input");
++
++            search.attr("tabIndex", this.opts.element.attr("tabIndex"));
++
++            this.resultsPage = 0;
++            this.context = null;
++
++            // initialize the container
++            this.initContainer();
++            this.initContainerWidth();
++
++            installFilteredMouseMove(this.results);
++            this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
++
++            installDebouncedScroll(80, this.results);
++            this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
++
++            // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
++            if ($.fn.mousewheel) {
++                results.mousewheel(function (e, delta, deltaX, deltaY) {
++                    var top = results.scrollTop(), height;
++                    if (deltaY > 0 && top - deltaY <= 0) {
++                        results.scrollTop(0);
++                        killEvent(e);
++                    } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
++                        results.scrollTop(results.get(0).scrollHeight - results.height());
++                        killEvent(e);
++                    }
++                });
++            }
++
++            installKeyUpChangeEvent(search);
++            search.bind("keyup-change", this.bind(this.updateResults));
++            search.bind("focus", function () { search.addClass("select2-focused"); if (search.val() === " ") search.val(""); });
++            search.bind("blur", function () { search.removeClass("select2-focused");});
++
++            this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) {
++                if ($(e.target).closest(".select2-result-selectable:not(.select2-disabled)").length > 0) {
++                    this.highlightUnderEvent(e);
++                    this.selectHighlighted(e);
++                } else {
++                    this.focusSearch();
++                }
++                killEvent(e);
++            }));
++
++            // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
++            // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
++            // dom it will trigger the popup close, which is not what we want
++            this.dropdown.bind("click mouseup mousedown", function (e) { e.stopPropagation(); });
++
++            if ($.isFunction(this.opts.initSelection)) {
++                // initialize selection based on the current value of the source element
++                this.initSelection();
++
++                // if the user has provided a function that can set selection based on the value of the source element
++                // we monitor the change event on the element and trigger it, allowing for two way synchronization
++                this.monitorSource();
++            }
++
++            if (opts.element.is(":disabled") || opts.element.is("[readonly='readonly']")) this.disable();
++        },
++
++        // abstract
++        destroy: function () {
++            var select2 = this.opts.element.data("select2");
++            if (select2 !== undefined) {
++                select2.container.remove();
++                select2.dropdown.remove();
++                select2.opts.element
++                    .removeData("select2")
++                    .unbind(".select2")
++                    .show();
++            }
++        },
++
++        // abstract
++        prepareOpts: function (opts) {
++            var element, select, idKey, ajaxUrl;
++
++            element = opts.element;
++
++            if (element.get(0).tagName.toLowerCase() === "select") {
++                this.select = select = opts.element;
++            }
++
++            if (select) {
++                // these options are not allowed when attached to a select because they are picked up off the element itself
++                $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
++                    if (this in opts) {
++                        throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element.");
++                    }
++                });
++            }
++
++            opts = $.extend({}, {
++                populateResults: function(container, results, query) {
++                    var populate,  data, result, children, id=this.opts.id, self=this;
++
++                    populate=function(results, container, depth) {
++
++                        var i, l, result, selectable, compound, node, label, innerContainer, formatted;
++                        for (i = 0, l = results.length; i < l; i = i + 1) {
++
++                            result=results[i];
++                            selectable=id(result) !== undefined;
++                            compound=result.children && result.children.length > 0;
++
++                            node=$("<li></li>");
++                            node.addClass("select2-results-dept-"+depth);
++                            node.addClass("select2-result");
++                            node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
++                            if (compound) { node.addClass("select2-result-with-children"); }
++                            node.addClass(self.opts.formatResultCssClass(result));
++
++                            label=$("<div></div>");
++                            label.addClass("select2-result-label");
++
++                            formatted=opts.formatResult(result, label, query);
++                            if (formatted!==undefined) {
++                                label.html(self.opts.escapeMarkup(formatted));
++                            }
++
++                            node.append(label);
++
++                            if (compound) {
++
++                                innerContainer=$("<ul></ul>");
++                                innerContainer.addClass("select2-result-sub");
++                                populate(result.children, innerContainer, depth+1);
++                                node.append(innerContainer);
++                            }
++
++                            node.data("select2-data", result);
++                            container.append(node);
++                        }
++                    };
++
++                    populate(results, container, 0);
++                }
++            }, $.fn.select2.defaults, opts);
++
++            if (typeof(opts.id) !== "function") {
++                idKey = opts.id;
++                opts.id = function (e) { return e[idKey]; };
++            }
++
++            if (select) {
++                opts.query = this.bind(function (query) {
++                    var data = { results: [], more: false },
++                        term = query.term,
++                        children, firstChild, process;
++
++                    process=function(element, collection) {
++                        var group;
++                        if (element.is("option")) {
++                            if (query.matcher(term, element.text(), element)) {
++                                collection.push({id:element.attr("value"), text:element.text(), element: element.get(), css: element.attr("class")});
++                            }
++                        } else if (element.is("optgroup")) {
++                            group={text:element.attr("label"), children:[], element: element.get(), css: element.attr("class")};
++                            element.children().each2(function(i, elm) { process(elm, group.children); });
++                            if (group.children.length>0) {
++                                collection.push(group);
++                            }
++                        }
++                    };
++
++                    children=element.children();
++
++                    // ignore the placeholder option if there is one
++                    if (this.getPlaceholder() !== undefined && children.length > 0) {
++                        firstChild = children[0];
++                        if ($(firstChild).text() === "") {
++                            children=children.not(firstChild);
++                        }
++                    }
++
++                    children.each2(function(i, elm) { process(elm, data.results); });
++
++                    query.callback(data);
++                });
++                // this is needed because inside val() we construct choices from options and there id is hardcoded
++                opts.id=function(e) { return e.id; };
++                opts.formatResultCssClass = function(data) { return data.css; }
++            } else {
++                if (!("query" in opts)) {
++                    if ("ajax" in opts) {
++                        ajaxUrl = opts.element.data("ajax-url");
++                        if (ajaxUrl && ajaxUrl.length > 0) {
++                            opts.ajax.url = ajaxUrl;
++                        }
++                        opts.query = ajax(opts.ajax);
++                    } else if ("data" in opts) {
++                        opts.query = local(opts.data);
++                    } else if ("tags" in opts) {
++                        opts.query = tags(opts.tags);
++                        opts.createSearchChoice = function (term) { return {id: term, text: term}; };
++                        opts.initSelection = function (element, callback) {
++                            var data = [];
++                            $(splitVal(element.val(), opts.separator)).each(function () {
++                                var id = this, text = this, tags=opts.tags;
++                                if ($.isFunction(tags)) tags=tags();
++                                $(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } });
++                                data.push({id: id, text: text});
++                            });
++
++                            callback(data);
++                        };
++                    }
++                }
++            }
++            if (typeof(opts.query) !== "function") {
++                throw "query function not defined for Select2 " + opts.element.attr("id");
++            }
++
++            return opts;
++        },
++
++        /**
++         * Monitor the original element for changes and update select2 accordingly
++         */
++        // abstract
++        monitorSource: function () {
++            this.opts.element.bind("change.select2", this.bind(function (e) {
++                if (this.opts.element.data("select2-change-triggered") !== true) {
++                    this.initSelection();
++                }
++            }));
++        },
++
++        /**
++         * Triggers the change event on the source element
++         */
++        // abstract
++        triggerChange: function (details) {
++
++            details = details || {};
++            details= $.extend({}, details, { type: "change", val: this.val() });
++            // prevents recursive triggering
++            this.opts.element.data("select2-change-triggered", true);
++            this.opts.element.trigger(details);
++            this.opts.element.data("select2-change-triggered", false);
++
++            // some validation frameworks ignore the change event and listen instead to keyup, click for selects
++            // so here we trigger the click event manually
++            this.opts.element.click();
++
++            // ValidationEngine ignorea the change event and listens instead to blur
++            // so here we trigger the blur event manually if so desired
++            if (this.opts.blurOnChange)
++                this.opts.element.blur();
++        },
++
++
++        // abstract
++        enable: function() {
++            if (this.enabled) return;
++
++            this.enabled=true;
++            this.container.removeClass("select2-container-disabled");
++        },
++
++        // abstract
++        disable: function() {
++            if (!this.enabled) return;
++
++            this.close();
++
++            this.enabled=false;
++            this.container.addClass("select2-container-disabled");
++        },
++
++        // abstract
++        opened: function () {
++            return this.container.hasClass("select2-dropdown-open");
++        },
++
++        // abstract
++        positionDropdown: function() {
++            var offset = this.container.offset(),
++                height = this.container.outerHeight(),
++                width = this.container.outerWidth(),
++                dropHeight = this.dropdown.outerHeight(),
++                viewportBottom = $(window).scrollTop() + document.documentElement.clientHeight,
++                dropTop = offset.top + height,
++                dropLeft = offset.left,
++                enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
++                enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(),
++                aboveNow = this.dropdown.hasClass("select2-drop-above"),
++                bodyOffset,
++                above,
++                css;
++
++            // console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
++            // console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove);
++
++            // fix positioning when body has an offset and is not position: static
++
++            if (this.body().css('position') !== 'static') {
++                bodyOffset = this.body().offset();
++                dropTop -= bodyOffset.top;
++                dropLeft -= bodyOffset.left;
++            }
++
++            // always prefer the current above/below alignment, unless there is not enough room
++
++            if (aboveNow) {
++                above = true;
++                if (!enoughRoomAbove && enoughRoomBelow) above = false;
++            } else {
++                above = false;
++                if (!enoughRoomBelow && enoughRoomAbove) above = true;
++            }
++
++            if (above) {
++                dropTop = offset.top - dropHeight;
++                this.container.addClass("select2-drop-above");
++                this.dropdown.addClass("select2-drop-above");
++            }
++            else {
++                this.container.removeClass("select2-drop-above");
++                this.dropdown.removeClass("select2-drop-above");
++            }
++
++            css = $.extend({
++                top: dropTop,
++                left: dropLeft,
++                width: width
++            }, evaluate(this.opts.dropdownCss));
++
++            this.dropdown.css(css);
++        },
++
++        // abstract
++        shouldOpen: function() {
++            var event;
++
++            if (this.opened()) return false;
++
++            event = $.Event("open");
++            this.opts.element.trigger(event);
++            return !event.isDefaultPrevented();
++        },
++
++        // abstract
++        clearDropdownAlignmentPreference: function() {
++            // clear the classes used to figure out the preference of where the dropdown should be opened
++            this.container.removeClass("select2-drop-above");
++            this.dropdown.removeClass("select2-drop-above");
++        },
++
++        /**
++         * Opens the dropdown
++         *
++         * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
++         * the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
++         */
++        // abstract
++        open: function () {
++
++            if (!this.shouldOpen()) return false;
++
++            window.setTimeout(this.bind(this.opening), 1);
++
++            return true;
++        },
++
++        /**
++         * Performs the opening of the dropdown
++         */
++        // abstract
++        opening: function() {
++            var cid = this.containerId, selector = this.containerSelector,
++                scroll = "scroll." + cid, resize = "resize." + cid;
++
++            this.container.parents().each(function() {
++                $(this).bind(scroll, function() {
++                    var s2 = $(selector);
++                    if (s2.length == 0) {
++                        $(this).unbind(scroll);
++                    }
++                    s2.select2("close");
++                });
++            });
++
++            $(window).bind(resize, function() {
++                var s2 = $(selector);
++                if (s2.length == 0) {
++                    $(window).unbind(resize);
++                }
++                s2.select2("close");
++            });
++
++            this.clearDropdownAlignmentPreference();
++
++            if (this.search.val() === " ") { this.search.val(""); }
++
++            this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
++
++            this.updateResults(true);
++
++            if(this.dropdown[0] !== this.body().children().last()[0]) {
++                this.dropdown.detach().appendTo(this.body());
++            }
++
++            this.dropdown.show();
++
++            this.positionDropdown();
++            this.dropdown.addClass("select2-drop-active");
++
++            this.ensureHighlightVisible();
++
++            this.focusSearch();
++        },
++
++        // abstract
++        close: function () {
++            if (!this.opened()) return;
++
++            var self = this;
++
++            this.container.parents().each(function() {
++                $(this).unbind("scroll." + self.containerId);
++            });
++            $(window).unbind("resize." + this.containerId);
++
++            this.clearDropdownAlignmentPreference();
++
++            this.dropdown.hide();
++            this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
++            this.results.empty();
++            this.clearSearch();
++
++            this.opts.element.trigger($.Event("close"));
++        },
++
++        // abstract
++        clearSearch: function () {
++
++        },
++
++        // abstract
++        ensureHighlightVisible: function () {
++            var results = this.results, children, index, child, hb, rb, y, more;
++
++            index = this.highlight();
++
++            if (index < 0) return;
++
++            if (index == 0) {
++
++                // if the first element is highlighted scroll all the way to the top,
++                // that way any unselectable headers above it will also be scrolled
++                // into view
++
++                results.scrollTop(0);
++                return;
++            }
++
++            children = results.find(".select2-result-selectable");
++
++            child = $(children[index]);
++
++            hb = child.offset().top + child.outerHeight();
++
++            // if this is the last child lets also make sure select2-more-results is visible
++            if (index === children.length - 1) {
++                more = results.find("li.select2-more-results");
++                if (more.length > 0) {
++                    hb = more.offset().top + more.outerHeight();
++                }
++            }
++
++            rb = results.offset().top + results.outerHeight();
++            if (hb > rb) {
++                results.scrollTop(results.scrollTop() + (hb - rb));
++            }
++            y = child.offset().top - results.offset().top;
++
++            // make sure the top of the element is visible
++            if (y < 0) {
++                results.scrollTop(results.scrollTop() + y); // y is negative
++            }
++        },
++
++        // abstract
++        moveHighlight: function (delta) {
++            var choices = this.results.find(".select2-result-selectable"),
++                index = this.highlight();
++
++            while (index > -1 && index < choices.length) {
++                index += delta;
++                var choice = $(choices[index]);
++                if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled")) {
++                    this.highlight(index);
++                    break;
++                }
++            }
++        },
++
++        // abstract
++        highlight: function (index) {
++            var choices = this.results.find(".select2-result-selectable").not(".select2-disabled");
++
++            if (arguments.length === 0) {
++                return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
++            }
++
++            if (index >= choices.length) index = choices.length - 1;
++            if (index < 0) index = 0;
++
++            choices.removeClass("select2-highlighted");
++
++            $(choices[index]).addClass("select2-highlighted");
++            this.ensureHighlightVisible();
++
++        },
++
++        // abstract
++        countSelectableResults: function() {
++            return this.results.find(".select2-result-selectable").not(".select2-disabled").length;
++        },
++
++        // abstract
++        highlightUnderEvent: function (event) {
++            var el = $(event.target).closest(".select2-result-selectable");
++            if (el.length > 0 && !el.is(".select2-highlighted")) {
++        		var choices = this.results.find('.select2-result-selectable');
++                this.highlight(choices.index(el));
++            } else if (el.length == 0) {
++                // if we are over an unselectable item remove al highlights
++                this.results.find(".select2-highlighted").removeClass("select2-highlighted");
++            }
++        },
++
++        // abstract
++        loadMoreIfNeeded: function () {
++            var results = this.results,
++                more = results.find("li.select2-more-results"),
++                below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
++                offset = -1, // index of first element without data
++                page = this.resultsPage + 1,
++                self=this,
++                term=this.search.val(),
++                context=this.context;
++
++            if (more.length === 0) return;
++            below = more.offset().top - results.offset().top - results.height();
++
++            if (below <= 0) {
++                more.addClass("select2-active");
++                this.opts.query({
++                        term: term,
++                        page: page,
++                        context: context,
++                        matcher: this.opts.matcher,
++                        callback: this.bind(function (data) {
++
++                    // ignore a response if the select2 has been closed before it was received
++                    if (!self.opened()) return;
++
++
++                    self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
++
++                    if (data.more===true) {
++                        more.detach().appendTo(results).text(self.opts.formatLoadMore(page+1));
++                        window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
++                    } else {
++                        more.remove();
++                    }
++                    self.positionDropdown();
++                    self.resultsPage = page;
++                })});
++            }
++        },
++
++        /**
++         * Default tokenizer function which does nothing
++         */
++        tokenize: function() {
++
++        },
++
++        /**
++         * @param initial whether or not this is the call to this method right after the dropdown has been opened
++         */
++        // abstract
++        updateResults: function (initial) {
++            var search = this.search, results = this.results, opts = this.opts, data, self=this, input;
++
++            // if the search is currently hidden we do not alter the results
++            if (initial !== true && (this.showSearchInput === false || !this.opened())) {
++                return;
++            }
++
++            search.addClass("select2-active");
++
++            function postRender() {
++                results.scrollTop(0);
++                search.removeClass("select2-active");
++                self.positionDropdown();
++            }
++
++            function render(html) {
++                results.html(self.opts.escapeMarkup(html));
++                postRender();
++            }
++
++            if (opts.maximumSelectionSize >=1) {
++                data = this.data();
++                if ($.isArray(data) && data.length >= opts.maximumSelectionSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
++            	    render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(opts.maximumSelectionSize) + "</li>");
++            	    return;
++                }
++            }
++
++            if (search.val().length < opts.minimumInputLength && checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
++                render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
++                return;
++            }
++            else {
++                render("<li class='select2-searching'>" + opts.formatSearching() + "</li>");
++            }
++
++            // give the tokenizer a chance to pre-process the input
++            input = this.tokenize();
++            if (input != undefined && input != null) {
++                search.val(input);
++            }
++
++            this.resultsPage = 1;
++            opts.query({
++                    term: search.val(),
++                    page: this.resultsPage,
++                    context: null,
++                    matcher: opts.matcher,
++                    callback: this.bind(function (data) {
++                var def; // default choice
++
++                // ignore a response if the select2 has been closed before it was received
++                if (!this.opened()) return;
++
++                // save context, if any
++                this.context = (data.context===undefined) ? null : data.context;
++
++                // create a default choice and prepend it to the list
++                if (this.opts.createSearchChoice && search.val() !== "") {
++                    def = this.opts.createSearchChoice.call(null, search.val(), data.results);
++                    if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) {
++                        if ($(data.results).filter(
++                            function () {
++                                return equal(self.id(this), self.id(def));
++                            }).length === 0) {
++                            data.results.unshift(def);
++                        }
++                    }
++                }
++
++                if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) {
++                    render("<li class='select2-no-results'>" + opts.formatNoMatches(search.val()) + "</li>");
++                    return;
++                }
++
++                results.empty();
++                self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
++
++                if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) {
++                    results.append("<li class='select2-more-results'>" + self.opts.escapeMarkup(opts.formatLoadMore(this.resultsPage)) + "</li>");
++                    window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
++                }
++
++                this.postprocessResults(data, initial);
++
++                postRender();
++            })});
++        },
++
++        // abstract
++        cancel: function () {
++            this.close();
++        },
++
++        // abstract
++        blur: function () {
++            this.close();
++            this.container.removeClass("select2-container-active");
++            this.dropdown.removeClass("select2-drop-active");
++            // synonymous to .is(':focus'), which is available in jquery >= 1.6
++            if (this.search[0] === document.activeElement) { this.search.blur(); }
++            this.clearSearch();
++            this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
++        },
++
++        // abstract
++        focusSearch: function () {
++            // need to do it here as well as in timeout so it works in IE
++            this.search.show();
++            this.search.focus();
++
++            /* we do this in a timeout so that current event processing can complete before this code is executed.
++             this makes sure the search field is focussed even if the current event would blur it */
++            window.setTimeout(this.bind(function () {
++                // reset the value so IE places the cursor at the end of the input box
++                this.search.show();
++                this.search.focus();
++                this.search.val(this.search.val());
++            }), 10);
++        },