PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /home2/samarhoy/public_html/admin/plugins/datatables-keytable/js
Viewing File: /home2/samarhoy/public_html/admin/plugins/datatables-keytable/js/dataTables.keyTable.js
/*! KeyTable 2.6.4 * ©2009-2021 SpryMedia Ltd - datatables.net/license */ /** * @summary KeyTable * @description Spreadsheet like keyboard navigation for DataTables * @version 2.6.4 * @file dataTables.keyTable.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact * @copyright Copyright 2009-2021 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit * * This source file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. * * For details please refer to: http://www.datatables.net */ (function( factory ){ if ( typeof define === 'function' && define.amd ) { // AMD define( ['jquery', 'datatables.net'], function ( $ ) { return factory( $, window, document ); } ); } else if ( typeof exports === 'object' ) { // CommonJS module.exports = function (root, $) { if ( ! root ) { root = window; } if ( ! $ || ! $.fn.dataTable ) { $ = require('datatables.net')(root, $).$; } return factory( $, root, root.document ); }; } else { // Browser factory( jQuery, window, document ); } }(function( $, window, document, undefined ) { 'use strict'; var DataTable = $.fn.dataTable; var namespaceCounter = 0; var editorNamespaceCounter = 0; var KeyTable = function ( dt, opts ) { // Sanity check that we are using DataTables 1.10 or newer if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) { throw 'KeyTable requires DataTables 1.10.8 or newer'; } // User and defaults configuration object this.c = $.extend( true, {}, DataTable.defaults.keyTable, KeyTable.defaults, opts ); // Internal settings this.s = { /** @type {DataTable.Api} DataTables' API instance */ dt: new DataTable.Api( dt ), enable: true, /** @type {bool} Flag for if a draw is triggered by focus */ focusDraw: false, /** @type {bool} Flag to indicate when waiting for a draw to happen. * Will ignore key presses at this point */ waitingForDraw: false, /** @type {object} Information about the last cell that was focused */ lastFocus: null, /** @type {string} Unique namespace per instance */ namespace: '.keyTable-'+(namespaceCounter++), /** @type {Node} Input element for tabbing into the table */ tabInput: null }; // DOM items this.dom = { }; // Check if row reorder has already been initialised on this table var settings = this.s.dt.settings()[0]; var exisiting = settings.keytable; if ( exisiting ) { return exisiting; } settings.keytable = this; this._constructor(); }; $.extend( KeyTable.prototype, { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * API methods for DataTables API interface */ /** * Blur the table's cell focus */ blur: function () { this._blur(); }, /** * Enable cell focus for the table * * @param {string} state Can be `true`, `false` or `-string navigation-only` */ enable: function ( state ) { this.s.enable = state; }, /** * Get enable status */ enabled: function () { return this.s.enable; }, /** * Focus on a cell * @param {integer} row Row index * @param {integer} column Column index */ focus: function ( row, column ) { this._focus( this.s.dt.cell( row, column ) ); }, /** * Is the cell focused * @param {object} cell Cell index to check * @returns {boolean} true if focused, false otherwise */ focused: function ( cell ) { var lastFocus = this.s.lastFocus; if ( ! lastFocus ) { return false; } var lastIdx = this.s.lastFocus.cell.index(); return cell.row === lastIdx.row && cell.column === lastIdx.column; }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constructor */ /** * Initialise the KeyTable instance * * @private */ _constructor: function () { this._tabInput(); var that = this; var dt = this.s.dt; var table = $( dt.table().node() ); var namespace = this.s.namespace; var editorBlock = false; // Need to be able to calculate the cell positions relative to the table if ( table.css('position') === 'static' ) { table.css( 'position', 'relative' ); } // Click to focus $( dt.table().body() ).on( 'click'+namespace, 'th, td', function (e) { if ( that.s.enable === false ) { return; } var cell = dt.cell( this ); if ( ! cell.any() ) { return; } that._focus( cell, null, false, e ); } ); // Key events $( document ).on( 'keydown'+namespace, function (e) { if ( ! editorBlock ) { that._key( e ); } } ); // Click blur if ( this.c.blurable ) { $( document ).on( 'mousedown'+namespace, function ( e ) { // Click on the search input will blur focus if ( $(e.target).parents( '.dataTables_filter' ).length ) { that._blur(); } // If the click was inside the DataTables container, don't blur if ( $(e.target).parents().filter( dt.table().container() ).length ) { return; } // Don't blur in Editor form if ( $(e.target).parents('div.DTE').length ) { return; } // Or an Editor date input if ( $(e.target).parents('div.editor-datetime').length || $(e.target).parents('div.dt-datetime').length ) { return; } //If the click was inside the fixed columns container, don't blur if ( $(e.target).parents().filter('.DTFC_Cloned').length ) { return; } that._blur(); } ); } if ( this.c.editor ) { var editor = this.c.editor; // Need to disable KeyTable when the main editor is shown editor.on( 'open.keyTableMain', function (e, mode, action) { if ( mode !== 'inline' && that.s.enable ) { that.enable( false ); editor.one( 'close'+namespace, function () { that.enable( true ); } ); } } ); if ( this.c.editOnFocus ) { dt.on( 'key-focus'+namespace+' key-refocus'+namespace, function ( e, dt, cell, orig ) { that._editor( null, orig, true ); } ); } // Activate Editor when a key is pressed (will be ignored, if // already active). dt.on( 'key'+namespace, function ( e, dt, key, cell, orig ) { that._editor( key, orig, false ); } ); // Active editing on double click - it will already have focus from // the click event handler above $( dt.table().body() ).on( 'dblclick'+namespace, 'th, td', function (e) { if ( that.s.enable === false ) { return; } var cell = dt.cell( this ); if ( ! cell.any() ) { return; } if ( that.s.lastFocus && this !== that.s.lastFocus.cell.node() ) { return; } that._editor( null, e, true ); } ); // While Editor is busy processing, we don't want to process any key events editor .on('preSubmit', function () { editorBlock = true; } ) .on('preSubmitCancelled', function () { editorBlock = false; } ) .on('submitComplete', function () { editorBlock = false; } ); } // Stave saving if ( dt.settings()[0].oFeatures.bStateSave ) { dt.on( 'stateSaveParams'+namespace, function (e, s, d) { d.keyTable = that.s.lastFocus ? that.s.lastFocus.cell.index() : null; } ); } dt.on( 'column-visibility'+namespace, function (e) { that._tabInput(); } ); // Redraw - retain focus on the current cell dt.on( 'draw'+namespace, function (e) { that._tabInput(); if ( that.s.focusDraw ) { return; } var lastFocus = that.s.lastFocus; if ( lastFocus ) { var relative = that.s.lastFocus.relative; var info = dt.page.info(); var row = relative.row + info.start; if ( info.recordsDisplay === 0 ) { return; } // Reverse if needed if ( row >= info.recordsDisplay ) { row = info.recordsDisplay - 1; } that._focus( row, relative.column, true, e ); } } ); // Clipboard support if ( this.c.clipboard ) { this._clipboard(); } dt.on( 'destroy'+namespace, function () { that._blur( true ); // Event tidy up dt.off( namespace ); $( dt.table().body() ) .off( 'click'+namespace, 'th, td' ) .off( 'dblclick'+namespace, 'th, td' ); $( document ) .off( 'mousedown'+namespace ) .off( 'keydown'+namespace ) .off( 'copy'+namespace ) .off( 'paste'+namespace ); } ); // Initial focus comes from state or options var state = dt.state.loaded(); if ( state && state.keyTable ) { // Wait until init is done dt.one( 'init', function () { var cell = dt.cell( state.keyTable ); // Ensure that the saved cell still exists if ( cell.any() ) { cell.focus(); } } ); } else if ( this.c.focus ) { dt.cell( this.c.focus ).focus(); } }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Private methods */ /** * Blur the control * * @param {boolean} [noEvents=false] Don't trigger updates / events (for destroying) * @private */ _blur: function (noEvents) { if ( ! this.s.enable || ! this.s.lastFocus ) { return; } var cell = this.s.lastFocus.cell; $( cell.node() ).removeClass( this.c.className ); this.s.lastFocus = null; if ( ! noEvents ) { this._updateFixedColumns(cell.index().column); this._emitEvent( 'key-blur', [ this.s.dt, cell ] ); } }, /** * Clipboard interaction handlers * * @private */ _clipboard: function () { var dt = this.s.dt; var that = this; var namespace = this.s.namespace; // IE8 doesn't support getting selected text if ( ! window.getSelection ) { return; } $(document).on( 'copy'+namespace, function (ejq) { var e = ejq.originalEvent; var selection = window.getSelection().toString(); var focused = that.s.lastFocus; // Only copy cell text to clipboard if there is no other selection // and there is a focused cell if ( ! selection && focused ) { e.clipboardData.setData( 'text/plain', focused.cell.render( that.c.clipboardOrthogonal ) ); e.preventDefault(); } } ); $(document).on( 'paste'+namespace, function (ejq) { var e = ejq.originalEvent; var focused = that.s.lastFocus; var activeEl = document.activeElement; var editor = that.c.editor; var pastedText; if ( focused && (! activeEl || activeEl.nodeName.toLowerCase() === 'body') ) { e.preventDefault(); if ( window.clipboardData && window.clipboardData.getData ) { // IE pastedText = window.clipboardData.getData('Text'); } else if ( e.clipboardData && e.clipboardData.getData ) { // Everything else pastedText = e.clipboardData.getData('text/plain'); } if ( editor ) { // Got Editor - need to activate inline editing, // set the value and submit var options = that._inlineOptions(focused.cell.index()); editor .inline(options.cell, options.field, options.options) .set( editor.displayed()[0], pastedText ) .submit(); } else { // No editor, so just dump the data in focused.cell.data( pastedText ); dt.draw(false); } } } ); }, /** * Get an array of the column indexes that KeyTable can operate on. This * is a merge of the user supplied columns and the visible columns. * * @private */ _columns: function () { var dt = this.s.dt; var user = dt.columns( this.c.columns ).indexes(); var out = []; dt.columns( ':visible' ).every( function (i) { if ( user.indexOf( i ) !== -1 ) { out.push( i ); } } ); return out; }, /** * Perform excel like navigation for Editor by triggering an edit on key * press * * @param {integer} key Key code for the pressed key * @param {object} orig Original event * @private */ _editor: function ( key, orig, hardEdit ) { // If nothing focused, we can't take any action if (! this.s.lastFocus) { return; } // DataTables draw event if (orig && orig.type === 'draw') { return; } var that = this; var dt = this.s.dt; var editor = this.c.editor; var editCell = this.s.lastFocus.cell; var namespace = this.s.namespace + 'e' + editorNamespaceCounter++; // Do nothing if there is already an inline edit in this cell if ( $('div.DTE', editCell.node()).length ) { return; } // Don't activate Editor on control key presses if ( key !== null && ( (key >= 0x00 && key <= 0x09) || key === 0x0b || key === 0x0c || (key >= 0x0e && key <= 0x1f) || (key >= 0x70 && key <= 0x7b) || (key >= 0x7f && key <= 0x9f) ) ) { return; } if ( orig ) { orig.stopPropagation(); // Return key should do nothing - for textareas it would empty the // contents if ( key === 13 ) { orig.preventDefault(); } } var editInline = function () { var options = that._inlineOptions(editCell.index()); editor .one( 'open'+namespace, function () { // Remove cancel open editor.off( 'cancelOpen'+namespace ); // Excel style - select all text if ( ! hardEdit ) { $('div.DTE_Field_InputControl input, div.DTE_Field_InputControl textarea').select(); } // Reduce the keys the Keys listens for dt.keys.enable( hardEdit ? 'tab-only' : 'navigation-only' ); // On blur of the navigation submit dt.on( 'key-blur.editor', function (e, dt, cell) { if ( editor.displayed() && cell.node() === editCell.node() ) { editor.submit(); } } ); // Highlight the cell a different colour on full edit if ( hardEdit ) { $( dt.table().container() ).addClass('dtk-focus-alt'); } // If the dev cancels the submit, we need to return focus editor.on( 'preSubmitCancelled'+namespace, function () { setTimeout( function () { that._focus( editCell, null, false ); }, 50 ); } ); editor.on( 'submitUnsuccessful'+namespace, function () { that._focus( editCell, null, false ); } ); // Restore full key navigation on close editor.one( 'close'+namespace, function () { dt.keys.enable( true ); dt.off( 'key-blur.editor' ); editor.off( namespace ); $( dt.table().container() ).removeClass('dtk-focus-alt'); if (that.s.returnSubmit) { that.s.returnSubmit = false; that._emitEvent( 'key-return-submit', [dt, editCell] ); } } ); } ) .one( 'cancelOpen'+namespace, function () { // `preOpen` can cancel the display of the form, so it // might be that the open event handler isn't needed editor.off( namespace ); } ) .inline(options.cell, options.field, options.options); }; // Editor 1.7 listens for `return` on keyup, so if return is the trigger // key, we need to wait for `keyup` otherwise Editor would just submit // the content triggered by this keypress. if ( key === 13 ) { hardEdit = true; $(document).one( 'keyup', function () { // immediately removed editInline(); } ); } else { editInline(); } }, _inlineOptions: function (cellIdx) { if (this.c.editorOptions) { return this.c.editorOptions(cellIdx); } return { cell: cellIdx, field: undefined, options: undefined }; }, /** * Emit an event on the DataTable for listeners * * @param {string} name Event name * @param {array} args Event arguments * @private */ _emitEvent: function ( name, args ) { this.s.dt.iterator( 'table', function ( ctx, i ) { $(ctx.nTable).triggerHandler( name, args ); } ); }, /** * Focus on a particular cell, shifting the table's paging if required * * @param {DataTables.Api|integer} row Can be given as an API instance that * contains the cell to focus or as an integer. As the latter it is the * visible row index (from the whole data set) - NOT the data index * @param {integer} [column] Not required if a cell is given as the first * parameter. Otherwise this is the column data index for the cell to * focus on * @param {boolean} [shift=true] Should the viewport be moved to show cell * @private */ _focus: function ( row, column, shift, originalEvent ) { var that = this; var dt = this.s.dt; var pageInfo = dt.page.info(); var lastFocus = this.s.lastFocus; if ( ! originalEvent) { originalEvent = null; } if ( ! this.s.enable ) { return; } if ( typeof row !== 'number' ) { // Its an API instance - check that there is actually a row if ( ! row.any() ) { return; } // Convert the cell to a row and column var index = row.index(); column = index.column; row = dt .rows( { filter: 'applied', order: 'applied' } ) .indexes() .indexOf( index.row ); // Don't focus rows that were filtered out. if ( row < 0 ) { return; } // For server-side processing normalise the row by adding the start // point, since `rows().indexes()` includes only rows that are // available at the client-side if ( pageInfo.serverSide ) { row += pageInfo.start; } } // Is the row on the current page? If not, we need to redraw to show the // page if ( pageInfo.length !== -1 && (row < pageInfo.start || row >= pageInfo.start+pageInfo.length) ) { this.s.focusDraw = true; this.s.waitingForDraw = true; dt .one( 'draw', function () { that.s.focusDraw = false; that.s.waitingForDraw = false; that._focus( row, column, undefined, originalEvent ); } ) .page( Math.floor( row / pageInfo.length ) ) .draw( false ); return; } // In the available columns? if ( $.inArray( column, this._columns() ) === -1 ) { return; } // De-normalise the server-side processing row, so we select the row // in its displayed position if ( pageInfo.serverSide ) { row -= pageInfo.start; } // Get the cell from the current position - ignoring any cells which might // not have been rendered (therefore can't use `:eq()` selector). var cells = dt.cells( null, column, {search: 'applied', order: 'applied'} ).flatten(); var cell = dt.cell( cells[ row ] ); if ( lastFocus ) { // Don't trigger a refocus on the same cell if ( lastFocus.node === cell.node() ) { this._emitEvent( 'key-refocus', [ this.s.dt, cell, originalEvent || null ] ); return; } // Otherwise blur the old focus this._blur(); } // Clear focus from other tables this._removeOtherFocus(); var node = $( cell.node() ); node.addClass( this.c.className ); this._updateFixedColumns(column); // Shift viewpoint and page to make cell visible if ( shift === undefined || shift === true ) { this._scroll( $(window), $(document.body), node, 'offset' ); var bodyParent = dt.table().body().parentNode; if ( bodyParent !== dt.table().header().parentNode ) { var parent = $(bodyParent.parentNode); this._scroll( parent, parent, node, 'position' ); } } // Event and finish this.s.lastFocus = { cell: cell, node: cell.node(), relative: { row: dt.rows( { page: 'current' } ).indexes().indexOf( cell.index().row ), column: cell.index().column } }; this._emitEvent( 'key-focus', [ this.s.dt, cell, originalEvent || null ] ); dt.state.save(); }, /** * Handle key press * * @param {object} e Event * @private */ _key: function ( e ) { // If we are waiting for a draw to happen from another key event, then // do nothing for this new key press. if ( this.s.waitingForDraw ) { e.preventDefault(); return; } var enable = this.s.enable; this.s.returnSubmit = (enable === 'navigation-only' || enable === 'tab-only') && e.keyCode === 13 ? true : false; var navEnable = enable === true || enable === 'navigation-only'; if ( ! enable ) { return; } if ( (e.keyCode === 0 || e.ctrlKey || e.metaKey || e.altKey) && !(e.ctrlKey && e.altKey) ) { return; } // If not focused, then there is no key action to take var lastFocus = this.s.lastFocus; if ( ! lastFocus ) { return; } // And the last focus still exists! if ( ! this.s.dt.cell(lastFocus.node).any() ) { this.s.lastFocus = null; return; } var that = this; var dt = this.s.dt; var scrolling = this.s.dt.settings()[0].oScroll.sY ? true : false; // If we are not listening for this key, do nothing if ( this.c.keys && $.inArray( e.keyCode, this.c.keys ) === -1 ) { return; } switch( e.keyCode ) { case 9: // tab // `enable` can be tab-only this._shift( e, e.shiftKey ? 'left' : 'right', true ); break; case 27: // esc if ( this.c.blurable && enable === true ) { this._blur(); } break; case 33: // page up (previous page) case 34: // page down (next page) if ( navEnable && !scrolling ) { e.preventDefault(); dt .page( e.keyCode === 33 ? 'previous' : 'next' ) .draw( false ); } break; case 35: // end (end of current page) case 36: // home (start of current page) if ( navEnable ) { e.preventDefault(); var indexes = dt.cells( {page: 'current'} ).indexes(); var colIndexes = this._columns(); this._focus( dt.cell( indexes[ e.keyCode === 35 ? indexes.length-1 : colIndexes[0] ] ), null, true, e ); } break; case 37: // left arrow if ( navEnable ) { this._shift( e, 'left' ); } break; case 38: // up arrow if ( navEnable ) { this._shift( e, 'up' ); } break; case 39: // right arrow if ( navEnable ) { this._shift( e, 'right' ); } break; case 40: // down arrow if ( navEnable ) { this._shift( e, 'down' ); } break; case 113: // F2 - Excel like hard edit if ( this.c.editor ) { this._editor(null, e, true); break; } // else fallthrough default: // Everything else - pass through only when fully enabled if ( enable === true ) { this._emitEvent( 'key', [ dt, e.keyCode, this.s.lastFocus.cell, e ] ); } break; } }, /** * Remove focus from all tables other than this one */ _removeOtherFocus: function () { var thisTable = this.s.dt.table().node(); $.fn.dataTable.tables({api:true}).iterator('table', function (settings) { if (this.table().node() !== thisTable) { this.cell.blur(); } }); }, /** * Scroll a container to make a cell visible in it. This can be used for * both DataTables scrolling and native window scrolling. * * @param {jQuery} container Scrolling container * @param {jQuery} scroller Item being scrolled * @param {jQuery} cell Cell in the scroller * @param {string} posOff `position` or `offset` - which to use for the * calculation. `offset` for the document, otherwise `position` * @private */ _scroll: function ( container, scroller, cell, posOff ) { var offset = cell[posOff](); var height = cell.outerHeight(); var width = cell.outerWidth(); var scrollTop = scroller.scrollTop(); var scrollLeft = scroller.scrollLeft(); var containerHeight = container.height(); var containerWidth = container.width(); // If Scroller is being used, the table can be `position: absolute` and that // needs to be taken account of in the offset. If no Scroller, this will be 0 if ( posOff === 'position' ) { offset.top += parseInt( cell.closest('table').css('top'), 10 ); } // Top correction if ( offset.top < scrollTop ) { scroller.scrollTop( offset.top ); } // Left correction if ( offset.left < scrollLeft ) { scroller.scrollLeft( offset.left ); } // Bottom correction if ( offset.top + height > scrollTop + containerHeight && height < containerHeight ) { scroller.scrollTop( offset.top + height - containerHeight ); } // Right correction if ( offset.left + width > scrollLeft + containerWidth && width < containerWidth ) { scroller.scrollLeft( offset.left + width - containerWidth ); } }, /** * Calculate a single offset movement in the table - up, down, left and * right and then perform the focus if possible * * @param {object} e Event object * @param {string} direction Movement direction * @param {boolean} keyBlurable `true` if the key press can result in the * table being blurred. This is so arrow keys won't blur the table, but * tab will. * @private */ _shift: function ( e, direction, keyBlurable ) { var that = this; var dt = this.s.dt; var pageInfo = dt.page.info(); var rows = pageInfo.recordsDisplay; var columns = this._columns(); var last = this.s.lastFocus; if ( ! last ) { return; } var currentCell = last.cell; if ( ! currentCell ) { return; } var currRow = dt .rows( { filter: 'applied', order: 'applied' } ) .indexes() .indexOf( currentCell.index().row ); // When server-side processing, `rows().indexes()` only gives the rows // that are available at the client-side, so we need to normalise the // row's current position by the display start point if ( pageInfo.serverSide ) { currRow += pageInfo.start; } var currCol = dt .columns( columns ) .indexes() .indexOf( currentCell.index().column ); var row = currRow, column = columns[ currCol ]; // row is the display, column is an index // If the direction is rtl then the logic needs to be inverted from this point forwards if($(dt.table().node()).css('direction') === 'rtl') { if(direction === 'right') { direction = 'left'; } else if(direction === 'left'){ direction = 'right'; } } if ( direction === 'right' ) { if ( currCol >= columns.length - 1 ) { row++; column = columns[0]; } else { column = columns[ currCol+1 ]; } } else if ( direction === 'left' ) { if ( currCol === 0 ) { row--; column = columns[ columns.length - 1 ]; } else { column = columns[ currCol-1 ]; } } else if ( direction === 'up' ) { row--; } else if ( direction === 'down' ) { row++; } if ( row >= 0 && row < rows && $.inArray( column, columns ) !== -1 ) { if (e) { e.preventDefault(); } this._focus( row, column, true, e ); } else if ( ! keyBlurable || ! this.c.blurable ) { // No new focus, but if the table isn't blurable, then don't loose // focus if (e) { e.preventDefault(); } } else { this._blur(); } }, /** * Create and insert a hidden input element that can receive focus on behalf * of the table * * @private */ _tabInput: function () { var that = this; var dt = this.s.dt; var tabIndex = this.c.tabIndex !== null ? this.c.tabIndex : dt.settings()[0].iTabIndex; if ( tabIndex == -1 ) { return; } // Only create the input element once on first class if (! this.s.tabInput) { var div = $('<div><input type="text" tabindex="'+tabIndex+'"/></div>') .css( { position: 'absolute', height: 1, width: 0, overflow: 'hidden' } ); div.children().on( 'focus', function (e) { var cell = dt.cell(':eq(0)', that._columns(), {page: 'current'}); if ( cell.any() ) { that._focus( cell, null, true, e ); } } ); this.s.tabInput = div; } // Insert the input element into the first cell in the table's body var cell = this.s.dt.cell(':eq(0)', '0:visible', {page: 'current', order: 'current'}).node(); if (cell) { $(cell).prepend(this.s.tabInput); } }, /** * Update fixed columns if they are enabled and if the cell we are * focusing is inside a fixed column * @param {integer} column Index of the column being changed * @private */ _updateFixedColumns: function( column ) { var dt = this.s.dt; var settings = dt.settings()[0]; if ( settings._oFixedColumns ) { var leftCols = settings._oFixedColumns.s.iLeftColumns; var rightCols = settings.aoColumns.length - settings._oFixedColumns.s.iRightColumns; if (column < leftCols || column >= rightCols) { dt.fixedColumns().update(); } } } } ); /** * KeyTable default settings for initialisation * * @namespace * @name KeyTable.defaults * @static */ KeyTable.defaults = { /** * Can focus be removed from the table * @type {Boolean} */ blurable: true, /** * Class to give to the focused cell * @type {String} */ className: 'focus', /** * Enable or disable clipboard support * @type {Boolean} */ clipboard: true, /** * Orthogonal data that should be copied to clipboard * @type {string} */ clipboardOrthogonal: 'display', /** * Columns that can be focused. This is automatically merged with the * visible columns as only visible columns can gain focus. * @type {String} */ columns: '', // all /** * Editor instance to automatically perform Excel like navigation * @type {Editor} */ editor: null, /** * Trigger editing immediately on focus * @type {boolean} */ editOnFocus: false, /** * Options to pass to Editor's inline method * @type {function} */ editorOptions: null, /** * Select a cell to automatically select on start up. `null` for no * automatic selection * @type {cell-selector} */ focus: null, /** * Array of keys to listen for * @type {null|array} */ keys: null, /** * Tab index for where the table should sit in the document's tab flow * @type {integer|null} */ tabIndex: null }; KeyTable.version = "2.6.4"; $.fn.dataTable.KeyTable = KeyTable; $.fn.DataTable.KeyTable = KeyTable; DataTable.Api.register( 'cell.blur()', function () { return this.iterator( 'table', function (ctx) { if ( ctx.keytable ) { ctx.keytable.blur(); } } ); } ); DataTable.Api.register( 'cell().focus()', function () { return this.iterator( 'cell', function (ctx, row, column) { if ( ctx.keytable ) { ctx.keytable.focus( row, column ); } } ); } ); DataTable.Api.register( 'keys.disable()', function () { return this.iterator( 'table', function (ctx) { if ( ctx.keytable ) { ctx.keytable.enable( false ); } } ); } ); DataTable.Api.register( 'keys.enable()', function ( opts ) { return this.iterator( 'table', function (ctx) { if ( ctx.keytable ) { ctx.keytable.enable( opts === undefined ? true : opts ); } } ); } ); DataTable.Api.register( 'keys.enabled()', function ( opts ) { var ctx = this.context; if (ctx.length) { return ctx[0].keytable ? ctx[0].keytable.enabled() : false; } return false; } ); DataTable.Api.register( 'keys.move()', function ( dir ) { return this.iterator( 'table', function (ctx) { if ( ctx.keytable ) { ctx.keytable._shift( null, dir, false ); } } ); } ); // Cell selector DataTable.ext.selector.cell.push( function ( settings, opts, cells ) { var focused = opts.focused; var kt = settings.keytable; var out = []; if ( ! kt || focused === undefined ) { return cells; } for ( var i=0, ien=cells.length ; i<ien ; i++ ) { if ( (focused === true && kt.focused( cells[i] ) ) || (focused === false && ! kt.focused( cells[i] ) ) ) { out.push( cells[i] ); } } return out; } ); // Attach a listener to the document which listens for DataTables initialisation // events so we can automatically initialise $(document).on( 'preInit.dt.dtk', function (e, settings, json) { if ( e.namespace !== 'dt' ) { return; } var init = settings.oInit.keys; var defaults = DataTable.defaults.keys; if ( init || defaults ) { var opts = $.extend( {}, defaults, init ); if ( init !== false ) { new KeyTable( settings, opts ); } } } ); return KeyTable; }));