PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/home/austenwhite.co.uk/www/dev/pub/static/adminhtml/Magento/backend/en_US/js/bundle/
Upload File :
Current File : /home/austenwhite.co.uk/www/dev/pub/static/adminhtml/Magento/backend/en_US/js/bundle/bundle8.js
require.config({"config": {
        "jsbuild":{"jquery/fileUploader/jquery.fileupload.js":"/*\n * jQuery File Upload Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, require */\n/* eslint-disable new-cap */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery', 'jquery/fileUploader/vendor/jquery.ui.widget'], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(require('jquery'), require('jquery/fileUploader/vendor/jquery.ui.widget'));\n  } else {\n    // Browser globals:\n    factory(window.jQuery);\n  }\n})(function ($) {\n  'use strict';\n\n  // Detect file input support, based on\n  // https://viljamis.com/2012/file-upload-support-on-mobile/\n  $.support.fileInput = !(\n    new RegExp(\n      // Handle devices which give false positives for the feature detection:\n      '(Android (1\\\\.[0156]|2\\\\.[01]))' +\n        '|(Windows Phone (OS 7|8\\\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +\n        '|(w(eb)?OSBrowser)|(webOS)' +\n        '|(Kindle/(1\\\\.0|2\\\\.[05]|3\\\\.0))'\n    ).test(window.navigator.userAgent) ||\n    // Feature detection for all other devices:\n    $('<input type=\"file\"/>').prop('disabled')\n  );\n\n  // The FileReader API is not actually used, but works as feature detection,\n  // as some Safari versions (5?) support XHR file uploads via the FormData API,\n  // but not non-multipart XHR file uploads.\n  // window.XMLHttpRequestUpload is not available on IE10, so we check for\n  // window.ProgressEvent instead to detect XHR2 file upload capability:\n  $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);\n  $.support.xhrFormDataFileUpload = !!window.FormData;\n\n  // Detect support for Blob slicing (required for chunked uploads):\n  $.support.blobSlice =\n    window.Blob &&\n    (Blob.prototype.slice ||\n      Blob.prototype.webkitSlice ||\n      Blob.prototype.mozSlice);\n\n  /**\n   * Helper function to create drag handlers for dragover/dragenter/dragleave\n   *\n   * @param {string} type Event type\n   * @returns {Function} Drag handler\n   */\n  function getDragHandler(type) {\n    var isDragOver = type === 'dragover';\n    return function (e) {\n      e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;\n      var dataTransfer = e.dataTransfer;\n      if (\n        dataTransfer &&\n        $.inArray('Files', dataTransfer.types) !== -1 &&\n        this._trigger(type, $.Event(type, { delegatedEvent: e })) !== false\n      ) {\n        e.preventDefault();\n        if (isDragOver) {\n          dataTransfer.dropEffect = 'copy';\n        }\n      }\n    };\n  }\n\n  // The fileupload widget listens for change events on file input fields defined\n  // via fileInput setting and paste or drop events of the given dropZone.\n  // In addition to the default jQuery Widget methods, the fileupload widget\n  // exposes the \"add\" and \"send\" methods, to add or directly send files using\n  // the fileupload API.\n  // By default, files added via file input selection, paste, drag & drop or\n  // \"add\" method are uploaded immediately, but it is possible to override\n  // the \"add\" callback option to queue file uploads.\n  $.widget('blueimp.fileupload', {\n    options: {\n      // The drop target element(s), by the default the complete document.\n      // Set to null to disable drag & drop support:\n      dropZone: $(document),\n      // The paste target element(s), by the default undefined.\n      // Set to a DOM node or jQuery object to enable file pasting:\n      pasteZone: undefined,\n      // The file input field(s), that are listened to for change events.\n      // If undefined, it is set to the file input fields inside\n      // of the widget element on plugin initialization.\n      // Set to null to disable the change listener.\n      fileInput: undefined,\n      // By default, the file input field is replaced with a clone after\n      // each input field change event. This is required for iframe transport\n      // queues and allows change events to be fired for the same file\n      // selection, but can be disabled by setting the following option to false:\n      replaceFileInput: true,\n      // The parameter name for the file form data (the request argument name).\n      // If undefined or empty, the name property of the file input field is\n      // used, or \"files[]\" if the file input name property is also empty,\n      // can be a string or an array of strings:\n      paramName: undefined,\n      // By default, each file of a selection is uploaded using an individual\n      // request for XHR type uploads. Set to false to upload file\n      // selections in one request each:\n      singleFileUploads: true,\n      // To limit the number of files uploaded with one XHR request,\n      // set the following option to an integer greater than 0:\n      limitMultiFileUploads: undefined,\n      // The following option limits the number of files uploaded with one\n      // XHR request to keep the request size under or equal to the defined\n      // limit in bytes:\n      limitMultiFileUploadSize: undefined,\n      // Multipart file uploads add a number of bytes to each uploaded file,\n      // therefore the following option adds an overhead for each file used\n      // in the limitMultiFileUploadSize configuration:\n      limitMultiFileUploadSizeOverhead: 512,\n      // Set the following option to true to issue all file upload requests\n      // in a sequential order:\n      sequentialUploads: false,\n      // To limit the number of concurrent uploads,\n      // set the following option to an integer greater than 0:\n      limitConcurrentUploads: undefined,\n      // Set the following option to true to force iframe transport uploads:\n      forceIframeTransport: false,\n      // Set the following option to the location of a redirect url on the\n      // origin server, for cross-domain iframe transport uploads:\n      redirect: undefined,\n      // The parameter name for the redirect url, sent as part of the form\n      // data and set to 'redirect' if this option is empty:\n      redirectParamName: undefined,\n      // Set the following option to the location of a postMessage window,\n      // to enable postMessage transport uploads:\n      postMessage: undefined,\n      // By default, XHR file uploads are sent as multipart/form-data.\n      // The iframe transport is always using multipart/form-data.\n      // Set to false to enable non-multipart XHR uploads:\n      multipart: true,\n      // To upload large files in smaller chunks, set the following option\n      // to a preferred maximum chunk size. If set to 0, null or undefined,\n      // or the browser does not support the required Blob API, files will\n      // be uploaded as a whole.\n      maxChunkSize: undefined,\n      // When a non-multipart upload or a chunked multipart upload has been\n      // aborted, this option can be used to resume the upload by setting\n      // it to the size of the already uploaded bytes. This option is most\n      // useful when modifying the options object inside of the \"add\" or\n      // \"send\" callbacks, as the options are cloned for each file upload.\n      uploadedBytes: undefined,\n      // By default, failed (abort or error) file uploads are removed from the\n      // global progress calculation. Set the following option to false to\n      // prevent recalculating the global progress data:\n      recalculateProgress: true,\n      // Interval in milliseconds to calculate and trigger progress events:\n      progressInterval: 100,\n      // Interval in milliseconds to calculate progress bitrate:\n      bitrateInterval: 500,\n      // By default, uploads are started automatically when adding files:\n      autoUpload: true,\n      // By default, duplicate file names are expected to be handled on\n      // the server-side. If this is not possible (e.g. when uploading\n      // files directly to Amazon S3), the following option can be set to\n      // an empty object or an object mapping existing filenames, e.g.:\n      // { \"image.jpg\": true, \"image (1).jpg\": true }\n      // If it is set, all files will be uploaded with unique filenames,\n      // adding increasing number suffixes if necessary, e.g.:\n      // \"image (2).jpg\"\n      uniqueFilenames: undefined,\n\n      // Error and info messages:\n      messages: {\n        uploadedBytes: 'Uploaded bytes exceed file size'\n      },\n\n      // Translation function, gets the message key to be translated\n      // and an object with context specific data as arguments:\n      i18n: function (message, context) {\n        // eslint-disable-next-line no-param-reassign\n        message = this.messages[message] || message.toString();\n        if (context) {\n          $.each(context, function (key, value) {\n            // eslint-disable-next-line no-param-reassign\n            message = message.replace('{' + key + '}', value);\n          });\n        }\n        return message;\n      },\n\n      // Additional form data to be sent along with the file uploads can be set\n      // using this option, which accepts an array of objects with name and\n      // value properties, a function returning such an array, a FormData\n      // object (for XHR file uploads), or a simple object.\n      // The form of the first fileInput is given as parameter to the function:\n      formData: function (form) {\n        return form.serializeArray();\n      },\n\n      // The add callback is invoked as soon as files are added to the fileupload\n      // widget (via file input selection, drag & drop, paste or add API call).\n      // If the singleFileUploads option is enabled, this callback will be\n      // called once for each file in the selection for XHR file uploads, else\n      // once for each file selection.\n      //\n      // The upload starts when the submit method is invoked on the data parameter.\n      // The data object contains a files property holding the added files\n      // and allows you to override plugin options as well as define ajax settings.\n      //\n      // Listeners for this callback can also be bound the following way:\n      // .on('fileuploadadd', func);\n      //\n      // data.submit() returns a Promise object and allows to attach additional\n      // handlers using jQuery's Deferred callbacks:\n      // data.submit().done(func).fail(func).always(func);\n      add: function (e, data) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        if (\n          data.autoUpload ||\n          (data.autoUpload !== false &&\n            $(this).fileupload('option', 'autoUpload'))\n        ) {\n          data.process().done(function () {\n            data.submit();\n          });\n        }\n      },\n\n      // Other callbacks:\n\n      // Callback for the submit event of each file upload:\n      // submit: function (e, data) {}, // .on('fileuploadsubmit', func);\n\n      // Callback for the start of each file upload request:\n      // send: function (e, data) {}, // .on('fileuploadsend', func);\n\n      // Callback for successful uploads:\n      // done: function (e, data) {}, // .on('fileuploaddone', func);\n\n      // Callback for failed (abort or error) uploads:\n      // fail: function (e, data) {}, // .on('fileuploadfail', func);\n\n      // Callback for completed (success, abort or error) requests:\n      // always: function (e, data) {}, // .on('fileuploadalways', func);\n\n      // Callback for upload progress events:\n      // progress: function (e, data) {}, // .on('fileuploadprogress', func);\n\n      // Callback for global upload progress events:\n      // progressall: function (e, data) {}, // .on('fileuploadprogressall', func);\n\n      // Callback for uploads start, equivalent to the global ajaxStart event:\n      // start: function (e) {}, // .on('fileuploadstart', func);\n\n      // Callback for uploads stop, equivalent to the global ajaxStop event:\n      // stop: function (e) {}, // .on('fileuploadstop', func);\n\n      // Callback for change events of the fileInput(s):\n      // change: function (e, data) {}, // .on('fileuploadchange', func);\n\n      // Callback for paste events to the pasteZone(s):\n      // paste: function (e, data) {}, // .on('fileuploadpaste', func);\n\n      // Callback for drop events of the dropZone(s):\n      // drop: function (e, data) {}, // .on('fileuploaddrop', func);\n\n      // Callback for dragover events of the dropZone(s):\n      // dragover: function (e) {}, // .on('fileuploaddragover', func);\n\n      // Callback before the start of each chunk upload request (before form data initialization):\n      // chunkbeforesend: function (e, data) {}, // .on('fileuploadchunkbeforesend', func);\n\n      // Callback for the start of each chunk upload request:\n      // chunksend: function (e, data) {}, // .on('fileuploadchunksend', func);\n\n      // Callback for successful chunk uploads:\n      // chunkdone: function (e, data) {}, // .on('fileuploadchunkdone', func);\n\n      // Callback for failed (abort or error) chunk uploads:\n      // chunkfail: function (e, data) {}, // .on('fileuploadchunkfail', func);\n\n      // Callback for completed (success, abort or error) chunk upload requests:\n      // chunkalways: function (e, data) {}, // .on('fileuploadchunkalways', func);\n\n      // The plugin options are used as settings object for the ajax calls.\n      // The following are jQuery ajax settings required for the file uploads:\n      processData: false,\n      contentType: false,\n      cache: false,\n      timeout: 0\n    },\n\n    // jQuery versions before 1.8 require promise.pipe if the return value is\n    // used, as promise.then in older versions has a different behavior, see:\n    // https://blog.jquery.com/2012/08/09/jquery-1-8-released/\n    // https://bugs.jquery.com/ticket/11010\n    // https://github.com/blueimp/jQuery-File-Upload/pull/3435\n    _promisePipe: (function () {\n      var parts = $.fn.jquery.split('.');\n      return Number(parts[0]) > 1 || Number(parts[1]) > 7 ? 'then' : 'pipe';\n    })(),\n\n    // A list of options that require reinitializing event listeners and/or\n    // special initialization code:\n    _specialOptions: [\n      'fileInput',\n      'dropZone',\n      'pasteZone',\n      'multipart',\n      'forceIframeTransport'\n    ],\n\n    _blobSlice:\n      $.support.blobSlice &&\n      function () {\n        var slice = this.slice || this.webkitSlice || this.mozSlice;\n        return slice.apply(this, arguments);\n      },\n\n    _BitrateTimer: function () {\n      this.timestamp = Date.now ? Date.now() : new Date().getTime();\n      this.loaded = 0;\n      this.bitrate = 0;\n      this.getBitrate = function (now, loaded, interval) {\n        var timeDiff = now - this.timestamp;\n        if (!this.bitrate || !interval || timeDiff > interval) {\n          this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;\n          this.loaded = loaded;\n          this.timestamp = now;\n        }\n        return this.bitrate;\n      };\n    },\n\n    _isXHRUpload: function (options) {\n      return (\n        !options.forceIframeTransport &&\n        ((!options.multipart && $.support.xhrFileUpload) ||\n          $.support.xhrFormDataFileUpload)\n      );\n    },\n\n    _getFormData: function (options) {\n      var formData;\n      if ($.type(options.formData) === 'function') {\n        return options.formData(options.form);\n      }\n      if ($.isArray(options.formData)) {\n        return options.formData;\n      }\n      if ($.type(options.formData) === 'object') {\n        formData = [];\n        $.each(options.formData, function (name, value) {\n          formData.push({ name: name, value: value });\n        });\n        return formData;\n      }\n      return [];\n    },\n\n    _getTotal: function (files) {\n      var total = 0;\n      $.each(files, function (index, file) {\n        total += file.size || 1;\n      });\n      return total;\n    },\n\n    _initProgressObject: function (obj) {\n      var progress = {\n        loaded: 0,\n        total: 0,\n        bitrate: 0\n      };\n      if (obj._progress) {\n        $.extend(obj._progress, progress);\n      } else {\n        obj._progress = progress;\n      }\n    },\n\n    _initResponseObject: function (obj) {\n      var prop;\n      if (obj._response) {\n        for (prop in obj._response) {\n          if (Object.prototype.hasOwnProperty.call(obj._response, prop)) {\n            delete obj._response[prop];\n          }\n        }\n      } else {\n        obj._response = {};\n      }\n    },\n\n    _onProgress: function (e, data) {\n      if (e.lengthComputable) {\n        var now = Date.now ? Date.now() : new Date().getTime(),\n          loaded;\n        if (\n          data._time &&\n          data.progressInterval &&\n          now - data._time < data.progressInterval &&\n          e.loaded !== e.total\n        ) {\n          return;\n        }\n        data._time = now;\n        loaded =\n          Math.floor(\n            (e.loaded / e.total) * (data.chunkSize || data._progress.total)\n          ) + (data.uploadedBytes || 0);\n        // Add the difference from the previously loaded state\n        // to the global loaded counter:\n        this._progress.loaded += loaded - data._progress.loaded;\n        this._progress.bitrate = this._bitrateTimer.getBitrate(\n          now,\n          this._progress.loaded,\n          data.bitrateInterval\n        );\n        data._progress.loaded = data.loaded = loaded;\n        data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(\n          now,\n          loaded,\n          data.bitrateInterval\n        );\n        // Trigger a custom progress event with a total data property set\n        // to the file size(s) of the current upload and a loaded data\n        // property calculated accordingly:\n        this._trigger(\n          'progress',\n          $.Event('progress', { delegatedEvent: e }),\n          data\n        );\n        // Trigger a global progress event for all current file uploads,\n        // including ajax calls queued for sequential file uploads:\n        this._trigger(\n          'progressall',\n          $.Event('progressall', { delegatedEvent: e }),\n          this._progress\n        );\n      }\n    },\n\n    _initProgressListener: function (options) {\n      var that = this,\n        xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();\n      // Access to the native XHR object is required to add event listeners\n      // for the upload progress event:\n      if (xhr.upload) {\n        $(xhr.upload).on('progress', function (e) {\n          var oe = e.originalEvent;\n          // Make sure the progress event properties get copied over:\n          e.lengthComputable = oe.lengthComputable;\n          e.loaded = oe.loaded;\n          e.total = oe.total;\n          that._onProgress(e, options);\n        });\n        options.xhr = function () {\n          return xhr;\n        };\n      }\n    },\n\n    _deinitProgressListener: function (options) {\n      var xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();\n      if (xhr.upload) {\n        $(xhr.upload).off('progress');\n      }\n    },\n\n    _isInstanceOf: function (type, obj) {\n      // Cross-frame instanceof check\n      return Object.prototype.toString.call(obj) === '[object ' + type + ']';\n    },\n\n    _getUniqueFilename: function (name, map) {\n      // eslint-disable-next-line no-param-reassign\n      name = String(name);\n      if (map[name]) {\n        // eslint-disable-next-line no-param-reassign\n        name = name.replace(\n          /(?: \\(([\\d]+)\\))?(\\.[^.]+)?$/,\n          function (_, p1, p2) {\n            var index = p1 ? Number(p1) + 1 : 1;\n            var ext = p2 || '';\n            return ' (' + index + ')' + ext;\n          }\n        );\n        return this._getUniqueFilename(name, map);\n      }\n      map[name] = true;\n      return name;\n    },\n\n    _initXHRData: function (options) {\n      var that = this,\n        formData,\n        file = options.files[0],\n        // Ignore non-multipart setting if not supported:\n        multipart = options.multipart || !$.support.xhrFileUpload,\n        paramName =\n          $.type(options.paramName) === 'array'\n            ? options.paramName[0]\n            : options.paramName;\n      options.headers = $.extend({}, options.headers);\n      if (options.contentRange) {\n        options.headers['Content-Range'] = options.contentRange;\n      }\n      if (!multipart || options.blob || !this._isInstanceOf('File', file)) {\n        options.headers['Content-Disposition'] =\n          'attachment; filename=\"' +\n          encodeURI(file.uploadName || file.name) +\n          '\"';\n      }\n      if (!multipart) {\n        options.contentType = file.type || 'application/octet-stream';\n        options.data = options.blob || file;\n      } else if ($.support.xhrFormDataFileUpload) {\n        if (options.postMessage) {\n          // window.postMessage does not allow sending FormData\n          // objects, so we just add the File/Blob objects to\n          // the formData array and let the postMessage window\n          // create the FormData object out of this array:\n          formData = this._getFormData(options);\n          if (options.blob) {\n            formData.push({\n              name: paramName,\n              value: options.blob\n            });\n          } else {\n            $.each(options.files, function (index, file) {\n              formData.push({\n                name:\n                  ($.type(options.paramName) === 'array' &&\n                    options.paramName[index]) ||\n                  paramName,\n                value: file\n              });\n            });\n          }\n        } else {\n          if (that._isInstanceOf('FormData', options.formData)) {\n            formData = options.formData;\n          } else {\n            formData = new FormData();\n            $.each(this._getFormData(options), function (index, field) {\n              formData.append(field.name, field.value);\n            });\n          }\n          if (options.blob) {\n            formData.append(\n              paramName,\n              options.blob,\n              file.uploadName || file.name\n            );\n          } else {\n            $.each(options.files, function (index, file) {\n              // This check allows the tests to run with\n              // dummy objects:\n              if (\n                that._isInstanceOf('File', file) ||\n                that._isInstanceOf('Blob', file)\n              ) {\n                var fileName = file.uploadName || file.name;\n                if (options.uniqueFilenames) {\n                  fileName = that._getUniqueFilename(\n                    fileName,\n                    options.uniqueFilenames\n                  );\n                }\n                formData.append(\n                  ($.type(options.paramName) === 'array' &&\n                    options.paramName[index]) ||\n                    paramName,\n                  file,\n                  fileName\n                );\n              }\n            });\n          }\n        }\n        options.data = formData;\n      }\n      // Blob reference is not needed anymore, free memory:\n      options.blob = null;\n    },\n\n    _initIframeSettings: function (options) {\n      var targetHost = $('<a></a>').prop('href', options.url).prop('host');\n      // Setting the dataType to iframe enables the iframe transport:\n      options.dataType = 'iframe ' + (options.dataType || '');\n      // The iframe transport accepts a serialized array as form data:\n      options.formData = this._getFormData(options);\n      // Add redirect url to form data on cross-domain uploads:\n      if (options.redirect && targetHost && targetHost !== location.host) {\n        options.formData.push({\n          name: options.redirectParamName || 'redirect',\n          value: options.redirect\n        });\n      }\n    },\n\n    _initDataSettings: function (options) {\n      if (this._isXHRUpload(options)) {\n        if (!this._chunkedUpload(options, true)) {\n          if (!options.data) {\n            this._initXHRData(options);\n          }\n          this._initProgressListener(options);\n        }\n        if (options.postMessage) {\n          // Setting the dataType to postmessage enables the\n          // postMessage transport:\n          options.dataType = 'postmessage ' + (options.dataType || '');\n        }\n      } else {\n        this._initIframeSettings(options);\n      }\n    },\n\n    _getParamName: function (options) {\n      var fileInput = $(options.fileInput),\n        paramName = options.paramName;\n      if (!paramName) {\n        paramName = [];\n        fileInput.each(function () {\n          var input = $(this),\n            name = input.prop('name') || 'files[]',\n            i = (input.prop('files') || [1]).length;\n          while (i) {\n            paramName.push(name);\n            i -= 1;\n          }\n        });\n        if (!paramName.length) {\n          paramName = [fileInput.prop('name') || 'files[]'];\n        }\n      } else if (!$.isArray(paramName)) {\n        paramName = [paramName];\n      }\n      return paramName;\n    },\n\n    _initFormSettings: function (options) {\n      // Retrieve missing options from the input field and the\n      // associated form, if available:\n      if (!options.form || !options.form.length) {\n        options.form = $(options.fileInput.prop('form'));\n        // If the given file input doesn't have an associated form,\n        // use the default widget file input's form:\n        if (!options.form.length) {\n          options.form = $(this.options.fileInput.prop('form'));\n        }\n      }\n      options.paramName = this._getParamName(options);\n      if (!options.url) {\n        options.url = options.form.prop('action') || location.href;\n      }\n      // The HTTP request method must be \"POST\" or \"PUT\":\n      options.type = (\n        options.type ||\n        ($.type(options.form.prop('method')) === 'string' &&\n          options.form.prop('method')) ||\n        ''\n      ).toUpperCase();\n      if (\n        options.type !== 'POST' &&\n        options.type !== 'PUT' &&\n        options.type !== 'PATCH'\n      ) {\n        options.type = 'POST';\n      }\n      if (!options.formAcceptCharset) {\n        options.formAcceptCharset = options.form.attr('accept-charset');\n      }\n    },\n\n    _getAJAXSettings: function (data) {\n      var options = $.extend({}, this.options, data);\n      this._initFormSettings(options);\n      this._initDataSettings(options);\n      return options;\n    },\n\n    // jQuery 1.6 doesn't provide .state(),\n    // while jQuery 1.8+ removed .isRejected() and .isResolved():\n    _getDeferredState: function (deferred) {\n      if (deferred.state) {\n        return deferred.state();\n      }\n      if (deferred.isResolved()) {\n        return 'resolved';\n      }\n      if (deferred.isRejected()) {\n        return 'rejected';\n      }\n      return 'pending';\n    },\n\n    // Maps jqXHR callbacks to the equivalent\n    // methods of the given Promise object:\n    _enhancePromise: function (promise) {\n      promise.success = promise.done;\n      promise.error = promise.fail;\n      promise.complete = promise.always;\n      return promise;\n    },\n\n    // Creates and returns a Promise object enhanced with\n    // the jqXHR methods abort, success, error and complete:\n    _getXHRPromise: function (resolveOrReject, context, args) {\n      var dfd = $.Deferred(),\n        promise = dfd.promise();\n      // eslint-disable-next-line no-param-reassign\n      context = context || this.options.context || promise;\n      if (resolveOrReject === true) {\n        dfd.resolveWith(context, args);\n      } else if (resolveOrReject === false) {\n        dfd.rejectWith(context, args);\n      }\n      promise.abort = dfd.promise;\n      return this._enhancePromise(promise);\n    },\n\n    // Adds convenience methods to the data callback argument:\n    _addConvenienceMethods: function (e, data) {\n      var that = this,\n        getPromise = function (args) {\n          return $.Deferred().resolveWith(that, args).promise();\n        };\n      data.process = function (resolveFunc, rejectFunc) {\n        if (resolveFunc || rejectFunc) {\n          data._processQueue = this._processQueue = (this._processQueue ||\n            getPromise([this]))\n            [that._promisePipe](function () {\n              if (data.errorThrown) {\n                return $.Deferred().rejectWith(that, [data]).promise();\n              }\n              return getPromise(arguments);\n            })\n            [that._promisePipe](resolveFunc, rejectFunc);\n        }\n        return this._processQueue || getPromise([this]);\n      };\n      data.submit = function () {\n        if (this.state() !== 'pending') {\n          data.jqXHR = this.jqXHR =\n            that._trigger(\n              'submit',\n              $.Event('submit', { delegatedEvent: e }),\n              this\n            ) !== false && that._onSend(e, this);\n        }\n        return this.jqXHR || that._getXHRPromise();\n      };\n      data.abort = function () {\n        if (this.jqXHR) {\n          return this.jqXHR.abort();\n        }\n        this.errorThrown = 'abort';\n        that._trigger('fail', null, this);\n        return that._getXHRPromise(false);\n      };\n      data.state = function () {\n        if (this.jqXHR) {\n          return that._getDeferredState(this.jqXHR);\n        }\n        if (this._processQueue) {\n          return that._getDeferredState(this._processQueue);\n        }\n      };\n      data.processing = function () {\n        return (\n          !this.jqXHR &&\n          this._processQueue &&\n          that._getDeferredState(this._processQueue) === 'pending'\n        );\n      };\n      data.progress = function () {\n        return this._progress;\n      };\n      data.response = function () {\n        return this._response;\n      };\n    },\n\n    // Parses the Range header from the server response\n    // and returns the uploaded bytes:\n    _getUploadedBytes: function (jqXHR) {\n      var range = jqXHR.getResponseHeader('Range'),\n        parts = range && range.split('-'),\n        upperBytesPos = parts && parts.length > 1 && parseInt(parts[1], 10);\n      return upperBytesPos && upperBytesPos + 1;\n    },\n\n    // Uploads a file in multiple, sequential requests\n    // by splitting the file up in multiple blob chunks.\n    // If the second parameter is true, only tests if the file\n    // should be uploaded in chunks, but does not invoke any\n    // upload requests:\n    _chunkedUpload: function (options, testOnly) {\n      options.uploadedBytes = options.uploadedBytes || 0;\n      var that = this,\n        file = options.files[0],\n        fs = file.size,\n        ub = options.uploadedBytes,\n        mcs = options.maxChunkSize || fs,\n        slice = this._blobSlice,\n        dfd = $.Deferred(),\n        promise = dfd.promise(),\n        jqXHR,\n        upload;\n      if (\n        !(\n          this._isXHRUpload(options) &&\n          slice &&\n          (ub || ($.type(mcs) === 'function' ? mcs(options) : mcs) < fs)\n        ) ||\n        options.data\n      ) {\n        return false;\n      }\n      if (testOnly) {\n        return true;\n      }\n      if (ub >= fs) {\n        file.error = options.i18n('uploadedBytes');\n        return this._getXHRPromise(false, options.context, [\n          null,\n          'error',\n          file.error\n        ]);\n      }\n      // The chunk upload method:\n      upload = function () {\n        // Clone the options object for each chunk upload:\n        var o = $.extend({}, options),\n          currentLoaded = o._progress.loaded;\n        o.blob = slice.call(\n          file,\n          ub,\n          ub + ($.type(mcs) === 'function' ? mcs(o) : mcs),\n          file.type\n        );\n        // Store the current chunk size, as the blob itself\n        // will be dereferenced after data processing:\n        o.chunkSize = o.blob.size;\n        // Expose the chunk bytes position range:\n        o.contentRange =\n          'bytes ' + ub + '-' + (ub + o.chunkSize - 1) + '/' + fs;\n        // Trigger chunkbeforesend to allow form data to be updated for this chunk\n        that._trigger('chunkbeforesend', null, o);\n        // Process the upload data (the blob and potential form data):\n        that._initXHRData(o);\n        // Add progress listeners for this chunk upload:\n        that._initProgressListener(o);\n        jqXHR = (\n          (that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||\n          that._getXHRPromise(false, o.context)\n        )\n          .done(function (result, textStatus, jqXHR) {\n            ub = that._getUploadedBytes(jqXHR) || ub + o.chunkSize;\n            // Create a progress event if no final progress event\n            // with loaded equaling total has been triggered\n            // for this chunk:\n            if (currentLoaded + o.chunkSize - o._progress.loaded) {\n              that._onProgress(\n                $.Event('progress', {\n                  lengthComputable: true,\n                  loaded: ub - o.uploadedBytes,\n                  total: ub - o.uploadedBytes\n                }),\n                o\n              );\n            }\n            options.uploadedBytes = o.uploadedBytes = ub;\n            o.result = result;\n            o.textStatus = textStatus;\n            o.jqXHR = jqXHR;\n            that._trigger('chunkdone', null, o);\n            that._trigger('chunkalways', null, o);\n            if (ub < fs) {\n              // File upload not yet complete,\n              // continue with the next chunk:\n              upload();\n            } else {\n              dfd.resolveWith(o.context, [result, textStatus, jqXHR]);\n            }\n          })\n          .fail(function (jqXHR, textStatus, errorThrown) {\n            o.jqXHR = jqXHR;\n            o.textStatus = textStatus;\n            o.errorThrown = errorThrown;\n            that._trigger('chunkfail', null, o);\n            that._trigger('chunkalways', null, o);\n            dfd.rejectWith(o.context, [jqXHR, textStatus, errorThrown]);\n          })\n          .always(function () {\n            that._deinitProgressListener(o);\n          });\n      };\n      this._enhancePromise(promise);\n      promise.abort = function () {\n        return jqXHR.abort();\n      };\n      upload();\n      return promise;\n    },\n\n    _beforeSend: function (e, data) {\n      if (this._active === 0) {\n        // the start callback is triggered when an upload starts\n        // and no other uploads are currently running,\n        // equivalent to the global ajaxStart event:\n        this._trigger('start');\n        // Set timer for global bitrate progress calculation:\n        this._bitrateTimer = new this._BitrateTimer();\n        // Reset the global progress values:\n        this._progress.loaded = this._progress.total = 0;\n        this._progress.bitrate = 0;\n      }\n      // Make sure the container objects for the .response() and\n      // .progress() methods on the data object are available\n      // and reset to their initial state:\n      this._initResponseObject(data);\n      this._initProgressObject(data);\n      data._progress.loaded = data.loaded = data.uploadedBytes || 0;\n      data._progress.total = data.total = this._getTotal(data.files) || 1;\n      data._progress.bitrate = data.bitrate = 0;\n      this._active += 1;\n      // Initialize the global progress values:\n      this._progress.loaded += data.loaded;\n      this._progress.total += data.total;\n    },\n\n    _onDone: function (result, textStatus, jqXHR, options) {\n      var total = options._progress.total,\n        response = options._response;\n      if (options._progress.loaded < total) {\n        // Create a progress event if no final progress event\n        // with loaded equaling total has been triggered:\n        this._onProgress(\n          $.Event('progress', {\n            lengthComputable: true,\n            loaded: total,\n            total: total\n          }),\n          options\n        );\n      }\n      response.result = options.result = result;\n      response.textStatus = options.textStatus = textStatus;\n      response.jqXHR = options.jqXHR = jqXHR;\n      this._trigger('done', null, options);\n    },\n\n    _onFail: function (jqXHR, textStatus, errorThrown, options) {\n      var response = options._response;\n      if (options.recalculateProgress) {\n        // Remove the failed (error or abort) file upload from\n        // the global progress calculation:\n        this._progress.loaded -= options._progress.loaded;\n        this._progress.total -= options._progress.total;\n      }\n      response.jqXHR = options.jqXHR = jqXHR;\n      response.textStatus = options.textStatus = textStatus;\n      response.errorThrown = options.errorThrown = errorThrown;\n      this._trigger('fail', null, options);\n    },\n\n    _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {\n      // jqXHRorResult, textStatus and jqXHRorError are added to the\n      // options object via done and fail callbacks\n      this._trigger('always', null, options);\n    },\n\n    _onSend: function (e, data) {\n      if (!data.submit) {\n        this._addConvenienceMethods(e, data);\n      }\n      var that = this,\n        jqXHR,\n        aborted,\n        slot,\n        pipe,\n        options = that._getAJAXSettings(data),\n        send = function () {\n          that._sending += 1;\n          // Set timer for bitrate progress calculation:\n          options._bitrateTimer = new that._BitrateTimer();\n          jqXHR =\n            jqXHR ||\n            (\n              ((aborted ||\n                that._trigger(\n                  'send',\n                  $.Event('send', { delegatedEvent: e }),\n                  options\n                ) === false) &&\n                that._getXHRPromise(false, options.context, aborted)) ||\n              that._chunkedUpload(options) ||\n              $.ajax(options)\n            )\n              .done(function (result, textStatus, jqXHR) {\n                that._onDone(result, textStatus, jqXHR, options);\n              })\n              .fail(function (jqXHR, textStatus, errorThrown) {\n                that._onFail(jqXHR, textStatus, errorThrown, options);\n              })\n              .always(function (jqXHRorResult, textStatus, jqXHRorError) {\n                that._deinitProgressListener(options);\n                that._onAlways(\n                  jqXHRorResult,\n                  textStatus,\n                  jqXHRorError,\n                  options\n                );\n                that._sending -= 1;\n                that._active -= 1;\n                if (\n                  options.limitConcurrentUploads &&\n                  options.limitConcurrentUploads > that._sending\n                ) {\n                  // Start the next queued upload,\n                  // that has not been aborted:\n                  var nextSlot = that._slots.shift();\n                  while (nextSlot) {\n                    if (that._getDeferredState(nextSlot) === 'pending') {\n                      nextSlot.resolve();\n                      break;\n                    }\n                    nextSlot = that._slots.shift();\n                  }\n                }\n                if (that._active === 0) {\n                  // The stop callback is triggered when all uploads have\n                  // been completed, equivalent to the global ajaxStop event:\n                  that._trigger('stop');\n                }\n              });\n          return jqXHR;\n        };\n      this._beforeSend(e, options);\n      if (\n        this.options.sequentialUploads ||\n        (this.options.limitConcurrentUploads &&\n          this.options.limitConcurrentUploads <= this._sending)\n      ) {\n        if (this.options.limitConcurrentUploads > 1) {\n          slot = $.Deferred();\n          this._slots.push(slot);\n          pipe = slot[that._promisePipe](send);\n        } else {\n          this._sequence = this._sequence[that._promisePipe](send, send);\n          pipe = this._sequence;\n        }\n        // Return the piped Promise object, enhanced with an abort method,\n        // which is delegated to the jqXHR object of the current upload,\n        // and jqXHR callbacks mapped to the equivalent Promise methods:\n        pipe.abort = function () {\n          aborted = [undefined, 'abort', 'abort'];\n          if (!jqXHR) {\n            if (slot) {\n              slot.rejectWith(options.context, aborted);\n            }\n            return send();\n          }\n          return jqXHR.abort();\n        };\n        return this._enhancePromise(pipe);\n      }\n      return send();\n    },\n\n    _onAdd: function (e, data) {\n      var that = this,\n        result = true,\n        options = $.extend({}, this.options, data),\n        files = data.files,\n        filesLength = files.length,\n        limit = options.limitMultiFileUploads,\n        limitSize = options.limitMultiFileUploadSize,\n        overhead = options.limitMultiFileUploadSizeOverhead,\n        batchSize = 0,\n        paramName = this._getParamName(options),\n        paramNameSet,\n        paramNameSlice,\n        fileSet,\n        i,\n        j = 0;\n      if (!filesLength) {\n        return false;\n      }\n      if (limitSize && files[0].size === undefined) {\n        limitSize = undefined;\n      }\n      if (\n        !(options.singleFileUploads || limit || limitSize) ||\n        !this._isXHRUpload(options)\n      ) {\n        fileSet = [files];\n        paramNameSet = [paramName];\n      } else if (!(options.singleFileUploads || limitSize) && limit) {\n        fileSet = [];\n        paramNameSet = [];\n        for (i = 0; i < filesLength; i += limit) {\n          fileSet.push(files.slice(i, i + limit));\n          paramNameSlice = paramName.slice(i, i + limit);\n          if (!paramNameSlice.length) {\n            paramNameSlice = paramName;\n          }\n          paramNameSet.push(paramNameSlice);\n        }\n      } else if (!options.singleFileUploads && limitSize) {\n        fileSet = [];\n        paramNameSet = [];\n        for (i = 0; i < filesLength; i = i + 1) {\n          batchSize += files[i].size + overhead;\n          if (\n            i + 1 === filesLength ||\n            batchSize + files[i + 1].size + overhead > limitSize ||\n            (limit && i + 1 - j >= limit)\n          ) {\n            fileSet.push(files.slice(j, i + 1));\n            paramNameSlice = paramName.slice(j, i + 1);\n            if (!paramNameSlice.length) {\n              paramNameSlice = paramName;\n            }\n            paramNameSet.push(paramNameSlice);\n            j = i + 1;\n            batchSize = 0;\n          }\n        }\n      } else {\n        paramNameSet = paramName;\n      }\n      data.originalFiles = files;\n      $.each(fileSet || files, function (index, element) {\n        var newData = $.extend({}, data);\n        newData.files = fileSet ? element : [element];\n        newData.paramName = paramNameSet[index];\n        that._initResponseObject(newData);\n        that._initProgressObject(newData);\n        that._addConvenienceMethods(e, newData);\n        result = that._trigger(\n          'add',\n          $.Event('add', { delegatedEvent: e }),\n          newData\n        );\n        return result;\n      });\n      return result;\n    },\n\n    _replaceFileInput: function (data) {\n      var input = data.fileInput,\n        inputClone = input.clone(true),\n        restoreFocus = input.is(document.activeElement);\n      // Add a reference for the new cloned file input to the data argument:\n      data.fileInputClone = inputClone;\n      $('<form></form>').append(inputClone)[0].reset();\n      // Detaching allows to insert the fileInput on another form\n      // without losing the file input value:\n      input.after(inputClone).detach();\n      // If the fileInput had focus before it was detached,\n      // restore focus to the inputClone.\n      if (restoreFocus) {\n        inputClone.trigger('focus');\n      }\n      // Avoid memory leaks with the detached file input:\n      $.cleanData(input.off('remove'));\n      // Replace the original file input element in the fileInput\n      // elements set with the clone, which has been copied including\n      // event handlers:\n      this.options.fileInput = this.options.fileInput.map(function (i, el) {\n        if (el === input[0]) {\n          return inputClone[0];\n        }\n        return el;\n      });\n      // If the widget has been initialized on the file input itself,\n      // override this.element with the file input clone:\n      if (input[0] === this.element[0]) {\n        this.element = inputClone;\n      }\n    },\n\n    _handleFileTreeEntry: function (entry, path) {\n      var that = this,\n        dfd = $.Deferred(),\n        entries = [],\n        dirReader,\n        errorHandler = function (e) {\n          if (e && !e.entry) {\n            e.entry = entry;\n          }\n          // Since $.when returns immediately if one\n          // Deferred is rejected, we use resolve instead.\n          // This allows valid files and invalid items\n          // to be returned together in one set:\n          dfd.resolve([e]);\n        },\n        successHandler = function (entries) {\n          that\n            ._handleFileTreeEntries(entries, path + entry.name + '/')\n            .done(function (files) {\n              dfd.resolve(files);\n            })\n            .fail(errorHandler);\n        },\n        readEntries = function () {\n          dirReader.readEntries(function (results) {\n            if (!results.length) {\n              successHandler(entries);\n            } else {\n              entries = entries.concat(results);\n              readEntries();\n            }\n          }, errorHandler);\n        };\n      // eslint-disable-next-line no-param-reassign\n      path = path || '';\n      if (entry.isFile) {\n        if (entry._file) {\n          // Workaround for Chrome bug #149735\n          entry._file.relativePath = path;\n          dfd.resolve(entry._file);\n        } else {\n          entry.file(function (file) {\n            file.relativePath = path;\n            dfd.resolve(file);\n          }, errorHandler);\n        }\n      } else if (entry.isDirectory) {\n        dirReader = entry.createReader();\n        readEntries();\n      } else {\n        // Return an empty list for file system items\n        // other than files or directories:\n        dfd.resolve([]);\n      }\n      return dfd.promise();\n    },\n\n    _handleFileTreeEntries: function (entries, path) {\n      var that = this;\n      return $.when\n        .apply(\n          $,\n          $.map(entries, function (entry) {\n            return that._handleFileTreeEntry(entry, path);\n          })\n        )\n        [this._promisePipe](function () {\n          return Array.prototype.concat.apply([], arguments);\n        });\n    },\n\n    _getDroppedFiles: function (dataTransfer) {\n      // eslint-disable-next-line no-param-reassign\n      dataTransfer = dataTransfer || {};\n      var items = dataTransfer.items;\n      if (\n        items &&\n        items.length &&\n        (items[0].webkitGetAsEntry || items[0].getAsEntry)\n      ) {\n        return this._handleFileTreeEntries(\n          $.map(items, function (item) {\n            var entry;\n            if (item.webkitGetAsEntry) {\n              entry = item.webkitGetAsEntry();\n              if (entry) {\n                // Workaround for Chrome bug #149735:\n                entry._file = item.getAsFile();\n              }\n              return entry;\n            }\n            return item.getAsEntry();\n          })\n        );\n      }\n      return $.Deferred().resolve($.makeArray(dataTransfer.files)).promise();\n    },\n\n    _getSingleFileInputFiles: function (fileInput) {\n      // eslint-disable-next-line no-param-reassign\n      fileInput = $(fileInput);\n      var entries = fileInput.prop('entries'),\n        files,\n        value;\n      if (entries && entries.length) {\n        return this._handleFileTreeEntries(entries);\n      }\n      files = $.makeArray(fileInput.prop('files'));\n      if (!files.length) {\n        value = fileInput.prop('value');\n        if (!value) {\n          return $.Deferred().resolve([]).promise();\n        }\n        // If the files property is not available, the browser does not\n        // support the File API and we add a pseudo File object with\n        // the input value as name with path information removed:\n        files = [{ name: value.replace(/^.*\\\\/, '') }];\n      } else if (files[0].name === undefined && files[0].fileName) {\n        // File normalization for Safari 4 and Firefox 3:\n        $.each(files, function (index, file) {\n          file.name = file.fileName;\n          file.size = file.fileSize;\n        });\n      }\n      return $.Deferred().resolve(files).promise();\n    },\n\n    _getFileInputFiles: function (fileInput) {\n      if (!(fileInput instanceof $) || fileInput.length === 1) {\n        return this._getSingleFileInputFiles(fileInput);\n      }\n      return $.when\n        .apply($, $.map(fileInput, this._getSingleFileInputFiles))\n        [this._promisePipe](function () {\n          return Array.prototype.concat.apply([], arguments);\n        });\n    },\n\n    _onChange: function (e) {\n      var that = this,\n        data = {\n          fileInput: $(e.target),\n          form: $(e.target.form)\n        };\n      this._getFileInputFiles(data.fileInput).always(function (files) {\n        data.files = files;\n        if (that.options.replaceFileInput) {\n          that._replaceFileInput(data);\n        }\n        if (\n          that._trigger(\n            'change',\n            $.Event('change', { delegatedEvent: e }),\n            data\n          ) !== false\n        ) {\n          that._onAdd(e, data);\n        }\n      });\n    },\n\n    _onPaste: function (e) {\n      var items =\n          e.originalEvent &&\n          e.originalEvent.clipboardData &&\n          e.originalEvent.clipboardData.items,\n        data = { files: [] };\n      if (items && items.length) {\n        $.each(items, function (index, item) {\n          var file = item.getAsFile && item.getAsFile();\n          if (file) {\n            data.files.push(file);\n          }\n        });\n        if (\n          this._trigger(\n            'paste',\n            $.Event('paste', { delegatedEvent: e }),\n            data\n          ) !== false\n        ) {\n          this._onAdd(e, data);\n        }\n      }\n    },\n\n    _onDrop: function (e) {\n      e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;\n      var that = this,\n        dataTransfer = e.dataTransfer,\n        data = {};\n      if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {\n        e.preventDefault();\n        this._getDroppedFiles(dataTransfer).always(function (files) {\n          data.files = files;\n          if (\n            that._trigger(\n              'drop',\n              $.Event('drop', { delegatedEvent: e }),\n              data\n            ) !== false\n          ) {\n            that._onAdd(e, data);\n          }\n        });\n      }\n    },\n\n    _onDragOver: getDragHandler('dragover'),\n\n    _onDragEnter: getDragHandler('dragenter'),\n\n    _onDragLeave: getDragHandler('dragleave'),\n\n    _initEventHandlers: function () {\n      if (this._isXHRUpload(this.options)) {\n        this._on(this.options.dropZone, {\n          dragover: this._onDragOver,\n          drop: this._onDrop,\n          // event.preventDefault() on dragenter is required for IE10+:\n          dragenter: this._onDragEnter,\n          // dragleave is not required, but added for completeness:\n          dragleave: this._onDragLeave\n        });\n        this._on(this.options.pasteZone, {\n          paste: this._onPaste\n        });\n      }\n      if ($.support.fileInput) {\n        this._on(this.options.fileInput, {\n          change: this._onChange\n        });\n      }\n    },\n\n    _destroyEventHandlers: function () {\n      this._off(this.options.dropZone, 'dragenter dragleave dragover drop');\n      this._off(this.options.pasteZone, 'paste');\n      this._off(this.options.fileInput, 'change');\n    },\n\n    _destroy: function () {\n      this._destroyEventHandlers();\n    },\n\n    _setOption: function (key, value) {\n      var reinit = $.inArray(key, this._specialOptions) !== -1;\n      if (reinit) {\n        this._destroyEventHandlers();\n      }\n      this._super(key, value);\n      if (reinit) {\n        this._initSpecialOptions();\n        this._initEventHandlers();\n      }\n    },\n\n    _initSpecialOptions: function () {\n      var options = this.options;\n      if (options.fileInput === undefined) {\n        options.fileInput = this.element.is('input[type=\"file\"]')\n          ? this.element\n          : this.element.find('input[type=\"file\"]');\n      } else if (!(options.fileInput instanceof $)) {\n        options.fileInput = $(options.fileInput);\n      }\n      if (!(options.dropZone instanceof $)) {\n        options.dropZone = $(options.dropZone);\n      }\n      if (!(options.pasteZone instanceof $)) {\n        options.pasteZone = $(options.pasteZone);\n      }\n    },\n\n    _getRegExp: function (str) {\n      var parts = str.split('/'),\n        modifiers = parts.pop();\n      parts.shift();\n      return new RegExp(parts.join('/'), modifiers);\n    },\n\n    _isRegExpOption: function (key, value) {\n      return (\n        key !== 'url' &&\n        $.type(value) === 'string' &&\n        /^\\/.*\\/[igm]{0,3}$/.test(value)\n      );\n    },\n\n    _initDataAttributes: function () {\n      var that = this,\n        options = this.options,\n        data = this.element.data();\n      // Initialize options set via HTML5 data-attributes:\n      $.each(this.element[0].attributes, function (index, attr) {\n        var key = attr.name.toLowerCase(),\n          value;\n        if (/^data-/.test(key)) {\n          // Convert hyphen-ated key to camelCase:\n          key = key.slice(5).replace(/-[a-z]/g, function (str) {\n            return str.charAt(1).toUpperCase();\n          });\n          value = data[key];\n          if (that._isRegExpOption(key, value)) {\n            value = that._getRegExp(value);\n          }\n          options[key] = value;\n        }\n      });\n    },\n\n    _create: function () {\n      this._initDataAttributes();\n      this._initSpecialOptions();\n      this._slots = [];\n      this._sequence = this._getXHRPromise(true);\n      this._sending = this._active = 0;\n      this._initProgressObject(this);\n      this._initEventHandlers();\n    },\n\n    // This method is exposed to the widget API and allows to query\n    // the number of active uploads:\n    active: function () {\n      return this._active;\n    },\n\n    // This method is exposed to the widget API and allows to query\n    // the widget upload progress.\n    // It returns an object with loaded, total and bitrate properties\n    // for the running uploads:\n    progress: function () {\n      return this._progress;\n    },\n\n    // This method is exposed to the widget API and allows adding files\n    // using the fileupload API. The data parameter accepts an object which\n    // must have a files property and can contain additional options:\n    // .fileupload('add', {files: filesList});\n    add: function (data) {\n      var that = this;\n      if (!data || this.options.disabled) {\n        return;\n      }\n      if (data.fileInput && !data.files) {\n        this._getFileInputFiles(data.fileInput).always(function (files) {\n          data.files = files;\n          that._onAdd(null, data);\n        });\n      } else {\n        data.files = $.makeArray(data.files);\n        this._onAdd(null, data);\n      }\n    },\n\n    // This method is exposed to the widget API and allows sending files\n    // using the fileupload API. The data parameter accepts an object which\n    // must have a files or fileInput property and can contain additional options:\n    // .fileupload('send', {files: filesList});\n    // The method returns a Promise object for the file upload call.\n    send: function (data) {\n      if (data && !this.options.disabled) {\n        if (data.fileInput && !data.files) {\n          var that = this,\n            dfd = $.Deferred(),\n            promise = dfd.promise(),\n            jqXHR,\n            aborted;\n          promise.abort = function () {\n            aborted = true;\n            if (jqXHR) {\n              return jqXHR.abort();\n            }\n            dfd.reject(null, 'abort', 'abort');\n            return promise;\n          };\n          this._getFileInputFiles(data.fileInput).always(function (files) {\n            if (aborted) {\n              return;\n            }\n            if (!files.length) {\n              dfd.reject();\n              return;\n            }\n            data.files = files;\n            jqXHR = that._onSend(null, data);\n            jqXHR.then(\n              function (result, textStatus, jqXHR) {\n                dfd.resolve(result, textStatus, jqXHR);\n              },\n              function (jqXHR, textStatus, errorThrown) {\n                dfd.reject(jqXHR, textStatus, errorThrown);\n              }\n            );\n          });\n          return this._enhancePromise(promise);\n        }\n        data.files = $.makeArray(data.files);\n        if (data.files.length) {\n          return this._onSend(null, data);\n        }\n      }\n      return this._getXHRPromise(false, data && data.context);\n    }\n  });\n});\n","jquery/fileUploader/jquery.fileupload-audio.js":"/*\n * jQuery File Upload Audio Preview Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2013, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, require */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery', 'jquery/fileUploader/vendor/blueimp-load-image/js/load-image', 'jquery/fileUploader/jquery.fileupload-process'], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(\n      require('jquery'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'),\n      require('jquery/fileUploader/jquery.fileupload-process')\n    );\n  } else {\n    // Browser globals:\n    factory(window.jQuery, window.loadImage);\n  }\n})(function ($, loadImage) {\n  'use strict';\n\n  // Prepend to the default processQueue:\n  $.blueimp.fileupload.prototype.options.processQueue.unshift(\n    {\n      action: 'loadAudio',\n      // Use the action as prefix for the \"@\" options:\n      prefix: true,\n      fileTypes: '@',\n      maxFileSize: '@',\n      disabled: '@disableAudioPreview'\n    },\n    {\n      action: 'setAudio',\n      name: '@audioPreviewName',\n      disabled: '@disableAudioPreview'\n    }\n  );\n\n  // The File Upload Audio Preview plugin extends the fileupload widget\n  // with audio preview functionality:\n  $.widget('blueimp.fileupload', $.blueimp.fileupload, {\n    options: {\n      // The regular expression for the types of audio files to load,\n      // matched against the file type:\n      loadAudioFileTypes: /^audio\\/.*$/\n    },\n\n    _audioElement: document.createElement('audio'),\n\n    processActions: {\n      // Loads the audio file given via data.files and data.index\n      // as audio element if the browser supports playing it.\n      // Accepts the options fileTypes (regular expression)\n      // and maxFileSize (integer) to limit the files to load:\n      loadAudio: function (data, options) {\n        if (options.disabled) {\n          return data;\n        }\n        var file = data.files[data.index],\n          url,\n          audio;\n        if (\n          this._audioElement.canPlayType &&\n          this._audioElement.canPlayType(file.type) &&\n          ($.type(options.maxFileSize) !== 'number' ||\n            file.size <= options.maxFileSize) &&\n          (!options.fileTypes || options.fileTypes.test(file.type))\n        ) {\n          url = loadImage.createObjectURL(file);\n          if (url) {\n            audio = this._audioElement.cloneNode(false);\n            audio.src = url;\n            audio.controls = true;\n            data.audio = audio;\n            return data;\n          }\n        }\n        return data;\n      },\n\n      // Sets the audio element as a property of the file object:\n      setAudio: function (data, options) {\n        if (data.audio && !options.disabled) {\n          data.files[data.index][options.name || 'preview'] = data.audio;\n        }\n        return data;\n      }\n    }\n  });\n});\n","jquery/fileUploader/jquery.fileupload-validate.js":"/*\n * jQuery File Upload Validation Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2013, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, require */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery', 'jquery/fileUploader/jquery.fileupload-process'], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(require('jquery'), require('jquery/fileUploader/jquery.fileupload-process'));\n  } else {\n    // Browser globals:\n    factory(window.jQuery);\n  }\n})(function ($) {\n  'use strict';\n\n  // Append to the default processQueue:\n  $.blueimp.fileupload.prototype.options.processQueue.push({\n    action: 'validate',\n    // Always trigger this action,\n    // even if the previous action was rejected:\n    always: true,\n    // Options taken from the global options map:\n    acceptFileTypes: '@',\n    maxFileSize: '@',\n    minFileSize: '@',\n    maxNumberOfFiles: '@',\n    disabled: '@disableValidation'\n  });\n\n  // The File Upload Validation plugin extends the fileupload widget\n  // with file validation functionality:\n  $.widget('blueimp.fileupload', $.blueimp.fileupload, {\n    options: {\n      /*\n            // The regular expression for allowed file types, matches\n            // against either file type or file name:\n            acceptFileTypes: /(\\.|\\/)(gif|jpe?g|png)$/i,\n            // The maximum allowed file size in bytes:\n            maxFileSize: 10000000, // 10 MB\n            // The minimum allowed file size in bytes:\n            minFileSize: undefined, // No minimal file size\n            // The limit of files to be uploaded:\n            maxNumberOfFiles: 10,\n            */\n\n      // Function returning the current number of files,\n      // has to be overridden for maxNumberOfFiles validation:\n      getNumberOfFiles: $.noop,\n\n      // Error and info messages:\n      messages: {\n        maxNumberOfFiles: 'Maximum number of files exceeded',\n        acceptFileTypes: 'File type not allowed',\n        maxFileSize: 'File is too large',\n        minFileSize: 'File is too small'\n      }\n    },\n\n    processActions: {\n      validate: function (data, options) {\n        if (options.disabled) {\n          return data;\n        }\n        // eslint-disable-next-line new-cap\n        var dfd = $.Deferred(),\n          settings = this.options,\n          file = data.files[data.index],\n          fileSize;\n        if (options.minFileSize || options.maxFileSize) {\n          fileSize = file.size;\n        }\n        if (\n          $.type(options.maxNumberOfFiles) === 'number' &&\n          (settings.getNumberOfFiles() || 0) + data.files.length >\n            options.maxNumberOfFiles\n        ) {\n          file.error = settings.i18n('maxNumberOfFiles');\n        } else if (\n          options.acceptFileTypes &&\n          !(\n            options.acceptFileTypes.test(file.type) ||\n            options.acceptFileTypes.test(file.name)\n          )\n        ) {\n          file.error = settings.i18n('acceptFileTypes');\n        } else if (fileSize > options.maxFileSize) {\n          file.error = settings.i18n('maxFileSize');\n        } else if (\n          $.type(fileSize) === 'number' &&\n          fileSize < options.minFileSize\n        ) {\n          file.error = settings.i18n('minFileSize');\n        } else {\n          delete file.error;\n        }\n        if (file.error || data.files.error) {\n          data.files.error = true;\n          dfd.rejectWith(this, [data]);\n        } else {\n          dfd.resolveWith(this, [data]);\n        }\n        return dfd.promise();\n      }\n    }\n  });\n});\n","jquery/fileUploader/jquery.fileupload-video.js":"/*\n * jQuery File Upload Video Preview Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2013, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, require */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery', 'jquery/fileUploader/vendor/blueimp-load-image/js/load-image', 'jquery/fileUploader/jquery.fileupload-process'], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(\n      require('jquery'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'),\n      require('jquery/fileUploader/jquery.fileupload-process')\n    );\n  } else {\n    // Browser globals:\n    factory(window.jQuery, window.loadImage);\n  }\n})(function ($, loadImage) {\n  'use strict';\n\n  // Prepend to the default processQueue:\n  $.blueimp.fileupload.prototype.options.processQueue.unshift(\n    {\n      action: 'loadVideo',\n      // Use the action as prefix for the \"@\" options:\n      prefix: true,\n      fileTypes: '@',\n      maxFileSize: '@',\n      disabled: '@disableVideoPreview'\n    },\n    {\n      action: 'setVideo',\n      name: '@videoPreviewName',\n      disabled: '@disableVideoPreview'\n    }\n  );\n\n  // The File Upload Video Preview plugin extends the fileupload widget\n  // with video preview functionality:\n  $.widget('blueimp.fileupload', $.blueimp.fileupload, {\n    options: {\n      // The regular expression for the types of video files to load,\n      // matched against the file type:\n      loadVideoFileTypes: /^video\\/.*$/\n    },\n\n    _videoElement: document.createElement('video'),\n\n    processActions: {\n      // Loads the video file given via data.files and data.index\n      // as video element if the browser supports playing it.\n      // Accepts the options fileTypes (regular expression)\n      // and maxFileSize (integer) to limit the files to load:\n      loadVideo: function (data, options) {\n        if (options.disabled) {\n          return data;\n        }\n        var file = data.files[data.index],\n          url,\n          video;\n        if (\n          this._videoElement.canPlayType &&\n          this._videoElement.canPlayType(file.type) &&\n          ($.type(options.maxFileSize) !== 'number' ||\n            file.size <= options.maxFileSize) &&\n          (!options.fileTypes || options.fileTypes.test(file.type))\n        ) {\n          url = loadImage.createObjectURL(file);\n          if (url) {\n            video = this._videoElement.cloneNode(false);\n            video.src = url;\n            video.controls = true;\n            data.video = video;\n            return data;\n          }\n        }\n        return data;\n      },\n\n      // Sets the video element as a property of the file object:\n      setVideo: function (data, options) {\n        if (data.video && !options.disabled) {\n          data.files[data.index][options.name || 'preview'] = data.video;\n        }\n        return data;\n      }\n    }\n  });\n});\n","jquery/fileUploader/jquery.fileupload-image.js":"/*\n * jQuery File Upload Image Preview & Resize Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2013, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, require */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define([\n      'jquery',\n      'jquery/fileUploader/vendor/blueimp-load-image/js/load-image',\n      'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta',\n      'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-scale',\n      'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-exif',\n      'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-orientation',\n      'jquery/fileUploader/vendor/blueimp-canvas-to-blob/js/canvas-to-blob',\n      'jquery/fileUploader/jquery.fileupload-process'\n    ], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(\n      require('jquery'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-scale'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-exif'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-orientation'),\n      require('jquery/fileUploader/vendor/blueimp-canvas-to-blob/js/canvas-to-blob'),\n      require('jquery/fileUploader/jquery.fileupload-process')\n    );\n  } else {\n    // Browser globals:\n    factory(window.jQuery, window.loadImage);\n  }\n})(function ($, loadImage) {\n  'use strict';\n\n  // Prepend to the default processQueue:\n  $.blueimp.fileupload.prototype.options.processQueue.unshift(\n    {\n      action: 'loadImageMetaData',\n      maxMetaDataSize: '@',\n      disableImageHead: '@',\n      disableMetaDataParsers: '@',\n      disableExif: '@',\n      disableExifOffsets: '@',\n      includeExifTags: '@',\n      excludeExifTags: '@',\n      disableIptc: '@',\n      disableIptcOffsets: '@',\n      includeIptcTags: '@',\n      excludeIptcTags: '@',\n      disabled: '@disableImageMetaDataLoad'\n    },\n    {\n      action: 'loadImage',\n      // Use the action as prefix for the \"@\" options:\n      prefix: true,\n      fileTypes: '@',\n      maxFileSize: '@',\n      noRevoke: '@',\n      disabled: '@disableImageLoad'\n    },\n    {\n      action: 'resizeImage',\n      // Use \"image\" as prefix for the \"@\" options:\n      prefix: 'image',\n      maxWidth: '@',\n      maxHeight: '@',\n      minWidth: '@',\n      minHeight: '@',\n      crop: '@',\n      orientation: '@',\n      forceResize: '@',\n      disabled: '@disableImageResize'\n    },\n    {\n      action: 'saveImage',\n      quality: '@imageQuality',\n      type: '@imageType',\n      disabled: '@disableImageResize'\n    },\n    {\n      action: 'saveImageMetaData',\n      disabled: '@disableImageMetaDataSave'\n    },\n    {\n      action: 'resizeImage',\n      // Use \"preview\" as prefix for the \"@\" options:\n      prefix: 'preview',\n      maxWidth: '@',\n      maxHeight: '@',\n      minWidth: '@',\n      minHeight: '@',\n      crop: '@',\n      orientation: '@',\n      thumbnail: '@',\n      canvas: '@',\n      disabled: '@disableImagePreview'\n    },\n    {\n      action: 'setImage',\n      name: '@imagePreviewName',\n      disabled: '@disableImagePreview'\n    },\n    {\n      action: 'deleteImageReferences',\n      disabled: '@disableImageReferencesDeletion'\n    }\n  );\n\n  // The File Upload Resize plugin extends the fileupload widget\n  // with image resize functionality:\n  $.widget('blueimp.fileupload', $.blueimp.fileupload, {\n    options: {\n      // The regular expression for the types of images to load:\n      // matched against the file type:\n      loadImageFileTypes: /^image\\/(gif|jpeg|png|svg\\+xml)$/,\n      // The maximum file size of images to load:\n      loadImageMaxFileSize: 10000000, // 10MB\n      // The maximum width of resized images:\n      imageMaxWidth: 1920,\n      // The maximum height of resized images:\n      imageMaxHeight: 1080,\n      // Defines the image orientation (1-8) or takes the orientation\n      // value from Exif data if set to true:\n      imageOrientation: true,\n      // Define if resized images should be cropped or only scaled:\n      imageCrop: false,\n      // Disable the resize image functionality by default:\n      disableImageResize: true,\n      // The maximum width of the preview images:\n      previewMaxWidth: 80,\n      // The maximum height of the preview images:\n      previewMaxHeight: 80,\n      // Defines the preview orientation (1-8) or takes the orientation\n      // value from Exif data if set to true:\n      previewOrientation: true,\n      // Create the preview using the Exif data thumbnail:\n      previewThumbnail: true,\n      // Define if preview images should be cropped or only scaled:\n      previewCrop: false,\n      // Define if preview images should be resized as canvas elements:\n      previewCanvas: true\n    },\n\n    processActions: {\n      // Loads the image given via data.files and data.index\n      // as img element, if the browser supports the File API.\n      // Accepts the options fileTypes (regular expression)\n      // and maxFileSize (integer) to limit the files to load:\n      loadImage: function (data, options) {\n        if (options.disabled) {\n          return data;\n        }\n        var that = this,\n          file = data.files[data.index],\n          // eslint-disable-next-line new-cap\n          dfd = $.Deferred();\n        if (\n          ($.type(options.maxFileSize) === 'number' &&\n            file.size > options.maxFileSize) ||\n          (options.fileTypes && !options.fileTypes.test(file.type)) ||\n          !loadImage(\n            file,\n            function (img) {\n              if (img.src) {\n                data.img = img;\n              }\n              dfd.resolveWith(that, [data]);\n            },\n            options\n          )\n        ) {\n          return data;\n        }\n        return dfd.promise();\n      },\n\n      // Resizes the image given as data.canvas or data.img\n      // and updates data.canvas or data.img with the resized image.\n      // Also stores the resized image as preview property.\n      // Accepts the options maxWidth, maxHeight, minWidth,\n      // minHeight, canvas and crop:\n      resizeImage: function (data, options) {\n        if (options.disabled || !(data.canvas || data.img)) {\n          return data;\n        }\n        // eslint-disable-next-line no-param-reassign\n        options = $.extend({ canvas: true }, options);\n        var that = this,\n          // eslint-disable-next-line new-cap\n          dfd = $.Deferred(),\n          img = (options.canvas && data.canvas) || data.img,\n          resolve = function (newImg) {\n            if (\n              newImg &&\n              (newImg.width !== img.width ||\n                newImg.height !== img.height ||\n                options.forceResize)\n            ) {\n              data[newImg.getContext ? 'canvas' : 'img'] = newImg;\n            }\n            data.preview = newImg;\n            dfd.resolveWith(that, [data]);\n          },\n          thumbnail,\n          thumbnailBlob;\n        if (data.exif && options.thumbnail) {\n          thumbnail = data.exif.get('Thumbnail');\n          thumbnailBlob = thumbnail && thumbnail.get('Blob');\n          if (thumbnailBlob) {\n            options.orientation = data.exif.get('Orientation');\n            loadImage(thumbnailBlob, resolve, options);\n            return dfd.promise();\n          }\n        }\n        if (data.orientation) {\n          // Prevent orienting the same image twice:\n          delete options.orientation;\n        } else {\n          data.orientation = options.orientation || loadImage.orientation;\n        }\n        if (img) {\n          resolve(loadImage.scale(img, options, data));\n          return dfd.promise();\n        }\n        return data;\n      },\n\n      // Saves the processed image given as data.canvas\n      // inplace at data.index of data.files:\n      saveImage: function (data, options) {\n        if (!data.canvas || options.disabled) {\n          return data;\n        }\n        var that = this,\n          file = data.files[data.index],\n          // eslint-disable-next-line new-cap\n          dfd = $.Deferred();\n        if (data.canvas.toBlob) {\n          data.canvas.toBlob(\n            function (blob) {\n              if (!blob.name) {\n                if (file.type === blob.type) {\n                  blob.name = file.name;\n                } else if (file.name) {\n                  blob.name = file.name.replace(\n                    /\\.\\w+$/,\n                    '.' + blob.type.substr(6)\n                  );\n                }\n              }\n              // Don't restore invalid meta data:\n              if (file.type !== blob.type) {\n                delete data.imageHead;\n              }\n              // Store the created blob at the position\n              // of the original file in the files list:\n              data.files[data.index] = blob;\n              dfd.resolveWith(that, [data]);\n            },\n            options.type || file.type,\n            options.quality\n          );\n        } else {\n          return data;\n        }\n        return dfd.promise();\n      },\n\n      loadImageMetaData: function (data, options) {\n        if (options.disabled) {\n          return data;\n        }\n        var that = this,\n          // eslint-disable-next-line new-cap\n          dfd = $.Deferred();\n        loadImage.parseMetaData(\n          data.files[data.index],\n          function (result) {\n            $.extend(data, result);\n            dfd.resolveWith(that, [data]);\n          },\n          options\n        );\n        return dfd.promise();\n      },\n\n      saveImageMetaData: function (data, options) {\n        if (\n          !(\n            data.imageHead &&\n            data.canvas &&\n            data.canvas.toBlob &&\n            !options.disabled\n          )\n        ) {\n          return data;\n        }\n        var that = this,\n          file = data.files[data.index],\n          // eslint-disable-next-line new-cap\n          dfd = $.Deferred();\n        if (data.orientation === true && data.exifOffsets) {\n          // Reset Exif Orientation data:\n          loadImage.writeExifData(data.imageHead, data, 'Orientation', 1);\n        }\n        loadImage.replaceHead(file, data.imageHead, function (blob) {\n          blob.name = file.name;\n          data.files[data.index] = blob;\n          dfd.resolveWith(that, [data]);\n        });\n        return dfd.promise();\n      },\n\n      // Sets the resized version of the image as a property of the\n      // file object, must be called after \"saveImage\":\n      setImage: function (data, options) {\n        if (data.preview && !options.disabled) {\n          data.files[data.index][options.name || 'preview'] = data.preview;\n        }\n        return data;\n      },\n\n      deleteImageReferences: function (data, options) {\n        if (!options.disabled) {\n          delete data.img;\n          delete data.canvas;\n          delete data.preview;\n          delete data.imageHead;\n        }\n        return data;\n      }\n    }\n  });\n});\n","jquery/fileUploader/jquery.fileupload-ui.js":"/*\n * jQuery File Upload User Interface Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, require */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define([\n      'jquery',\n      'jquery/fileUploader/vendor/blueimp-tmpl/js/tmpl',\n      'jquery/fileUploader/jquery.fileupload-image',\n      'jquery/fileUploader/jquery.fileupload-audio',\n      'jquery/fileUploader/jquery.fileupload-video',\n      'jquery/fileUploader/jquery.fileupload-validate'\n    ], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(\n      require('jquery'),\n      require('jquery/fileUploader/vendor/blueimp-tmpl/js/tmpl'),\n      require('jquery/fileUploader/jquery.fileupload-image'),\n      require('jquery/fileUploader/jquery.fileupload-audio'),\n      require('jquery/fileUploader/jquery.fileupload-video'),\n      require('jquery/fileUploader/jquery.fileupload-validate')\n    );\n  } else {\n    // Browser globals:\n    factory(window.jQuery, window.tmpl);\n  }\n})(function ($, tmpl) {\n  'use strict';\n\n  $.blueimp.fileupload.prototype._specialOptions.push(\n    'filesContainer',\n    'uploadTemplateId',\n    'downloadTemplateId'\n  );\n\n  // The UI version extends the file upload widget\n  // and adds complete user interface interaction:\n  $.widget('blueimp.fileupload', $.blueimp.fileupload, {\n    options: {\n      // By default, files added to the widget are uploaded as soon\n      // as the user clicks on the start buttons. To enable automatic\n      // uploads, set the following option to true:\n      autoUpload: false,\n      // The class to show/hide UI elements:\n      showElementClass: 'in',\n      // The ID of the upload template:\n      uploadTemplateId: 'template-upload',\n      // The ID of the download template:\n      downloadTemplateId: 'template-download',\n      // The container for the list of files. If undefined, it is set to\n      // an element with class \"files\" inside of the widget element:\n      filesContainer: undefined,\n      // By default, files are appended to the files container.\n      // Set the following option to true, to prepend files instead:\n      prependFiles: false,\n      // The expected data type of the upload response, sets the dataType\n      // option of the $.ajax upload requests:\n      dataType: 'json',\n\n      // Error and info messages:\n      messages: {\n        unknownError: 'Unknown error'\n      },\n\n      // Function returning the current number of files,\n      // used by the maxNumberOfFiles validation:\n      getNumberOfFiles: function () {\n        return this.filesContainer.children().not('.processing').length;\n      },\n\n      // Callback to retrieve the list of files from the server response:\n      getFilesFromResponse: function (data) {\n        if (data.result && $.isArray(data.result.files)) {\n          return data.result.files;\n        }\n        return [];\n      },\n\n      // The add callback is invoked as soon as files are added to the fileupload\n      // widget (via file input selection, drag & drop or add API call).\n      // See the basic file upload widget for more information:\n      add: function (e, data) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var $this = $(this),\n          that = $this.data('blueimp-fileupload') || $this.data('fileupload'),\n          options = that.options;\n        data.context = that\n          ._renderUpload(data.files)\n          .data('data', data)\n          .addClass('processing');\n        options.filesContainer[options.prependFiles ? 'prepend' : 'append'](\n          data.context\n        );\n        that._forceReflow(data.context);\n        that._transition(data.context);\n        data\n          .process(function () {\n            return $this.fileupload('process', data);\n          })\n          .always(function () {\n            data.context\n              .each(function (index) {\n                $(this)\n                  .find('.size')\n                  .text(that._formatFileSize(data.files[index].size));\n              })\n              .removeClass('processing');\n            that._renderPreviews(data);\n          })\n          .done(function () {\n            data.context.find('.edit,.start').prop('disabled', false);\n            if (\n              that._trigger('added', e, data) !== false &&\n              (options.autoUpload || data.autoUpload) &&\n              data.autoUpload !== false\n            ) {\n              data.submit();\n            }\n          })\n          .fail(function () {\n            if (data.files.error) {\n              data.context.each(function (index) {\n                var error = data.files[index].error;\n                if (error) {\n                  $(this).find('.error').text(error);\n                }\n              });\n            }\n          });\n      },\n      // Callback for the start of each file upload request:\n      send: function (e, data) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var that =\n          $(this).data('blueimp-fileupload') || $(this).data('fileupload');\n        if (\n          data.context &&\n          data.dataType &&\n          data.dataType.substr(0, 6) === 'iframe'\n        ) {\n          // Iframe Transport does not support progress events.\n          // In lack of an indeterminate progress bar, we set\n          // the progress to 100%, showing the full animated bar:\n          data.context\n            .find('.progress')\n            .addClass(!$.support.transition && 'progress-animated')\n            .attr('aria-valuenow', 100)\n            .children()\n            .first()\n            .css('width', '100%');\n        }\n        return that._trigger('sent', e, data);\n      },\n      // Callback for successful uploads:\n      done: function (e, data) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var that =\n            $(this).data('blueimp-fileupload') || $(this).data('fileupload'),\n          getFilesFromResponse =\n            data.getFilesFromResponse || that.options.getFilesFromResponse,\n          files = getFilesFromResponse(data),\n          template,\n          deferred;\n        if (data.context) {\n          data.context.each(function (index) {\n            var file = files[index] || { error: 'Empty file upload result' };\n            deferred = that._addFinishedDeferreds();\n            that._transition($(this)).done(function () {\n              var node = $(this);\n              template = that._renderDownload([file]).replaceAll(node);\n              that._forceReflow(template);\n              that._transition(template).done(function () {\n                data.context = $(this);\n                that._trigger('completed', e, data);\n                that._trigger('finished', e, data);\n                deferred.resolve();\n              });\n            });\n          });\n        } else {\n          template = that\n            ._renderDownload(files)\n            [that.options.prependFiles ? 'prependTo' : 'appendTo'](\n              that.options.filesContainer\n            );\n          that._forceReflow(template);\n          deferred = that._addFinishedDeferreds();\n          that._transition(template).done(function () {\n            data.context = $(this);\n            that._trigger('completed', e, data);\n            that._trigger('finished', e, data);\n            deferred.resolve();\n          });\n        }\n      },\n      // Callback for failed (abort or error) uploads:\n      fail: function (e, data) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var that =\n            $(this).data('blueimp-fileupload') || $(this).data('fileupload'),\n          template,\n          deferred;\n        if (data.context) {\n          data.context.each(function (index) {\n            if (data.errorThrown !== 'abort') {\n              var file = data.files[index];\n              file.error =\n                file.error || data.errorThrown || data.i18n('unknownError');\n              deferred = that._addFinishedDeferreds();\n              that._transition($(this)).done(function () {\n                var node = $(this);\n                template = that._renderDownload([file]).replaceAll(node);\n                that._forceReflow(template);\n                that._transition(template).done(function () {\n                  data.context = $(this);\n                  that._trigger('failed', e, data);\n                  that._trigger('finished', e, data);\n                  deferred.resolve();\n                });\n              });\n            } else {\n              deferred = that._addFinishedDeferreds();\n              that._transition($(this)).done(function () {\n                $(this).remove();\n                that._trigger('failed', e, data);\n                that._trigger('finished', e, data);\n                deferred.resolve();\n              });\n            }\n          });\n        } else if (data.errorThrown !== 'abort') {\n          data.context = that\n            ._renderUpload(data.files)\n            [that.options.prependFiles ? 'prependTo' : 'appendTo'](\n              that.options.filesContainer\n            )\n            .data('data', data);\n          that._forceReflow(data.context);\n          deferred = that._addFinishedDeferreds();\n          that._transition(data.context).done(function () {\n            data.context = $(this);\n            that._trigger('failed', e, data);\n            that._trigger('finished', e, data);\n            deferred.resolve();\n          });\n        } else {\n          that._trigger('failed', e, data);\n          that._trigger('finished', e, data);\n          that._addFinishedDeferreds().resolve();\n        }\n      },\n      // Callback for upload progress events:\n      progress: function (e, data) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var progress = Math.floor((data.loaded / data.total) * 100);\n        if (data.context) {\n          data.context.each(function () {\n            $(this)\n              .find('.progress')\n              .attr('aria-valuenow', progress)\n              .children()\n              .first()\n              .css('width', progress + '%');\n          });\n        }\n      },\n      // Callback for global upload progress events:\n      progressall: function (e, data) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var $this = $(this),\n          progress = Math.floor((data.loaded / data.total) * 100),\n          globalProgressNode = $this.find('.fileupload-progress'),\n          extendedProgressNode = globalProgressNode.find('.progress-extended');\n        if (extendedProgressNode.length) {\n          extendedProgressNode.html(\n            (\n              $this.data('blueimp-fileupload') || $this.data('fileupload')\n            )._renderExtendedProgress(data)\n          );\n        }\n        globalProgressNode\n          .find('.progress')\n          .attr('aria-valuenow', progress)\n          .children()\n          .first()\n          .css('width', progress + '%');\n      },\n      // Callback for uploads start, equivalent to the global ajaxStart event:\n      start: function (e) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var that =\n          $(this).data('blueimp-fileupload') || $(this).data('fileupload');\n        that._resetFinishedDeferreds();\n        that\n          ._transition($(this).find('.fileupload-progress'))\n          .done(function () {\n            that._trigger('started', e);\n          });\n      },\n      // Callback for uploads stop, equivalent to the global ajaxStop event:\n      stop: function (e) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var that =\n            $(this).data('blueimp-fileupload') || $(this).data('fileupload'),\n          deferred = that._addFinishedDeferreds();\n        $.when.apply($, that._getFinishedDeferreds()).done(function () {\n          that._trigger('stopped', e);\n        });\n        that\n          ._transition($(this).find('.fileupload-progress'))\n          .done(function () {\n            $(this)\n              .find('.progress')\n              .attr('aria-valuenow', '0')\n              .children()\n              .first()\n              .css('width', '0%');\n            $(this).find('.progress-extended').html('&nbsp;');\n            deferred.resolve();\n          });\n      },\n      processstart: function (e) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        $(this).addClass('fileupload-processing');\n      },\n      processstop: function (e) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        $(this).removeClass('fileupload-processing');\n      },\n      // Callback for file deletion:\n      destroy: function (e, data) {\n        if (e.isDefaultPrevented()) {\n          return false;\n        }\n        var that =\n            $(this).data('blueimp-fileupload') || $(this).data('fileupload'),\n          removeNode = function () {\n            that._transition(data.context).done(function () {\n              $(this).remove();\n              that._trigger('destroyed', e, data);\n            });\n          };\n        if (data.url) {\n          data.dataType = data.dataType || that.options.dataType;\n          $.ajax(data)\n            .done(removeNode)\n            .fail(function () {\n              that._trigger('destroyfailed', e, data);\n            });\n        } else {\n          removeNode();\n        }\n      }\n    },\n\n    _resetFinishedDeferreds: function () {\n      this._finishedUploads = [];\n    },\n\n    _addFinishedDeferreds: function (deferred) {\n      // eslint-disable-next-line new-cap\n      var promise = deferred || $.Deferred();\n      this._finishedUploads.push(promise);\n      return promise;\n    },\n\n    _getFinishedDeferreds: function () {\n      return this._finishedUploads;\n    },\n\n    // Link handler, that allows to download files\n    // by drag & drop of the links to the desktop:\n    _enableDragToDesktop: function () {\n      var link = $(this),\n        url = link.prop('href'),\n        name = link.prop('download'),\n        type = 'application/octet-stream';\n      link.on('dragstart', function (e) {\n        try {\n          e.originalEvent.dataTransfer.setData(\n            'DownloadURL',\n            [type, name, url].join(':')\n          );\n        } catch (ignore) {\n          // Ignore exceptions\n        }\n      });\n    },\n\n    _formatFileSize: function (bytes) {\n      if (typeof bytes !== 'number') {\n        return '';\n      }\n      if (bytes >= 1000000000) {\n        return (bytes / 1000000000).toFixed(2) + ' GB';\n      }\n      if (bytes >= 1000000) {\n        return (bytes / 1000000).toFixed(2) + ' MB';\n      }\n      return (bytes / 1000).toFixed(2) + ' KB';\n    },\n\n    _formatBitrate: function (bits) {\n      if (typeof bits !== 'number') {\n        return '';\n      }\n      if (bits >= 1000000000) {\n        return (bits / 1000000000).toFixed(2) + ' Gbit/s';\n      }\n      if (bits >= 1000000) {\n        return (bits / 1000000).toFixed(2) + ' Mbit/s';\n      }\n      if (bits >= 1000) {\n        return (bits / 1000).toFixed(2) + ' kbit/s';\n      }\n      return bits.toFixed(2) + ' bit/s';\n    },\n\n    _formatTime: function (seconds) {\n      var date = new Date(seconds * 1000),\n        days = Math.floor(seconds / 86400);\n      days = days ? days + 'd ' : '';\n      return (\n        days +\n        ('0' + date.getUTCHours()).slice(-2) +\n        ':' +\n        ('0' + date.getUTCMinutes()).slice(-2) +\n        ':' +\n        ('0' + date.getUTCSeconds()).slice(-2)\n      );\n    },\n\n    _formatPercentage: function (floatValue) {\n      return (floatValue * 100).toFixed(2) + ' %';\n    },\n\n    _renderExtendedProgress: function (data) {\n      return (\n        this._formatBitrate(data.bitrate) +\n        ' | ' +\n        this._formatTime(((data.total - data.loaded) * 8) / data.bitrate) +\n        ' | ' +\n        this._formatPercentage(data.loaded / data.total) +\n        ' | ' +\n        this._formatFileSize(data.loaded) +\n        ' / ' +\n        this._formatFileSize(data.total)\n      );\n    },\n\n    _renderTemplate: function (func, files) {\n      if (!func) {\n        return $();\n      }\n      var result = func({\n        files: files,\n        formatFileSize: this._formatFileSize,\n        options: this.options\n      });\n      if (result instanceof $) {\n        return result;\n      }\n      return $(this.options.templatesContainer).html(result).children();\n    },\n\n    _renderPreviews: function (data) {\n      data.context.find('.preview').each(function (index, elm) {\n        $(elm).empty().append(data.files[index].preview);\n      });\n    },\n\n    _renderUpload: function (files) {\n      return this._renderTemplate(this.options.uploadTemplate, files);\n    },\n\n    _renderDownload: function (files) {\n      return this._renderTemplate(this.options.downloadTemplate, files)\n        .find('a[download]')\n        .each(this._enableDragToDesktop)\n        .end();\n    },\n\n    _editHandler: function (e) {\n      e.preventDefault();\n      if (!this.options.edit) return;\n      var that = this,\n        button = $(e.currentTarget),\n        template = button.closest('.template-upload'),\n        data = template.data('data'),\n        index = button.data().index;\n      this.options.edit(data.files[index]).then(function (file) {\n        if (!file) return;\n        data.files[index] = file;\n        data.context.addClass('processing');\n        template.find('.edit,.start').prop('disabled', true);\n        $(that.element)\n          .fileupload('process', data)\n          .always(function () {\n            template\n              .find('.size')\n              .text(that._formatFileSize(data.files[index].size));\n            data.context.removeClass('processing');\n            that._renderPreviews(data);\n          })\n          .done(function () {\n            template.find('.edit,.start').prop('disabled', false);\n          })\n          .fail(function () {\n            template.find('.edit').prop('disabled', false);\n            var error = data.files[index].error;\n            if (error) {\n              template.find('.error').text(error);\n            }\n          });\n      });\n    },\n\n    _startHandler: function (e) {\n      e.preventDefault();\n      var button = $(e.currentTarget),\n        template = button.closest('.template-upload'),\n        data = template.data('data');\n      button.prop('disabled', true);\n      if (data && data.submit) {\n        data.submit();\n      }\n    },\n\n    _cancelHandler: function (e) {\n      e.preventDefault();\n      var template = $(e.currentTarget).closest(\n          '.template-upload,.template-download'\n        ),\n        data = template.data('data') || {};\n      data.context = data.context || template;\n      if (data.abort) {\n        data.abort();\n      } else {\n        data.errorThrown = 'abort';\n        this._trigger('fail', e, data);\n      }\n    },\n\n    _deleteHandler: function (e) {\n      e.preventDefault();\n      var button = $(e.currentTarget);\n      this._trigger(\n        'destroy',\n        e,\n        $.extend(\n          {\n            context: button.closest('.template-download'),\n            type: 'DELETE'\n          },\n          button.data()\n        )\n      );\n    },\n\n    _forceReflow: function (node) {\n      return $.support.transition && node.length && node[0].offsetWidth;\n    },\n\n    _transition: function (node) {\n      // eslint-disable-next-line new-cap\n      var dfd = $.Deferred();\n      if (\n        $.support.transition &&\n        node.hasClass('fade') &&\n        node.is(':visible')\n      ) {\n        var transitionEndHandler = function (e) {\n          // Make sure we don't respond to other transition events\n          // in the container element, e.g. from button elements:\n          if (e.target === node[0]) {\n            node.off($.support.transition.end, transitionEndHandler);\n            dfd.resolveWith(node);\n          }\n        };\n        node\n          .on($.support.transition.end, transitionEndHandler)\n          .toggleClass(this.options.showElementClass);\n      } else {\n        node.toggleClass(this.options.showElementClass);\n        dfd.resolveWith(node);\n      }\n      return dfd;\n    },\n\n    _initButtonBarEventHandlers: function () {\n      var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),\n        filesList = this.options.filesContainer;\n      this._on(fileUploadButtonBar.find('.start'), {\n        click: function (e) {\n          e.preventDefault();\n          filesList.find('.start').trigger('click');\n        }\n      });\n      this._on(fileUploadButtonBar.find('.cancel'), {\n        click: function (e) {\n          e.preventDefault();\n          filesList.find('.cancel').trigger('click');\n        }\n      });\n      this._on(fileUploadButtonBar.find('.delete'), {\n        click: function (e) {\n          e.preventDefault();\n          filesList\n            .find('.toggle:checked')\n            .closest('.template-download')\n            .find('.delete')\n            .trigger('click');\n          fileUploadButtonBar.find('.toggle').prop('checked', false);\n        }\n      });\n      this._on(fileUploadButtonBar.find('.toggle'), {\n        change: function (e) {\n          filesList\n            .find('.toggle')\n            .prop('checked', $(e.currentTarget).is(':checked'));\n        }\n      });\n    },\n\n    _destroyButtonBarEventHandlers: function () {\n      this._off(\n        this.element\n          .find('.fileupload-buttonbar')\n          .find('.start, .cancel, .delete'),\n        'click'\n      );\n      this._off(this.element.find('.fileupload-buttonbar .toggle'), 'change.');\n    },\n\n    _initEventHandlers: function () {\n      this._super();\n      this._on(this.options.filesContainer, {\n        'click .edit': this._editHandler,\n        'click .start': this._startHandler,\n        'click .cancel': this._cancelHandler,\n        'click .delete': this._deleteHandler\n      });\n      this._initButtonBarEventHandlers();\n    },\n\n    _destroyEventHandlers: function () {\n      this._destroyButtonBarEventHandlers();\n      this._off(this.options.filesContainer, 'click');\n      this._super();\n    },\n\n    _enableFileInputButton: function () {\n      this.element\n        .find('.fileinput-button input')\n        .prop('disabled', false)\n        .parent()\n        .removeClass('disabled');\n    },\n\n    _disableFileInputButton: function () {\n      this.element\n        .find('.fileinput-button input')\n        .prop('disabled', true)\n        .parent()\n        .addClass('disabled');\n    },\n\n    _initTemplates: function () {\n      var options = this.options;\n      options.templatesContainer = this.document[0].createElement(\n        options.filesContainer.prop('nodeName')\n      );\n      if (tmpl) {\n        if (options.uploadTemplateId) {\n          options.uploadTemplate = tmpl(options.uploadTemplateId);\n        }\n        if (options.downloadTemplateId) {\n          options.downloadTemplate = tmpl(options.downloadTemplateId);\n        }\n      }\n    },\n\n    _initFilesContainer: function () {\n      var options = this.options;\n      if (options.filesContainer === undefined) {\n        options.filesContainer = this.element.find('.files');\n      } else if (!(options.filesContainer instanceof $)) {\n        options.filesContainer = $(options.filesContainer);\n      }\n    },\n\n    _initSpecialOptions: function () {\n      this._super();\n      this._initFilesContainer();\n      // this._initTemplates();\n    },\n\n    _create: function () {\n      this._super();\n      this._resetFinishedDeferreds();\n      if (!$.support.fileInput) {\n        this._disableFileInputButton();\n      }\n    },\n\n    enable: function () {\n      var wasDisabled = false;\n      if (this.options.disabled) {\n        wasDisabled = true;\n      }\n      this._super();\n      if (wasDisabled) {\n        this.element.find('input, button').prop('disabled', false);\n        this._enableFileInputButton();\n      }\n    },\n\n    disable: function () {\n      if (!this.options.disabled) {\n        this.element.find('input, button').prop('disabled', true);\n        this._disableFileInputButton();\n      }\n      this._super();\n    }\n  });\n});\n","jquery/fileUploader/jquery.fileuploader.js":"/**\n * Custom Uploader\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/* global define, require */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define([\n      'jquery',\n      'jquery/fileUploader/jquery.fileupload-image',\n      'jquery/fileUploader/jquery.fileupload-audio',\n      'jquery/fileUploader/jquery.fileupload-video',\n      'jquery/fileUploader/jquery.iframe-transport',\n    ], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(\n      require('jquery'),\n      require('jquery/fileUploader/jquery.fileupload-image'),\n      require('jquery/fileUploader/jquery.fileupload-audio'),\n      require('jquery/fileUploader/jquery.fileupload-video'),\n      require('jquery/fileUploader/jquery.iframe-transport')\n    );\n  } else {\n    // Browser globals:\n    factory(window.jQuery);\n  }\n})();\n","jquery/fileUploader/vendor/jquery.ui.widget.js":"/*! jQuery UI - v1.13.2\n * http://jqueryui.com\n * Includes: widget.js\n * Copyright jQuery Foundation and other contributors; Licensed MIT */\n\n/* global define, require */\n/* eslint-disable no-param-reassign, new-cap, jsdoc/require-jsdoc */\n\n(function (factory) {\n    'use strict';\n    if (typeof define === 'function' && define.amd) {\n        // AMD. Register as an anonymous module.\n        define(['jquery'], factory);\n    } else if (typeof exports === 'object') {\n        // Node/CommonJS\n        factory(require('jquery'));\n    } else {\n        // Browser globals\n        factory(window.jQuery);\n    }\n})(function ($) {\n    ('use strict');\n\n    $.ui = $.ui || {};\n\n    $.ui.version = '1.13.2';\n\n    /*!\n     * jQuery UI Widget 1.13.2\n     * http://jqueryui.com\n     *\n     * Copyright jQuery Foundation and other contributors\n     * Released under the MIT license.\n     * http://jquery.org/license\n     */\n\n    //>>label: Widget\n    //>>group: Core\n    //>>description: Provides a factory for creating stateful widgets with a common API.\n    //>>docs: http://api.jqueryui.com/jQuery.widget/\n    //>>demos: http://jqueryui.com/widget/\n\n    // Support: jQuery 1.9.x or older\n    // $.expr[ \":\" ] is deprecated.\n    if (!$.expr.pseudos) {\n        $.expr.pseudos = $.expr[':'];\n    }\n\n    // Support: jQuery 1.11.x or older\n    // $.unique has been renamed to $.uniqueSort\n    if (!$.uniqueSort) {\n        $.uniqueSort = $.unique;\n    }\n    var widgetUuid = 0;\n    var widgetHasOwnProperty = Array.prototype.hasOwnProperty;\n    var widgetSlice = Array.prototype.slice;\n\n    $.cleanData = (function (orig) {\n        return function (elems) {\n            var events, elem, i;\n            for (i = 0; (elem = elems[i]) != null; i++) {\n                // Only trigger remove when necessary to save time\n                events = $._data(elem, 'events');\n                if (events && events.remove) {\n                    $(elem).triggerHandler('remove');\n                }\n            }\n            orig(elems);\n        };\n    })($.cleanData);\n\n    $.widget = function (name, base, prototype) {\n        var existingConstructor, constructor, basePrototype;\n\n        // ProxiedPrototype allows the provided prototype to remain unmodified\n        // so that it can be used as a mixin for multiple widgets (#8876)\n        var proxiedPrototype = {};\n\n        var namespace = name.split('.')[0];\n        // eslint-disable-next-line no-param-reassign\n        name = name.split('.')[1];\n        var fullName = namespace + '-' + name;\n\n        if (!prototype) {\n            prototype = base;\n            base = $.Widget;\n        }\n\n        if (Array.isArray(prototype)) {\n            prototype = $.extend.apply(null, [{}].concat(prototype));\n        }\n\n        // Create selector for plugin\n        $.expr.pseudos[fullName.toLowerCase()] = function (elem) {\n            return !!$.data(elem, fullName);\n        };\n\n        $[namespace] = $[namespace] || {};\n        existingConstructor = $[namespace][name];\n        constructor = $[namespace][name] = function (options, element) {\n            // Allow instantiation without \"new\" keyword\n            if (!this || !this._createWidget) {\n                return new constructor(options, element);\n            }\n\n            // Allow instantiation without initializing for simple inheritance\n            // must use \"new\" keyword (the code above always passes args)\n            if (arguments.length) {\n                this._createWidget(options, element);\n            }\n        };\n\n        // Extend with the existing constructor to carry over any static properties\n        $.extend(constructor, existingConstructor, {\n            version: prototype.version,\n\n            // Copy the object used to create the prototype in case we need to\n            // redefine the widget later\n            _proto: $.extend({}, prototype),\n\n            // Track widgets that inherit from this widget in case this widget is\n            // redefined after a widget inherits from it\n            _childConstructors: []\n        });\n\n        basePrototype = new base();\n\n        // We need to make the options hash a property directly on the new instance\n        // otherwise we'll modify the options hash on the prototype that we're\n        // inheriting from\n        basePrototype.options = $.widget.extend({}, basePrototype.options);\n        $.each(prototype, function (prop, value) {\n            if (typeof value !== 'function') {\n                proxiedPrototype[prop] = value;\n                return;\n            }\n            proxiedPrototype[prop] = (function () {\n                /**\n                 *\n                 */\n                function _super() {\n                    return base.prototype[prop].apply(this, arguments);\n                }\n\n                /**\n                 * @param args\n                 */\n                function _superApply(args) {\n                    return base.prototype[prop].apply(this, args);\n                }\n\n                return function () {\n                    var __super = this._super;\n                    var __superApply = this._superApply;\n                    var returnValue;\n\n                    this._super = _super;\n                    this._superApply = _superApply;\n\n                    returnValue = value.apply(this, arguments);\n\n                    this._super = __super;\n                    this._superApply = __superApply;\n\n                    return returnValue;\n                };\n            })();\n        });\n        constructor.prototype = $.widget.extend(\n            basePrototype,\n            {\n                // TODO: remove support for widgetEventPrefix\n                // always use the name + a colon as the prefix, e.g., draggable:start\n                // don't prefix for widgets that aren't DOM-based\n                widgetEventPrefix: existingConstructor\n                    ? basePrototype.widgetEventPrefix || name\n                    : name\n            },\n            proxiedPrototype,\n            {\n                constructor: constructor,\n                namespace: namespace,\n                widgetName: name,\n                widgetFullName: fullName\n            }\n        );\n\n        // If this widget is being redefined then we need to find all widgets that\n        // are inheriting from it and redefine all of them so that they inherit from\n        // the new version of this widget. We're essentially trying to replace one\n        // level in the prototype chain.\n        if (existingConstructor) {\n            $.each(existingConstructor._childConstructors, function (i, child) {\n                var childPrototype = child.prototype;\n\n                // Redefine the child widget using the same prototype that was\n                // originally used, but inherit from the new version of the base\n                $.widget(\n                    childPrototype.namespace + '.' + childPrototype.widgetName,\n                    constructor,\n                    child._proto\n                );\n            });\n\n            // Remove the list of existing child constructors from the old constructor\n            // so the old child constructors can be garbage collected\n            delete existingConstructor._childConstructors;\n        } else {\n            base._childConstructors.push(constructor);\n        }\n\n        $.widget.bridge(name, constructor);\n\n        return constructor;\n    };\n\n    $.widget.extend = function (target) {\n        var input = widgetSlice.call(arguments, 1);\n        var inputIndex = 0;\n        var inputLength = input.length;\n        var key;\n        var value;\n\n        for (; inputIndex < inputLength; inputIndex++) {\n            for (key in input[inputIndex]) {\n                value = input[inputIndex][key];\n                if (\n                    widgetHasOwnProperty.call(input[inputIndex], key) &&\n                    value !== undefined\n                ) {\n                    // Clone objects\n                    if ($.isPlainObject(value)) {\n                        target[key] = $.isPlainObject(target[key])\n                            ? $.widget.extend({}, target[key], value)\n                            : // Don't extend strings, arrays, etc. with objects\n                            $.widget.extend({}, value);\n\n                        // Copy everything else by reference\n                    } else {\n                        target[key] = value;\n                    }\n                }\n            }\n        }\n        return target;\n    };\n\n    $.widget.bridge = function (name, object) {\n        var fullName = object.prototype.widgetFullName || name;\n        $.fn[name] = function (options) {\n            var isMethodCall = typeof options === 'string';\n            var args = widgetSlice.call(arguments, 1);\n            var returnValue = this;\n\n            if (isMethodCall) {\n                // If this is an empty collection, we need to have the instance method\n                // return undefined instead of the jQuery instance\n                if (!this.length && options === 'instance') {\n                    returnValue = undefined;\n                } else {\n                    this.each(function () {\n                        var methodValue;\n                        var instance = $.data(this, fullName);\n\n                        if (options === 'instance') {\n                            returnValue = instance;\n                            return false;\n                        }\n\n                        if (!instance) {\n                            return $.error(\n                                'cannot call methods on ' +\n                                name +\n                                ' prior to initialization; ' +\n                                \"attempted to call method '\" +\n                                options +\n                                \"'\"\n                            );\n                        }\n\n                        if (\n                            typeof instance[options] !== 'function' ||\n                            options.charAt(0) === '_'\n                        ) {\n                            return $.error(\n                                \"no such method '\" +\n                                options +\n                                \"' for \" +\n                                name +\n                                ' widget instance'\n                            );\n                        }\n\n                        methodValue = instance[options].apply(instance, args);\n\n                        if (methodValue !== instance && methodValue !== undefined) {\n                            returnValue =\n                                methodValue && methodValue.jquery\n                                    ? returnValue.pushStack(methodValue.get())\n                                    : methodValue;\n                            return false;\n                        }\n                    });\n                }\n            } else {\n                // Allow multiple hashes to be passed on init\n                if (args.length) {\n                    options = $.widget.extend.apply(null, [options].concat(args));\n                }\n\n                this.each(function () {\n                    var instance = $.data(this, fullName);\n                    if (instance) {\n                        instance.option(options || {});\n                        if (instance._init) {\n                            instance._init();\n                        }\n                    } else {\n                        $.data(this, fullName, new object(options, this));\n                    }\n                });\n            }\n\n            return returnValue;\n        };\n    };\n\n    $.Widget = function (/* options, element */) {};\n    $.Widget._childConstructors = [];\n\n    $.Widget.prototype = {\n        widgetName: 'widget',\n        widgetEventPrefix: '',\n        defaultElement: '<div>',\n\n        options: {\n            classes: {},\n            disabled: false,\n\n            // Callbacks\n            create: null\n        },\n\n        _createWidget: function (options, element) {\n            element = $(element || this.defaultElement || this)[0];\n            this.element = $(element);\n            this.uuid = widgetUuid++;\n            this.eventNamespace = '.' + this.widgetName + this.uuid;\n\n            this.bindings = $();\n            this.hoverable = $();\n            this.focusable = $();\n            this.classesElementLookup = {};\n\n            if (element !== this) {\n                $.data(element, this.widgetFullName, this);\n                this._on(true, this.element, {\n                    remove: function (event) {\n                        if (event.target === element) {\n                            this.destroy();\n                        }\n                    }\n                });\n                this.document = $(\n                    element.style\n                        ? // Element within the document\n                        element.ownerDocument\n                        : // Element is window or document\n                        element.document || element\n                );\n                this.window = $(\n                    this.document[0].defaultView || this.document[0].parentWindow\n                );\n            }\n\n            this.options = $.widget.extend(\n                {},\n                this.options,\n                this._getCreateOptions(),\n                options\n            );\n\n            this._create();\n\n            if (this.options.disabled) {\n                this._setOptionDisabled(this.options.disabled);\n            }\n\n            this._trigger('create', null, this._getCreateEventData());\n            this._init();\n        },\n\n        _getCreateOptions: function () {\n            return {};\n        },\n\n        _getCreateEventData: $.noop,\n\n        _create: $.noop,\n\n        _init: $.noop,\n\n        destroy: function () {\n            var that = this;\n\n            this._destroy();\n            $.each(this.classesElementLookup, function (key, value) {\n                that._removeClass(value, key);\n            });\n\n            // We can probably remove the unbind calls in 2.0\n            // all event bindings should go through this._on()\n            this.element.off(this.eventNamespace).removeData(this.widgetFullName);\n            this.widget().off(this.eventNamespace).removeAttr('aria-disabled');\n\n            // Clean up events and states\n            this.bindings.off(this.eventNamespace);\n        },\n\n        _destroy: $.noop,\n\n        widget: function () {\n            return this.element;\n        },\n\n        option: function (key, value) {\n            var options = key;\n            var parts;\n            var curOption;\n            var i;\n\n            if (arguments.length === 0) {\n                // Don't return a reference to the internal hash\n                return $.widget.extend({}, this.options);\n            }\n\n            if (typeof key === 'string') {\n                // Handle nested keys, e.g., \"foo.bar\" => { foo: { bar: ___ } }\n                options = {};\n                parts = key.split('.');\n                key = parts.shift();\n                if (parts.length) {\n                    curOption = options[key] = $.widget.extend({}, this.options[key]);\n                    for (i = 0; i < parts.length - 1; i++) {\n                        curOption[parts[i]] = curOption[parts[i]] || {};\n                        curOption = curOption[parts[i]];\n                    }\n                    key = parts.pop();\n                    if (arguments.length === 1) {\n                        return curOption[key] === undefined ? null : curOption[key];\n                    }\n                    curOption[key] = value;\n                } else {\n                    if (arguments.length === 1) {\n                        return this.options[key] === undefined ? null : this.options[key];\n                    }\n                    options[key] = value;\n                }\n            }\n\n            this._setOptions(options);\n\n            return this;\n        },\n\n        _setOptions: function (options) {\n            var key;\n\n            for (key in options) {\n                this._setOption(key, options[key]);\n            }\n\n            return this;\n        },\n\n        _setOption: function (key, value) {\n            if (key === 'classes') {\n                this._setOptionClasses(value);\n            }\n\n            this.options[key] = value;\n\n            if (key === 'disabled') {\n                this._setOptionDisabled(value);\n            }\n\n            return this;\n        },\n\n        _setOptionClasses: function (value) {\n            var classKey, elements, currentElements;\n\n            for (classKey in value) {\n                currentElements = this.classesElementLookup[classKey];\n                if (\n                    value[classKey] === this.options.classes[classKey] ||\n                    !currentElements ||\n                    !currentElements.length\n                ) {\n                    continue;\n                }\n\n                // We are doing this to create a new jQuery object because the _removeClass() call\n                // on the next line is going to destroy the reference to the current elements being\n                // tracked. We need to save a copy of this collection so that we can add the new classes\n                // below.\n                elements = $(currentElements.get());\n                this._removeClass(currentElements, classKey);\n\n                // We don't use _addClass() here, because that uses this.options.classes\n                // for generating the string of classes. We want to use the value passed in from\n                // _setOption(), this is the new value of the classes option which was passed to\n                // _setOption(). We pass this value directly to _classes().\n                elements.addClass(\n                    this._classes({\n                        element: elements,\n                        keys: classKey,\n                        classes: value,\n                        add: true\n                    })\n                );\n            }\n        },\n\n        _setOptionDisabled: function (value) {\n            this._toggleClass(\n                this.widget(),\n                this.widgetFullName + '-disabled',\n                null,\n                !!value\n            );\n\n            // If the widget is becoming disabled, then nothing is interactive\n            if (value) {\n                this._removeClass(this.hoverable, null, 'ui-state-hover');\n                this._removeClass(this.focusable, null, 'ui-state-focus');\n            }\n        },\n\n        enable: function () {\n            return this._setOptions({ disabled: false });\n        },\n\n        disable: function () {\n            return this._setOptions({ disabled: true });\n        },\n\n        _classes: function (options) {\n            var full = [];\n            var that = this;\n\n            options = $.extend(\n                {\n                    element: this.element,\n                    classes: this.options.classes || {}\n                },\n                options\n            );\n\n            /**\n             *\n             */\n            function bindRemoveEvent() {\n                var nodesToBind = [];\n\n                options.element.each(function (_, element) {\n                    var isTracked = $.map(that.classesElementLookup, function (elements) {\n                        return elements;\n                    }).some(function (elements) {\n                        return elements.is(element);\n                    });\n\n                    if (!isTracked) {\n                        nodesToBind.push(element);\n                    }\n                });\n\n                that._on($(nodesToBind), {\n                    remove: '_untrackClassesElement'\n                });\n            }\n\n            /**\n             * @param classes\n             * @param checkOption\n             */\n            function processClassString(classes, checkOption) {\n                var current, i;\n                for (i = 0; i < classes.length; i++) {\n                    current = that.classesElementLookup[classes[i]] || $();\n                    if (options.add) {\n                        bindRemoveEvent();\n                        current = $(\n                            $.uniqueSort(current.get().concat(options.element.get()))\n                        );\n                    } else {\n                        current = $(current.not(options.element).get());\n                    }\n                    that.classesElementLookup[classes[i]] = current;\n                    full.push(classes[i]);\n                    if (checkOption && options.classes[classes[i]]) {\n                        full.push(options.classes[classes[i]]);\n                    }\n                }\n            }\n\n            if (options.keys) {\n                processClassString(options.keys.match(/\\S+/g) || [], true);\n            }\n            if (options.extra) {\n                processClassString(options.extra.match(/\\S+/g) || []);\n            }\n\n            return full.join(' ');\n        },\n\n        _untrackClassesElement: function (event) {\n            var that = this;\n            $.each(that.classesElementLookup, function (key, value) {\n                if ($.inArray(event.target, value) !== -1) {\n                    that.classesElementLookup[key] = $(value.not(event.target).get());\n                }\n            });\n\n            this._off($(event.target));\n        },\n\n        _removeClass: function (element, keys, extra) {\n            return this._toggleClass(element, keys, extra, false);\n        },\n\n        _addClass: function (element, keys, extra) {\n            return this._toggleClass(element, keys, extra, true);\n        },\n\n        _toggleClass: function (element, keys, extra, add) {\n            add = typeof add === 'boolean' ? add : extra;\n            var shift = typeof element === 'string' || element === null,\n                options = {\n                    extra: shift ? keys : extra,\n                    keys: shift ? element : keys,\n                    element: shift ? this.element : element,\n                    add: add\n                };\n            options.element.toggleClass(this._classes(options), add);\n            return this;\n        },\n\n        _on: function (suppressDisabledCheck, element, handlers) {\n            var delegateElement;\n            var instance = this;\n\n            // No suppressDisabledCheck flag, shuffle arguments\n            if (typeof suppressDisabledCheck !== 'boolean') {\n                handlers = element;\n                element = suppressDisabledCheck;\n                suppressDisabledCheck = false;\n            }\n\n            // No element argument, shuffle and use this.element\n            if (!handlers) {\n                handlers = element;\n                element = this.element;\n                delegateElement = this.widget();\n            } else {\n                element = delegateElement = $(element);\n                this.bindings = this.bindings.add(element);\n            }\n\n            $.each(handlers, function (event, handler) {\n                /**\n                 *\n                 */\n                function handlerProxy() {\n                    // Allow widgets to customize the disabled handling\n                    // - disabled as an array instead of boolean\n                    // - disabled class as method for disabling individual parts\n                    if (\n                        !suppressDisabledCheck &&\n                        (instance.options.disabled === true ||\n                            $(this).hasClass('ui-state-disabled'))\n                    ) {\n                        return;\n                    }\n                    return (\n                        typeof handler === 'string' ? instance[handler] : handler\n                    ).apply(instance, arguments);\n                }\n\n                // Copy the guid so direct unbinding works\n                if (typeof handler !== 'string') {\n                    handlerProxy.guid = handler.guid =\n                        handler.guid || handlerProxy.guid || $.guid++;\n                }\n\n                var match = event.match(/^([\\w:-]*)\\s*(.*)$/);\n                var eventName = match[1] + instance.eventNamespace;\n                var selector = match[2];\n\n                if (selector) {\n                    delegateElement.on(eventName, selector, handlerProxy);\n                } else {\n                    element.on(eventName, handlerProxy);\n                }\n            });\n        },\n\n        _off: function (element, eventName) {\n            eventName =\n                (eventName || '').split(' ').join(this.eventNamespace + ' ') +\n                this.eventNamespace;\n            element.off(eventName);\n\n            // Clear the stack to avoid memory leaks (#10056)\n            this.bindings = $(this.bindings.not(element).get());\n            this.focusable = $(this.focusable.not(element).get());\n            this.hoverable = $(this.hoverable.not(element).get());\n        },\n\n        _delay: function (handler, delay) {\n            /**\n             *\n             */\n            function handlerProxy() {\n                return (\n                    typeof handler === 'string' ? instance[handler] : handler\n                ).apply(instance, arguments);\n            }\n            var instance = this;\n            return setTimeout(handlerProxy, delay || 0);\n        },\n\n        _hoverable: function (element) {\n            this.hoverable = this.hoverable.add(element);\n            this._on(element, {\n                mouseenter: function (event) {\n                    this._addClass($(event.currentTarget), null, 'ui-state-hover');\n                },\n                mouseleave: function (event) {\n                    this._removeClass($(event.currentTarget), null, 'ui-state-hover');\n                }\n            });\n        },\n\n        _focusable: function (element) {\n            this.focusable = this.focusable.add(element);\n            this._on(element, {\n                focusin: function (event) {\n                    this._addClass($(event.currentTarget), null, 'ui-state-focus');\n                },\n                focusout: function (event) {\n                    this._removeClass($(event.currentTarget), null, 'ui-state-focus');\n                }\n            });\n        },\n\n        _trigger: function (type, event, data) {\n            var prop, orig;\n            var callback = this.options[type];\n\n            data = data || {};\n            event = $.Event(event);\n            event.type = (\n                type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type\n            ).toLowerCase();\n\n            // The original event may come from any element\n            // so we need to reset the target on the new event\n            event.target = this.element[0];\n\n            // Copy original event properties over to the new event\n            orig = event.originalEvent;\n            if (orig) {\n                for (prop in orig) {\n                    if (!(prop in event)) {\n                        event[prop] = orig[prop];\n                    }\n                }\n            }\n\n            this.element.trigger(event, data);\n            return !(\n                (typeof callback === 'function' &&\n                    callback.apply(this.element[0], [event].concat(data)) === false) ||\n                event.isDefaultPrevented()\n            );\n        }\n    };\n\n    $.each({ show: 'fadeIn', hide: 'fadeOut' }, function (method, defaultEffect) {\n        $.Widget.prototype['_' + method] = function (element, options, callback) {\n            if (typeof options === 'string') {\n                options = { effect: options };\n            }\n\n            var hasOptions;\n            var effectName = !options\n                ? method\n                : options === true || typeof options === 'number'\n                    ? defaultEffect\n                    : options.effect || defaultEffect;\n\n            options = options || {};\n            if (typeof options === 'number') {\n                options = { duration: options };\n            } else if (options === true) {\n                options = {};\n            }\n\n            hasOptions = !$.isEmptyObject(options);\n            options.complete = callback;\n\n            if (options.delay) {\n                element.delay(options.delay);\n            }\n\n            if (hasOptions && $.effects && $.effects.effect[effectName]) {\n                element[method](options);\n            } else if (effectName !== method && element[effectName]) {\n                element[effectName](options.duration, options.easing, callback);\n            } else {\n                element.queue(function (next) {\n                    $(this)[method]();\n                    if (callback) {\n                        callback.call(element[0]);\n                    }\n                    next();\n                });\n            }\n        };\n    });\n});\n","jquery/fileUploader/vendor/blueimp-canvas-to-blob/js/canvas-to-blob.js":"/*\n * JavaScript Canvas to Blob\n * https://github.com/blueimp/JavaScript-Canvas-to-Blob\n *\n * Copyright 2012, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n *\n * Based on stackoverflow user Stoive's code snippet:\n * http://stackoverflow.com/q/4998908\n */\n\n/* global define, Uint8Array, ArrayBuffer, module */\n\n;(function (window) {\n  'use strict'\n\n  var CanvasPrototype =\n    window.HTMLCanvasElement && window.HTMLCanvasElement.prototype\n  var hasBlobConstructor =\n    window.Blob &&\n    (function () {\n      try {\n        return Boolean(new Blob())\n      } catch (e) {\n        return false\n      }\n    })()\n  var hasArrayBufferViewSupport =\n    hasBlobConstructor &&\n    window.Uint8Array &&\n    (function () {\n      try {\n        return new Blob([new Uint8Array(100)]).size === 100\n      } catch (e) {\n        return false\n      }\n    })()\n  var BlobBuilder =\n    window.BlobBuilder ||\n    window.WebKitBlobBuilder ||\n    window.MozBlobBuilder ||\n    window.MSBlobBuilder\n  var dataURIPattern = /^data:((.*?)(;charset=.*?)?)(;base64)?,/\n  var dataURLtoBlob =\n    (hasBlobConstructor || BlobBuilder) &&\n    window.atob &&\n    window.ArrayBuffer &&\n    window.Uint8Array &&\n    function (dataURI) {\n      var matches,\n        mediaType,\n        isBase64,\n        dataString,\n        byteString,\n        arrayBuffer,\n        intArray,\n        i,\n        bb\n      // Parse the dataURI components as per RFC 2397\n      matches = dataURI.match(dataURIPattern)\n      if (!matches) {\n        throw new Error('invalid data URI')\n      }\n      // Default to text/plain;charset=US-ASCII\n      mediaType = matches[2]\n        ? matches[1]\n        : 'text/plain' + (matches[3] || ';charset=US-ASCII')\n      isBase64 = !!matches[4]\n      dataString = dataURI.slice(matches[0].length)\n      if (isBase64) {\n        // Convert base64 to raw binary data held in a string:\n        byteString = atob(dataString)\n      } else {\n        // Convert base64/URLEncoded data component to raw binary:\n        byteString = decodeURIComponent(dataString)\n      }\n      // Write the bytes of the string to an ArrayBuffer:\n      arrayBuffer = new ArrayBuffer(byteString.length)\n      intArray = new Uint8Array(arrayBuffer)\n      for (i = 0; i < byteString.length; i += 1) {\n        intArray[i] = byteString.charCodeAt(i)\n      }\n      // Write the ArrayBuffer (or ArrayBufferView) to a blob:\n      if (hasBlobConstructor) {\n        return new Blob([hasArrayBufferViewSupport ? intArray : arrayBuffer], {\n          type: mediaType\n        })\n      }\n      bb = new BlobBuilder()\n      bb.append(arrayBuffer)\n      return bb.getBlob(mediaType)\n    }\n  if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {\n    if (CanvasPrototype.mozGetAsFile) {\n      CanvasPrototype.toBlob = function (callback, type, quality) {\n        var self = this\n        setTimeout(function () {\n          if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {\n            callback(dataURLtoBlob(self.toDataURL(type, quality)))\n          } else {\n            callback(self.mozGetAsFile('blob', type))\n          }\n        })\n      }\n    } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {\n      if (CanvasPrototype.msToBlob) {\n        CanvasPrototype.toBlob = function (callback, type, quality) {\n          var self = this\n          setTimeout(function () {\n            if (\n              ((type && type !== 'image/png') || quality) &&\n              CanvasPrototype.toDataURL &&\n              dataURLtoBlob\n            ) {\n              callback(dataURLtoBlob(self.toDataURL(type, quality)))\n            } else {\n              callback(self.msToBlob(type))\n            }\n          })\n        }\n      } else {\n        CanvasPrototype.toBlob = function (callback, type, quality) {\n          var self = this\n          setTimeout(function () {\n            callback(dataURLtoBlob(self.toDataURL(type, quality)))\n          })\n        }\n      }\n    }\n  }\n  if (typeof define === 'function' && define.amd) {\n    define(function () {\n      return dataURLtoBlob\n    })\n  } else if (typeof module === 'object' && module.exports) {\n    module.exports = dataURLtoBlob\n  } else {\n    window.dataURLtoBlob = dataURLtoBlob\n  }\n})(window)\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image-scale.js":"/*\n * JavaScript Load Image Scaling\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, module, require */\n\n;(function (factory) {\n  'use strict'\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery/fileUploader/vendor/blueimp-load-image/js/load-image'], factory)\n  } else if (typeof module === 'object' && module.exports) {\n    factory(require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'))\n  } else {\n    // Browser globals:\n    factory(window.loadImage)\n  }\n})(function (loadImage) {\n  'use strict'\n\n  var originalTransform = loadImage.transform\n\n  loadImage.createCanvas = function (width, height, offscreen) {\n    if (offscreen && loadImage.global.OffscreenCanvas) {\n      return new OffscreenCanvas(width, height)\n    }\n    var canvas = document.createElement('canvas')\n    canvas.width = width\n    canvas.height = height\n    return canvas\n  }\n\n  loadImage.transform = function (img, options, callback, file, data) {\n    originalTransform.call(\n      loadImage,\n      loadImage.scale(img, options, data),\n      options,\n      callback,\n      file,\n      data\n    )\n  }\n\n  // Transform image coordinates, allows to override e.g.\n  // the canvas orientation based on the orientation option,\n  // gets canvas, options and data passed as arguments:\n  loadImage.transformCoordinates = function () {}\n\n  // Returns transformed options, allows to override e.g.\n  // maxWidth, maxHeight and crop options based on the aspectRatio.\n  // gets img, options, data passed as arguments:\n  loadImage.getTransformedOptions = function (img, options) {\n    var aspectRatio = options.aspectRatio\n    var newOptions\n    var i\n    var width\n    var height\n    if (!aspectRatio) {\n      return options\n    }\n    newOptions = {}\n    for (i in options) {\n      if (Object.prototype.hasOwnProperty.call(options, i)) {\n        newOptions[i] = options[i]\n      }\n    }\n    newOptions.crop = true\n    width = img.naturalWidth || img.width\n    height = img.naturalHeight || img.height\n    if (width / height > aspectRatio) {\n      newOptions.maxWidth = height * aspectRatio\n      newOptions.maxHeight = height\n    } else {\n      newOptions.maxWidth = width\n      newOptions.maxHeight = width / aspectRatio\n    }\n    return newOptions\n  }\n\n  // Canvas render method, allows to implement a different rendering algorithm:\n  loadImage.drawImage = function (\n    img,\n    canvas,\n    sourceX,\n    sourceY,\n    sourceWidth,\n    sourceHeight,\n    destWidth,\n    destHeight,\n    options\n  ) {\n    var ctx = canvas.getContext('2d')\n    if (options.imageSmoothingEnabled === false) {\n      ctx.msImageSmoothingEnabled = false\n      ctx.imageSmoothingEnabled = false\n    } else if (options.imageSmoothingQuality) {\n      ctx.imageSmoothingQuality = options.imageSmoothingQuality\n    }\n    ctx.drawImage(\n      img,\n      sourceX,\n      sourceY,\n      sourceWidth,\n      sourceHeight,\n      0,\n      0,\n      destWidth,\n      destHeight\n    )\n    return ctx\n  }\n\n  // Determines if the target image should be a canvas element:\n  loadImage.requiresCanvas = function (options) {\n    return options.canvas || options.crop || !!options.aspectRatio\n  }\n\n  // Scales and/or crops the given image (img or canvas HTML element)\n  // using the given options:\n  loadImage.scale = function (img, options, data) {\n    // eslint-disable-next-line no-param-reassign\n    options = options || {}\n    // eslint-disable-next-line no-param-reassign\n    data = data || {}\n    var useCanvas =\n      img.getContext ||\n      (loadImage.requiresCanvas(options) &&\n        !!loadImage.global.HTMLCanvasElement)\n    var width = img.naturalWidth || img.width\n    var height = img.naturalHeight || img.height\n    var destWidth = width\n    var destHeight = height\n    var maxWidth\n    var maxHeight\n    var minWidth\n    var minHeight\n    var sourceWidth\n    var sourceHeight\n    var sourceX\n    var sourceY\n    var pixelRatio\n    var downsamplingRatio\n    var tmp\n    var canvas\n    /**\n     * Scales up image dimensions\n     */\n    function scaleUp() {\n      var scale = Math.max(\n        (minWidth || destWidth) / destWidth,\n        (minHeight || destHeight) / destHeight\n      )\n      if (scale > 1) {\n        destWidth *= scale\n        destHeight *= scale\n      }\n    }\n    /**\n     * Scales down image dimensions\n     */\n    function scaleDown() {\n      var scale = Math.min(\n        (maxWidth || destWidth) / destWidth,\n        (maxHeight || destHeight) / destHeight\n      )\n      if (scale < 1) {\n        destWidth *= scale\n        destHeight *= scale\n      }\n    }\n    if (useCanvas) {\n      // eslint-disable-next-line no-param-reassign\n      options = loadImage.getTransformedOptions(img, options, data)\n      sourceX = options.left || 0\n      sourceY = options.top || 0\n      if (options.sourceWidth) {\n        sourceWidth = options.sourceWidth\n        if (options.right !== undefined && options.left === undefined) {\n          sourceX = width - sourceWidth - options.right\n        }\n      } else {\n        sourceWidth = width - sourceX - (options.right || 0)\n      }\n      if (options.sourceHeight) {\n        sourceHeight = options.sourceHeight\n        if (options.bottom !== undefined && options.top === undefined) {\n          sourceY = height - sourceHeight - options.bottom\n        }\n      } else {\n        sourceHeight = height - sourceY - (options.bottom || 0)\n      }\n      destWidth = sourceWidth\n      destHeight = sourceHeight\n    }\n    maxWidth = options.maxWidth\n    maxHeight = options.maxHeight\n    minWidth = options.minWidth\n    minHeight = options.minHeight\n    if (useCanvas && maxWidth && maxHeight && options.crop) {\n      destWidth = maxWidth\n      destHeight = maxHeight\n      tmp = sourceWidth / sourceHeight - maxWidth / maxHeight\n      if (tmp < 0) {\n        sourceHeight = (maxHeight * sourceWidth) / maxWidth\n        if (options.top === undefined && options.bottom === undefined) {\n          sourceY = (height - sourceHeight) / 2\n        }\n      } else if (tmp > 0) {\n        sourceWidth = (maxWidth * sourceHeight) / maxHeight\n        if (options.left === undefined && options.right === undefined) {\n          sourceX = (width - sourceWidth) / 2\n        }\n      }\n    } else {\n      if (options.contain || options.cover) {\n        minWidth = maxWidth = maxWidth || minWidth\n        minHeight = maxHeight = maxHeight || minHeight\n      }\n      if (options.cover) {\n        scaleDown()\n        scaleUp()\n      } else {\n        scaleUp()\n        scaleDown()\n      }\n    }\n    if (useCanvas) {\n      pixelRatio = options.pixelRatio\n      if (\n        pixelRatio > 1 &&\n        // Check if the image has not yet had the device pixel ratio applied:\n        !(\n          img.style.width &&\n          Math.floor(parseFloat(img.style.width, 10)) ===\n            Math.floor(width / pixelRatio)\n        )\n      ) {\n        destWidth *= pixelRatio\n        destHeight *= pixelRatio\n      }\n      // Check if workaround for Chromium orientation crop bug is required:\n      // https://bugs.chromium.org/p/chromium/issues/detail?id=1074354\n      if (\n        loadImage.orientationCropBug &&\n        !img.getContext &&\n        (sourceX || sourceY || sourceWidth !== width || sourceHeight !== height)\n      ) {\n        // Write the complete source image to an intermediate canvas first:\n        tmp = img\n        // eslint-disable-next-line no-param-reassign\n        img = loadImage.createCanvas(width, height, true)\n        loadImage.drawImage(\n          tmp,\n          img,\n          0,\n          0,\n          width,\n          height,\n          width,\n          height,\n          options\n        )\n      }\n      downsamplingRatio = options.downsamplingRatio\n      if (\n        downsamplingRatio > 0 &&\n        downsamplingRatio < 1 &&\n        destWidth < sourceWidth &&\n        destHeight < sourceHeight\n      ) {\n        while (sourceWidth * downsamplingRatio > destWidth) {\n          canvas = loadImage.createCanvas(\n            sourceWidth * downsamplingRatio,\n            sourceHeight * downsamplingRatio,\n            true\n          )\n          loadImage.drawImage(\n            img,\n            canvas,\n            sourceX,\n            sourceY,\n            sourceWidth,\n            sourceHeight,\n            canvas.width,\n            canvas.height,\n            options\n          )\n          sourceX = 0\n          sourceY = 0\n          sourceWidth = canvas.width\n          sourceHeight = canvas.height\n          // eslint-disable-next-line no-param-reassign\n          img = canvas\n        }\n      }\n      canvas = loadImage.createCanvas(destWidth, destHeight)\n      loadImage.transformCoordinates(canvas, options, data)\n      if (pixelRatio > 1) {\n        canvas.style.width = canvas.width / pixelRatio + 'px'\n      }\n      loadImage\n        .drawImage(\n          img,\n          canvas,\n          sourceX,\n          sourceY,\n          sourceWidth,\n          sourceHeight,\n          destWidth,\n          destHeight,\n          options\n        )\n        .setTransform(1, 0, 0, 1, 0, 0) // reset to the identity matrix\n      return canvas\n    }\n    img.width = destWidth\n    img.height = destHeight\n    return img\n  }\n})\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image-exif.js":"/*\n * JavaScript Load Image Exif Parser\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2013, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, module, require, DataView */\n\n/* eslint-disable no-console */\n\n;(function (factory) {\n  'use strict'\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery/fileUploader/vendor/blueimp-load-image/js/load-image', 'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta'], factory)\n  } else if (typeof module === 'object' && module.exports) {\n    factory(require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'), require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta'))\n  } else {\n    // Browser globals:\n    factory(window.loadImage)\n  }\n})(function (loadImage) {\n  'use strict'\n\n  /**\n   * Exif tag map\n   *\n   * @name ExifMap\n   * @class\n   * @param {number|string} tagCode IFD tag code\n   */\n  function ExifMap(tagCode) {\n    if (tagCode) {\n      Object.defineProperty(this, 'map', {\n        value: this.ifds[tagCode].map\n      })\n      Object.defineProperty(this, 'tags', {\n        value: (this.tags && this.tags[tagCode]) || {}\n      })\n    }\n  }\n\n  ExifMap.prototype.map = {\n    Orientation: 0x0112,\n    Thumbnail: 'ifd1',\n    Blob: 0x0201, // Alias for JPEGInterchangeFormat\n    Exif: 0x8769,\n    GPSInfo: 0x8825,\n    Interoperability: 0xa005\n  }\n\n  ExifMap.prototype.ifds = {\n    ifd1: { name: 'Thumbnail', map: ExifMap.prototype.map },\n    0x8769: { name: 'Exif', map: {} },\n    0x8825: { name: 'GPSInfo', map: {} },\n    0xa005: { name: 'Interoperability', map: {} }\n  }\n\n  /**\n   * Retrieves exif tag value\n   *\n   * @param {number|string} id Exif tag code or name\n   * @returns {object} Exif tag value\n   */\n  ExifMap.prototype.get = function (id) {\n    return this[id] || this[this.map[id]]\n  }\n\n  /**\n   * Returns the Exif Thumbnail data as Blob.\n   *\n   * @param {DataView} dataView Data view interface\n   * @param {number} offset Thumbnail data offset\n   * @param {number} length Thumbnail data length\n   * @returns {undefined|Blob} Returns the Thumbnail Blob or undefined\n   */\n  function getExifThumbnail(dataView, offset, length) {\n    if (!length) return\n    if (offset + length > dataView.byteLength) {\n      console.log('Invalid Exif data: Invalid thumbnail data.')\n      return\n    }\n    return new Blob(\n      [loadImage.bufferSlice.call(dataView.buffer, offset, offset + length)],\n      {\n        type: 'image/jpeg'\n      }\n    )\n  }\n\n  var ExifTagTypes = {\n    // byte, 8-bit unsigned int:\n    1: {\n      getValue: function (dataView, dataOffset) {\n        return dataView.getUint8(dataOffset)\n      },\n      size: 1\n    },\n    // ascii, 8-bit byte:\n    2: {\n      getValue: function (dataView, dataOffset) {\n        return String.fromCharCode(dataView.getUint8(dataOffset))\n      },\n      size: 1,\n      ascii: true\n    },\n    // short, 16 bit int:\n    3: {\n      getValue: function (dataView, dataOffset, littleEndian) {\n        return dataView.getUint16(dataOffset, littleEndian)\n      },\n      size: 2\n    },\n    // long, 32 bit int:\n    4: {\n      getValue: function (dataView, dataOffset, littleEndian) {\n        return dataView.getUint32(dataOffset, littleEndian)\n      },\n      size: 4\n    },\n    // rational = two long values, first is numerator, second is denominator:\n    5: {\n      getValue: function (dataView, dataOffset, littleEndian) {\n        return (\n          dataView.getUint32(dataOffset, littleEndian) /\n          dataView.getUint32(dataOffset + 4, littleEndian)\n        )\n      },\n      size: 8\n    },\n    // slong, 32 bit signed int:\n    9: {\n      getValue: function (dataView, dataOffset, littleEndian) {\n        return dataView.getInt32(dataOffset, littleEndian)\n      },\n      size: 4\n    },\n    // srational, two slongs, first is numerator, second is denominator:\n    10: {\n      getValue: function (dataView, dataOffset, littleEndian) {\n        return (\n          dataView.getInt32(dataOffset, littleEndian) /\n          dataView.getInt32(dataOffset + 4, littleEndian)\n        )\n      },\n      size: 8\n    }\n  }\n  // undefined, 8-bit byte, value depending on field:\n  ExifTagTypes[7] = ExifTagTypes[1]\n\n  /**\n   * Returns Exif tag value.\n   *\n   * @param {DataView} dataView Data view interface\n   * @param {number} tiffOffset TIFF offset\n   * @param {number} offset Tag offset\n   * @param {number} type Tag type\n   * @param {number} length Tag length\n   * @param {boolean} littleEndian Little endian encoding\n   * @returns {object} Tag value\n   */\n  function getExifValue(\n    dataView,\n    tiffOffset,\n    offset,\n    type,\n    length,\n    littleEndian\n  ) {\n    var tagType = ExifTagTypes[type]\n    var tagSize\n    var dataOffset\n    var values\n    var i\n    var str\n    var c\n    if (!tagType) {\n      console.log('Invalid Exif data: Invalid tag type.')\n      return\n    }\n    tagSize = tagType.size * length\n    // Determine if the value is contained in the dataOffset bytes,\n    // or if the value at the dataOffset is a pointer to the actual data:\n    dataOffset =\n      tagSize > 4\n        ? tiffOffset + dataView.getUint32(offset + 8, littleEndian)\n        : offset + 8\n    if (dataOffset + tagSize > dataView.byteLength) {\n      console.log('Invalid Exif data: Invalid data offset.')\n      return\n    }\n    if (length === 1) {\n      return tagType.getValue(dataView, dataOffset, littleEndian)\n    }\n    values = []\n    for (i = 0; i < length; i += 1) {\n      values[i] = tagType.getValue(\n        dataView,\n        dataOffset + i * tagType.size,\n        littleEndian\n      )\n    }\n    if (tagType.ascii) {\n      str = ''\n      // Concatenate the chars:\n      for (i = 0; i < values.length; i += 1) {\n        c = values[i]\n        // Ignore the terminating NULL byte(s):\n        if (c === '\\u0000') {\n          break\n        }\n        str += c\n      }\n      return str\n    }\n    return values\n  }\n\n  /**\n   * Determines if the given tag should be included.\n   *\n   * @param {object} includeTags Map of tags to include\n   * @param {object} excludeTags Map of tags to exclude\n   * @param {number|string} tagCode Tag code to check\n   * @returns {boolean} True if the tag should be included\n   */\n  function shouldIncludeTag(includeTags, excludeTags, tagCode) {\n    return (\n      (!includeTags || includeTags[tagCode]) &&\n      (!excludeTags || excludeTags[tagCode] !== true)\n    )\n  }\n\n  /**\n   * Parses Exif tags.\n   *\n   * @param {DataView} dataView Data view interface\n   * @param {number} tiffOffset TIFF offset\n   * @param {number} dirOffset Directory offset\n   * @param {boolean} littleEndian Little endian encoding\n   * @param {ExifMap} tags Map to store parsed exif tags\n   * @param {ExifMap} tagOffsets Map to store parsed exif tag offsets\n   * @param {object} includeTags Map of tags to include\n   * @param {object} excludeTags Map of tags to exclude\n   * @returns {number} Next directory offset\n   */\n  function parseExifTags(\n    dataView,\n    tiffOffset,\n    dirOffset,\n    littleEndian,\n    tags,\n    tagOffsets,\n    includeTags,\n    excludeTags\n  ) {\n    var tagsNumber, dirEndOffset, i, tagOffset, tagNumber, tagValue\n    if (dirOffset + 6 > dataView.byteLength) {\n      console.log('Invalid Exif data: Invalid directory offset.')\n      return\n    }\n    tagsNumber = dataView.getUint16(dirOffset, littleEndian)\n    dirEndOffset = dirOffset + 2 + 12 * tagsNumber\n    if (dirEndOffset + 4 > dataView.byteLength) {\n      console.log('Invalid Exif data: Invalid directory size.')\n      return\n    }\n    for (i = 0; i < tagsNumber; i += 1) {\n      tagOffset = dirOffset + 2 + 12 * i\n      tagNumber = dataView.getUint16(tagOffset, littleEndian)\n      if (!shouldIncludeTag(includeTags, excludeTags, tagNumber)) continue\n      tagValue = getExifValue(\n        dataView,\n        tiffOffset,\n        tagOffset,\n        dataView.getUint16(tagOffset + 2, littleEndian), // tag type\n        dataView.getUint32(tagOffset + 4, littleEndian), // tag length\n        littleEndian\n      )\n      tags[tagNumber] = tagValue\n      if (tagOffsets) {\n        tagOffsets[tagNumber] = tagOffset\n      }\n    }\n    // Return the offset to the next directory:\n    return dataView.getUint32(dirEndOffset, littleEndian)\n  }\n\n  /**\n   * Parses tags in a given IFD (Image File Directory).\n   *\n   * @param {object} data Data object to store exif tags and offsets\n   * @param {number|string} tagCode IFD tag code\n   * @param {DataView} dataView Data view interface\n   * @param {number} tiffOffset TIFF offset\n   * @param {boolean} littleEndian Little endian encoding\n   * @param {object} includeTags Map of tags to include\n   * @param {object} excludeTags Map of tags to exclude\n   */\n  function parseExifIFD(\n    data,\n    tagCode,\n    dataView,\n    tiffOffset,\n    littleEndian,\n    includeTags,\n    excludeTags\n  ) {\n    var dirOffset = data.exif[tagCode]\n    if (dirOffset) {\n      data.exif[tagCode] = new ExifMap(tagCode)\n      if (data.exifOffsets) {\n        data.exifOffsets[tagCode] = new ExifMap(tagCode)\n      }\n      parseExifTags(\n        dataView,\n        tiffOffset,\n        tiffOffset + dirOffset,\n        littleEndian,\n        data.exif[tagCode],\n        data.exifOffsets && data.exifOffsets[tagCode],\n        includeTags && includeTags[tagCode],\n        excludeTags && excludeTags[tagCode]\n      )\n    }\n  }\n\n  loadImage.parseExifData = function (dataView, offset, length, data, options) {\n    if (options.disableExif) {\n      return\n    }\n    var includeTags = options.includeExifTags\n    var excludeTags = options.excludeExifTags || {\n      0x8769: {\n        // ExifIFDPointer\n        0x927c: true // MakerNote\n      }\n    }\n    var tiffOffset = offset + 10\n    var littleEndian\n    var dirOffset\n    var thumbnailIFD\n    // Check for the ASCII code for \"Exif\" (0x45786966):\n    if (dataView.getUint32(offset + 4) !== 0x45786966) {\n      // No Exif data, might be XMP data instead\n      return\n    }\n    if (tiffOffset + 8 > dataView.byteLength) {\n      console.log('Invalid Exif data: Invalid segment size.')\n      return\n    }\n    // Check for the two null bytes:\n    if (dataView.getUint16(offset + 8) !== 0x0000) {\n      console.log('Invalid Exif data: Missing byte alignment offset.')\n      return\n    }\n    // Check the byte alignment:\n    switch (dataView.getUint16(tiffOffset)) {\n      case 0x4949:\n        littleEndian = true\n        break\n      case 0x4d4d:\n        littleEndian = false\n        break\n      default:\n        console.log('Invalid Exif data: Invalid byte alignment marker.')\n        return\n    }\n    // Check for the TIFF tag marker (0x002A):\n    if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002a) {\n      console.log('Invalid Exif data: Missing TIFF marker.')\n      return\n    }\n    // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n    dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian)\n    // Create the exif object to store the tags:\n    data.exif = new ExifMap()\n    if (!options.disableExifOffsets) {\n      data.exifOffsets = new ExifMap()\n      data.exifTiffOffset = tiffOffset\n      data.exifLittleEndian = littleEndian\n    }\n    // Parse the tags of the main image directory (IFD0) and retrieve the\n    // offset to the next directory (IFD1), usually the thumbnail directory:\n    dirOffset = parseExifTags(\n      dataView,\n      tiffOffset,\n      tiffOffset + dirOffset,\n      littleEndian,\n      data.exif,\n      data.exifOffsets,\n      includeTags,\n      excludeTags\n    )\n    if (dirOffset && shouldIncludeTag(includeTags, excludeTags, 'ifd1')) {\n      data.exif.ifd1 = dirOffset\n      if (data.exifOffsets) {\n        data.exifOffsets.ifd1 = tiffOffset + dirOffset\n      }\n    }\n    Object.keys(data.exif.ifds).forEach(function (tagCode) {\n      parseExifIFD(\n        data,\n        tagCode,\n        dataView,\n        tiffOffset,\n        littleEndian,\n        includeTags,\n        excludeTags\n      )\n    })\n    thumbnailIFD = data.exif.ifd1\n    // Check for JPEG Thumbnail offset and data length:\n    if (thumbnailIFD && thumbnailIFD[0x0201]) {\n      thumbnailIFD[0x0201] = getExifThumbnail(\n        dataView,\n        tiffOffset + thumbnailIFD[0x0201],\n        thumbnailIFD[0x0202] // Thumbnail data length\n      )\n    }\n  }\n\n  // Registers the Exif parser for the APP1 JPEG metadata segment:\n  loadImage.metaDataParsers.jpeg[0xffe1].push(loadImage.parseExifData)\n\n  loadImage.exifWriters = {\n    // Orientation writer:\n    0x0112: function (buffer, data, value) {\n      var orientationOffset = data.exifOffsets[0x0112]\n      if (!orientationOffset) return buffer\n      var view = new DataView(buffer, orientationOffset + 8, 2)\n      view.setUint16(0, value, data.exifLittleEndian)\n      return buffer\n    }\n  }\n\n  loadImage.writeExifData = function (buffer, data, id, value) {\n    loadImage.exifWriters[data.exif.map[id]](buffer, data, value)\n  }\n\n  loadImage.ExifMap = ExifMap\n\n  // Adds the following properties to the parseMetaData callback data:\n  // - exif: The parsed Exif tags\n  // - exifOffsets: The parsed Exif tag offsets\n  // - exifTiffOffset: TIFF header offset (used for offset pointers)\n  // - exifLittleEndian: little endian order if true, big endian if false\n\n  // Adds the following options to the parseMetaData method:\n  // - disableExif: Disables Exif parsing when true.\n  // - disableExifOffsets: Disables storing Exif tag offsets when true.\n  // - includeExifTags: A map of Exif tags to include for parsing.\n  // - excludeExifTags: A map of Exif tags to exclude from parsing.\n})\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image.js":"/*\n * JavaScript Load Image\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, module, Promise */\n\n;(function ($) {\n  'use strict'\n\n  var urlAPI = $.URL || $.webkitURL\n\n  /**\n   * Creates an object URL for a given File object.\n   *\n   * @param {Blob} blob Blob object\n   * @returns {string|boolean} Returns object URL if API exists, else false.\n   */\n  function createObjectURL(blob) {\n    return urlAPI ? urlAPI.createObjectURL(blob) : false\n  }\n\n  /**\n   * Revokes a given object URL.\n   *\n   * @param {string} url Blob object URL\n   * @returns {undefined|boolean} Returns undefined if API exists, else false.\n   */\n  function revokeObjectURL(url) {\n    return urlAPI ? urlAPI.revokeObjectURL(url) : false\n  }\n\n  /**\n   * Helper function to revoke an object URL\n   *\n   * @param {string} url Blob Object URL\n   * @param {object} [options] Options object\n   */\n  function revokeHelper(url, options) {\n    if (url && url.slice(0, 5) === 'blob:' && !(options && options.noRevoke)) {\n      revokeObjectURL(url)\n    }\n  }\n\n  /**\n   * Loads a given File object via FileReader interface.\n   *\n   * @param {Blob} file Blob object\n   * @param {Function} onload Load event callback\n   * @param {Function} [onerror] Error/Abort event callback\n   * @param {string} [method=readAsDataURL] FileReader method\n   * @returns {FileReader|boolean} Returns FileReader if API exists, else false.\n   */\n  function readFile(file, onload, onerror, method) {\n    if (!$.FileReader) return false\n    var reader = new FileReader()\n    reader.onload = function () {\n      onload.call(reader, this.result)\n    }\n    if (onerror) {\n      reader.onabort = reader.onerror = function () {\n        onerror.call(reader, this.error)\n      }\n    }\n    var readerMethod = reader[method || 'readAsDataURL']\n    if (readerMethod) {\n      readerMethod.call(reader, file)\n      return reader\n    }\n  }\n\n  /**\n   * Cross-frame instanceof check.\n   *\n   * @param {string} type Instance type\n   * @param {object} obj Object instance\n   * @returns {boolean} Returns true if the object is of the given instance.\n   */\n  function isInstanceOf(type, obj) {\n    // Cross-frame instanceof check\n    return Object.prototype.toString.call(obj) === '[object ' + type + ']'\n  }\n\n  /**\n   * @typedef { HTMLImageElement|HTMLCanvasElement } Result\n   */\n\n  /**\n   * Loads an image for a given File object.\n   *\n   * @param {Blob|string} file Blob object or image URL\n   * @param {Function|object} [callback] Image load event callback or options\n   * @param {object} [options] Options object\n   * @returns {HTMLImageElement|FileReader|Promise<Result>} Object\n   */\n  function loadImage(file, callback, options) {\n    /**\n     * Promise executor\n     *\n     * @param {Function} resolve Resolution function\n     * @param {Function} reject Rejection function\n     * @returns {HTMLImageElement|FileReader} Object\n     */\n    function executor(resolve, reject) {\n      var img = document.createElement('img')\n      var url\n      /**\n       * Callback for the fetchBlob call.\n       *\n       * @param {HTMLImageElement|HTMLCanvasElement} img Error object\n       * @param {object} data Data object\n       * @returns {undefined} Undefined\n       */\n      function resolveWrapper(img, data) {\n        if (resolve === reject) {\n          // Not using Promises\n          if (resolve) resolve(img, data)\n          return\n        } else if (img instanceof Error) {\n          reject(img)\n          return\n        }\n        data = data || {} // eslint-disable-line no-param-reassign\n        data.image = img\n        resolve(data)\n      }\n      /**\n       * Callback for the fetchBlob call.\n       *\n       * @param {Blob} blob Blob object\n       * @param {Error} err Error object\n       */\n      function fetchBlobCallback(blob, err) {\n        if (err && $.console) console.log(err) // eslint-disable-line no-console\n        if (blob && isInstanceOf('Blob', blob)) {\n          file = blob // eslint-disable-line no-param-reassign\n          url = createObjectURL(file)\n        } else {\n          url = file\n          if (options && options.crossOrigin) {\n            img.crossOrigin = options.crossOrigin\n          }\n        }\n        img.src = url\n      }\n      img.onerror = function (event) {\n        revokeHelper(url, options)\n        if (reject) reject.call(img, event)\n      }\n      img.onload = function () {\n        revokeHelper(url, options)\n        var data = {\n          originalWidth: img.naturalWidth || img.width,\n          originalHeight: img.naturalHeight || img.height\n        }\n        try {\n          loadImage.transform(img, options, resolveWrapper, file, data)\n        } catch (error) {\n          if (reject) reject(error)\n        }\n      }\n      if (typeof file === 'string') {\n        if (loadImage.requiresMetaData(options)) {\n          loadImage.fetchBlob(file, fetchBlobCallback, options)\n        } else {\n          fetchBlobCallback()\n        }\n        return img\n      } else if (isInstanceOf('Blob', file) || isInstanceOf('File', file)) {\n        url = createObjectURL(file)\n        if (url) {\n          img.src = url\n          return img\n        }\n        return readFile(\n          file,\n          function (url) {\n            img.src = url\n          },\n          reject\n        )\n      }\n    }\n    if ($.Promise && typeof callback !== 'function') {\n      options = callback // eslint-disable-line no-param-reassign\n      return new Promise(executor)\n    }\n    return executor(callback, callback)\n  }\n\n  // Determines if metadata should be loaded automatically.\n  // Requires the load image meta extension to load metadata.\n  loadImage.requiresMetaData = function (options) {\n    return options && options.meta\n  }\n\n  // If the callback given to this function returns a blob, it is used as image\n  // source instead of the original url and overrides the file argument used in\n  // the onload and onerror event callbacks:\n  loadImage.fetchBlob = function (url, callback) {\n    callback()\n  }\n\n  loadImage.transform = function (img, options, callback, file, data) {\n    callback(img, data)\n  }\n\n  loadImage.global = $\n  loadImage.readFile = readFile\n  loadImage.isInstanceOf = isInstanceOf\n  loadImage.createObjectURL = createObjectURL\n  loadImage.revokeObjectURL = revokeObjectURL\n\n  if (typeof define === 'function' && define.amd) {\n    define(function () {\n      return loadImage\n    })\n  } else if (typeof module === 'object' && module.exports) {\n    module.exports = loadImage\n  } else {\n    $.loadImage = loadImage\n  }\n})((typeof window !== 'undefined' && window) || this)\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image-exif-map.js":"/*\n * JavaScript Load Image Exif Map\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2013, Sebastian Tschan\n * https://blueimp.net\n *\n * Exif tags mapping based on\n * https://github.com/jseidelin/exif-js\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, module, require */\n\n;(function (factory) {\n  'use strict'\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery/fileUploader/vendor/blueimp-load-image/js/load-image', 'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-exif'], factory)\n  } else if (typeof module === 'object' && module.exports) {\n    factory(require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'), require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-exif'))\n  } else {\n    // Browser globals:\n    factory(window.loadImage)\n  }\n})(function (loadImage) {\n  'use strict'\n\n  var ExifMapProto = loadImage.ExifMap.prototype\n\n  ExifMapProto.tags = {\n    // =================\n    // TIFF tags (IFD0):\n    // =================\n    0x0100: 'ImageWidth',\n    0x0101: 'ImageHeight',\n    0x0102: 'BitsPerSample',\n    0x0103: 'Compression',\n    0x0106: 'PhotometricInterpretation',\n    0x0112: 'Orientation',\n    0x0115: 'SamplesPerPixel',\n    0x011c: 'PlanarConfiguration',\n    0x0212: 'YCbCrSubSampling',\n    0x0213: 'YCbCrPositioning',\n    0x011a: 'XResolution',\n    0x011b: 'YResolution',\n    0x0128: 'ResolutionUnit',\n    0x0111: 'StripOffsets',\n    0x0116: 'RowsPerStrip',\n    0x0117: 'StripByteCounts',\n    0x0201: 'JPEGInterchangeFormat',\n    0x0202: 'JPEGInterchangeFormatLength',\n    0x012d: 'TransferFunction',\n    0x013e: 'WhitePoint',\n    0x013f: 'PrimaryChromaticities',\n    0x0211: 'YCbCrCoefficients',\n    0x0214: 'ReferenceBlackWhite',\n    0x0132: 'DateTime',\n    0x010e: 'ImageDescription',\n    0x010f: 'Make',\n    0x0110: 'Model',\n    0x0131: 'Software',\n    0x013b: 'Artist',\n    0x8298: 'Copyright',\n    0x8769: {\n      // ExifIFDPointer\n      0x9000: 'ExifVersion', // EXIF version\n      0xa000: 'FlashpixVersion', // Flashpix format version\n      0xa001: 'ColorSpace', // Color space information tag\n      0xa002: 'PixelXDimension', // Valid width of meaningful image\n      0xa003: 'PixelYDimension', // Valid height of meaningful image\n      0xa500: 'Gamma',\n      0x9101: 'ComponentsConfiguration', // Information about channels\n      0x9102: 'CompressedBitsPerPixel', // Compressed bits per pixel\n      0x927c: 'MakerNote', // Any desired information written by the manufacturer\n      0x9286: 'UserComment', // Comments by user\n      0xa004: 'RelatedSoundFile', // Name of related sound file\n      0x9003: 'DateTimeOriginal', // Date and time when the original image was generated\n      0x9004: 'DateTimeDigitized', // Date and time when the image was stored digitally\n      0x9010: 'OffsetTime', // Time zone when the image file was last changed\n      0x9011: 'OffsetTimeOriginal', // Time zone when the image was stored digitally\n      0x9012: 'OffsetTimeDigitized', // Time zone when the image was stored digitally\n      0x9290: 'SubSecTime', // Fractions of seconds for DateTime\n      0x9291: 'SubSecTimeOriginal', // Fractions of seconds for DateTimeOriginal\n      0x9292: 'SubSecTimeDigitized', // Fractions of seconds for DateTimeDigitized\n      0x829a: 'ExposureTime', // Exposure time (in seconds)\n      0x829d: 'FNumber',\n      0x8822: 'ExposureProgram', // Exposure program\n      0x8824: 'SpectralSensitivity', // Spectral sensitivity\n      0x8827: 'PhotographicSensitivity', // EXIF 2.3, ISOSpeedRatings in EXIF 2.2\n      0x8828: 'OECF', // Optoelectric conversion factor\n      0x8830: 'SensitivityType',\n      0x8831: 'StandardOutputSensitivity',\n      0x8832: 'RecommendedExposureIndex',\n      0x8833: 'ISOSpeed',\n      0x8834: 'ISOSpeedLatitudeyyy',\n      0x8835: 'ISOSpeedLatitudezzz',\n      0x9201: 'ShutterSpeedValue', // Shutter speed\n      0x9202: 'ApertureValue', // Lens aperture\n      0x9203: 'BrightnessValue', // Value of brightness\n      0x9204: 'ExposureBias', // Exposure bias\n      0x9205: 'MaxApertureValue', // Smallest F number of lens\n      0x9206: 'SubjectDistance', // Distance to subject in meters\n      0x9207: 'MeteringMode', // Metering mode\n      0x9208: 'LightSource', // Kind of light source\n      0x9209: 'Flash', // Flash status\n      0x9214: 'SubjectArea', // Location and area of main subject\n      0x920a: 'FocalLength', // Focal length of the lens in mm\n      0xa20b: 'FlashEnergy', // Strobe energy in BCPS\n      0xa20c: 'SpatialFrequencyResponse',\n      0xa20e: 'FocalPlaneXResolution', // Number of pixels in width direction per FPRUnit\n      0xa20f: 'FocalPlaneYResolution', // Number of pixels in height direction per FPRUnit\n      0xa210: 'FocalPlaneResolutionUnit', // Unit for measuring the focal plane resolution\n      0xa214: 'SubjectLocation', // Location of subject in image\n      0xa215: 'ExposureIndex', // Exposure index selected on camera\n      0xa217: 'SensingMethod', // Image sensor type\n      0xa300: 'FileSource', // Image source (3 == DSC)\n      0xa301: 'SceneType', // Scene type (1 == directly photographed)\n      0xa302: 'CFAPattern', // Color filter array geometric pattern\n      0xa401: 'CustomRendered', // Special processing\n      0xa402: 'ExposureMode', // Exposure mode\n      0xa403: 'WhiteBalance', // 1 = auto white balance, 2 = manual\n      0xa404: 'DigitalZoomRatio', // Digital zoom ratio\n      0xa405: 'FocalLengthIn35mmFilm',\n      0xa406: 'SceneCaptureType', // Type of scene\n      0xa407: 'GainControl', // Degree of overall image gain adjustment\n      0xa408: 'Contrast', // Direction of contrast processing applied by camera\n      0xa409: 'Saturation', // Direction of saturation processing applied by camera\n      0xa40a: 'Sharpness', // Direction of sharpness processing applied by camera\n      0xa40b: 'DeviceSettingDescription',\n      0xa40c: 'SubjectDistanceRange', // Distance to subject\n      0xa420: 'ImageUniqueID', // Identifier assigned uniquely to each image\n      0xa430: 'CameraOwnerName',\n      0xa431: 'BodySerialNumber',\n      0xa432: 'LensSpecification',\n      0xa433: 'LensMake',\n      0xa434: 'LensModel',\n      0xa435: 'LensSerialNumber'\n    },\n    0x8825: {\n      // GPSInfoIFDPointer\n      0x0000: 'GPSVersionID',\n      0x0001: 'GPSLatitudeRef',\n      0x0002: 'GPSLatitude',\n      0x0003: 'GPSLongitudeRef',\n      0x0004: 'GPSLongitude',\n      0x0005: 'GPSAltitudeRef',\n      0x0006: 'GPSAltitude',\n      0x0007: 'GPSTimeStamp',\n      0x0008: 'GPSSatellites',\n      0x0009: 'GPSStatus',\n      0x000a: 'GPSMeasureMode',\n      0x000b: 'GPSDOP',\n      0x000c: 'GPSSpeedRef',\n      0x000d: 'GPSSpeed',\n      0x000e: 'GPSTrackRef',\n      0x000f: 'GPSTrack',\n      0x0010: 'GPSImgDirectionRef',\n      0x0011: 'GPSImgDirection',\n      0x0012: 'GPSMapDatum',\n      0x0013: 'GPSDestLatitudeRef',\n      0x0014: 'GPSDestLatitude',\n      0x0015: 'GPSDestLongitudeRef',\n      0x0016: 'GPSDestLongitude',\n      0x0017: 'GPSDestBearingRef',\n      0x0018: 'GPSDestBearing',\n      0x0019: 'GPSDestDistanceRef',\n      0x001a: 'GPSDestDistance',\n      0x001b: 'GPSProcessingMethod',\n      0x001c: 'GPSAreaInformation',\n      0x001d: 'GPSDateStamp',\n      0x001e: 'GPSDifferential',\n      0x001f: 'GPSHPositioningError'\n    },\n    0xa005: {\n      // InteroperabilityIFDPointer\n      0x0001: 'InteroperabilityIndex'\n    }\n  }\n\n  // IFD1 directory can contain any IFD0 tags:\n  ExifMapProto.tags.ifd1 = ExifMapProto.tags\n\n  ExifMapProto.stringValues = {\n    ExposureProgram: {\n      0: 'Undefined',\n      1: 'Manual',\n      2: 'Normal program',\n      3: 'Aperture priority',\n      4: 'Shutter priority',\n      5: 'Creative program',\n      6: 'Action program',\n      7: 'Portrait mode',\n      8: 'Landscape mode'\n    },\n    MeteringMode: {\n      0: 'Unknown',\n      1: 'Average',\n      2: 'CenterWeightedAverage',\n      3: 'Spot',\n      4: 'MultiSpot',\n      5: 'Pattern',\n      6: 'Partial',\n      255: 'Other'\n    },\n    LightSource: {\n      0: 'Unknown',\n      1: 'Daylight',\n      2: 'Fluorescent',\n      3: 'Tungsten (incandescent light)',\n      4: 'Flash',\n      9: 'Fine weather',\n      10: 'Cloudy weather',\n      11: 'Shade',\n      12: 'Daylight fluorescent (D 5700 - 7100K)',\n      13: 'Day white fluorescent (N 4600 - 5400K)',\n      14: 'Cool white fluorescent (W 3900 - 4500K)',\n      15: 'White fluorescent (WW 3200 - 3700K)',\n      17: 'Standard light A',\n      18: 'Standard light B',\n      19: 'Standard light C',\n      20: 'D55',\n      21: 'D65',\n      22: 'D75',\n      23: 'D50',\n      24: 'ISO studio tungsten',\n      255: 'Other'\n    },\n    Flash: {\n      0x0000: 'Flash did not fire',\n      0x0001: 'Flash fired',\n      0x0005: 'Strobe return light not detected',\n      0x0007: 'Strobe return light detected',\n      0x0009: 'Flash fired, compulsory flash mode',\n      0x000d: 'Flash fired, compulsory flash mode, return light not detected',\n      0x000f: 'Flash fired, compulsory flash mode, return light detected',\n      0x0010: 'Flash did not fire, compulsory flash mode',\n      0x0018: 'Flash did not fire, auto mode',\n      0x0019: 'Flash fired, auto mode',\n      0x001d: 'Flash fired, auto mode, return light not detected',\n      0x001f: 'Flash fired, auto mode, return light detected',\n      0x0020: 'No flash function',\n      0x0041: 'Flash fired, red-eye reduction mode',\n      0x0045: 'Flash fired, red-eye reduction mode, return light not detected',\n      0x0047: 'Flash fired, red-eye reduction mode, return light detected',\n      0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode',\n      0x004d: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',\n      0x004f: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',\n      0x0059: 'Flash fired, auto mode, red-eye reduction mode',\n      0x005d: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',\n      0x005f: 'Flash fired, auto mode, return light detected, red-eye reduction mode'\n    },\n    SensingMethod: {\n      1: 'Undefined',\n      2: 'One-chip color area sensor',\n      3: 'Two-chip color area sensor',\n      4: 'Three-chip color area sensor',\n      5: 'Color sequential area sensor',\n      7: 'Trilinear sensor',\n      8: 'Color sequential linear sensor'\n    },\n    SceneCaptureType: {\n      0: 'Standard',\n      1: 'Landscape',\n      2: 'Portrait',\n      3: 'Night scene'\n    },\n    SceneType: {\n      1: 'Directly photographed'\n    },\n    CustomRendered: {\n      0: 'Normal process',\n      1: 'Custom process'\n    },\n    WhiteBalance: {\n      0: 'Auto white balance',\n      1: 'Manual white balance'\n    },\n    GainControl: {\n      0: 'None',\n      1: 'Low gain up',\n      2: 'High gain up',\n      3: 'Low gain down',\n      4: 'High gain down'\n    },\n    Contrast: {\n      0: 'Normal',\n      1: 'Soft',\n      2: 'Hard'\n    },\n    Saturation: {\n      0: 'Normal',\n      1: 'Low saturation',\n      2: 'High saturation'\n    },\n    Sharpness: {\n      0: 'Normal',\n      1: 'Soft',\n      2: 'Hard'\n    },\n    SubjectDistanceRange: {\n      0: 'Unknown',\n      1: 'Macro',\n      2: 'Close view',\n      3: 'Distant view'\n    },\n    FileSource: {\n      3: 'DSC'\n    },\n    ComponentsConfiguration: {\n      0: '',\n      1: 'Y',\n      2: 'Cb',\n      3: 'Cr',\n      4: 'R',\n      5: 'G',\n      6: 'B'\n    },\n    Orientation: {\n      1: 'Original',\n      2: 'Horizontal flip',\n      3: 'Rotate 180\u00b0 CCW',\n      4: 'Vertical flip',\n      5: 'Vertical flip + Rotate 90\u00b0 CW',\n      6: 'Rotate 90\u00b0 CW',\n      7: 'Horizontal flip + Rotate 90\u00b0 CW',\n      8: 'Rotate 90\u00b0 CCW'\n    }\n  }\n\n  ExifMapProto.getText = function (name) {\n    var value = this.get(name)\n    switch (name) {\n      case 'LightSource':\n      case 'Flash':\n      case 'MeteringMode':\n      case 'ExposureProgram':\n      case 'SensingMethod':\n      case 'SceneCaptureType':\n      case 'SceneType':\n      case 'CustomRendered':\n      case 'WhiteBalance':\n      case 'GainControl':\n      case 'Contrast':\n      case 'Saturation':\n      case 'Sharpness':\n      case 'SubjectDistanceRange':\n      case 'FileSource':\n      case 'Orientation':\n        return this.stringValues[name][value]\n      case 'ExifVersion':\n      case 'FlashpixVersion':\n        if (!value) return\n        return String.fromCharCode(value[0], value[1], value[2], value[3])\n      case 'ComponentsConfiguration':\n        if (!value) return\n        return (\n          this.stringValues[name][value[0]] +\n          this.stringValues[name][value[1]] +\n          this.stringValues[name][value[2]] +\n          this.stringValues[name][value[3]]\n        )\n      case 'GPSVersionID':\n        if (!value) return\n        return value[0] + '.' + value[1] + '.' + value[2] + '.' + value[3]\n    }\n    return String(value)\n  }\n\n  ExifMapProto.getAll = function () {\n    var map = {}\n    var prop\n    var obj\n    var name\n    for (prop in this) {\n      if (Object.prototype.hasOwnProperty.call(this, prop)) {\n        obj = this[prop]\n        if (obj && obj.getAll) {\n          map[this.ifds[prop].name] = obj.getAll()\n        } else {\n          name = this.tags[prop]\n          if (name) map[name] = this.getText(name)\n        }\n      }\n    }\n    return map\n  }\n\n  ExifMapProto.getName = function (tagCode) {\n    var name = this.tags[tagCode]\n    if (typeof name === 'object') return this.ifds[tagCode].name\n    return name\n  }\n\n  // Extend the map of tag names to tag codes:\n  ;(function () {\n    var tags = ExifMapProto.tags\n    var prop\n    var ifd\n    var subTags\n    // Map the tag names to tags:\n    for (prop in tags) {\n      if (Object.prototype.hasOwnProperty.call(tags, prop)) {\n        ifd = ExifMapProto.ifds[prop]\n        if (ifd) {\n          subTags = tags[prop]\n          for (prop in subTags) {\n            if (Object.prototype.hasOwnProperty.call(subTags, prop)) {\n              ifd.map[subTags[prop]] = Number(prop)\n            }\n          }\n        } else {\n          ExifMapProto.map[tags[prop]] = Number(prop)\n        }\n      }\n    }\n  })()\n})\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image-iptc-map.js":"/*\n * JavaScript Load Image IPTC Map\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2013, Sebastian Tschan\n * Copyright 2018, Dave Bevan\n *\n * IPTC tags mapping based on\n * https://iptc.org/standards/photo-metadata\n * https://exiftool.org/TagNames/IPTC.html\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, module, require */\n\n;(function (factory) {\n  'use strict'\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery/fileUploader/vendor/blueimp-load-image/js/load-image', 'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-iptc'], factory)\n  } else if (typeof module === 'object' && module.exports) {\n    factory(require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'), require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-iptc'))\n  } else {\n    // Browser globals:\n    factory(window.loadImage)\n  }\n})(function (loadImage) {\n  'use strict'\n\n  var IptcMapProto = loadImage.IptcMap.prototype\n\n  IptcMapProto.tags = {\n    0: 'ApplicationRecordVersion',\n    3: 'ObjectTypeReference',\n    4: 'ObjectAttributeReference',\n    5: 'ObjectName',\n    7: 'EditStatus',\n    8: 'EditorialUpdate',\n    10: 'Urgency',\n    12: 'SubjectReference',\n    15: 'Category',\n    20: 'SupplementalCategories',\n    22: 'FixtureIdentifier',\n    25: 'Keywords',\n    26: 'ContentLocationCode',\n    27: 'ContentLocationName',\n    30: 'ReleaseDate',\n    35: 'ReleaseTime',\n    37: 'ExpirationDate',\n    38: 'ExpirationTime',\n    40: 'SpecialInstructions',\n    42: 'ActionAdvised',\n    45: 'ReferenceService',\n    47: 'ReferenceDate',\n    50: 'ReferenceNumber',\n    55: 'DateCreated',\n    60: 'TimeCreated',\n    62: 'DigitalCreationDate',\n    63: 'DigitalCreationTime',\n    65: 'OriginatingProgram',\n    70: 'ProgramVersion',\n    75: 'ObjectCycle',\n    80: 'Byline',\n    85: 'BylineTitle',\n    90: 'City',\n    92: 'Sublocation',\n    95: 'State',\n    100: 'CountryCode',\n    101: 'Country',\n    103: 'OriginalTransmissionReference',\n    105: 'Headline',\n    110: 'Credit',\n    115: 'Source',\n    116: 'CopyrightNotice',\n    118: 'Contact',\n    120: 'Caption',\n    121: 'LocalCaption',\n    122: 'Writer',\n    125: 'RasterizedCaption',\n    130: 'ImageType',\n    131: 'ImageOrientation',\n    135: 'LanguageIdentifier',\n    150: 'AudioType',\n    151: 'AudioSamplingRate',\n    152: 'AudioSamplingResolution',\n    153: 'AudioDuration',\n    154: 'AudioOutcue',\n    184: 'JobID',\n    185: 'MasterDocumentID',\n    186: 'ShortDocumentID',\n    187: 'UniqueDocumentID',\n    188: 'OwnerID',\n    200: 'ObjectPreviewFileFormat',\n    201: 'ObjectPreviewFileVersion',\n    202: 'ObjectPreviewData',\n    221: 'Prefs',\n    225: 'ClassifyState',\n    228: 'SimilarityIndex',\n    230: 'DocumentNotes',\n    231: 'DocumentHistory',\n    232: 'ExifCameraInfo',\n    255: 'CatalogSets'\n  }\n\n  IptcMapProto.stringValues = {\n    10: {\n      0: '0 (reserved)',\n      1: '1 (most urgent)',\n      2: '2',\n      3: '3',\n      4: '4',\n      5: '5 (normal urgency)',\n      6: '6',\n      7: '7',\n      8: '8 (least urgent)',\n      9: '9 (user-defined priority)'\n    },\n    75: {\n      a: 'Morning',\n      b: 'Both Morning and Evening',\n      p: 'Evening'\n    },\n    131: {\n      L: 'Landscape',\n      P: 'Portrait',\n      S: 'Square'\n    }\n  }\n\n  IptcMapProto.getText = function (id) {\n    var value = this.get(id)\n    var tagCode = this.map[id]\n    var stringValue = this.stringValues[tagCode]\n    if (stringValue) return stringValue[value]\n    return String(value)\n  }\n\n  IptcMapProto.getAll = function () {\n    var map = {}\n    var prop\n    var name\n    for (prop in this) {\n      if (Object.prototype.hasOwnProperty.call(this, prop)) {\n        name = this.tags[prop]\n        if (name) map[name] = this.getText(name)\n      }\n    }\n    return map\n  }\n\n  IptcMapProto.getName = function (tagCode) {\n    return this.tags[tagCode]\n  }\n\n  // Extend the map of tag names to tag codes:\n  ;(function () {\n    var tags = IptcMapProto.tags\n    var map = IptcMapProto.map || {}\n    var prop\n    // Map the tag names to tags:\n    for (prop in tags) {\n      if (Object.prototype.hasOwnProperty.call(tags, prop)) {\n        map[tags[prop]] = Number(prop)\n      }\n    }\n  })()\n})\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image-iptc.js":"/*\n * JavaScript Load Image IPTC Parser\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2013, Sebastian Tschan\n * Copyright 2018, Dave Bevan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, module, require, DataView */\n\n;(function (factory) {\n  'use strict'\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery/fileUploader/vendor/blueimp-load-image/js/load-image', 'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta'], factory)\n  } else if (typeof module === 'object' && module.exports) {\n    factory(require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'), require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta'))\n  } else {\n    // Browser globals:\n    factory(window.loadImage)\n  }\n})(function (loadImage) {\n  'use strict'\n\n  /**\n   * IPTC tag map\n   *\n   * @name IptcMap\n   * @class\n   */\n  function IptcMap() {}\n\n  IptcMap.prototype.map = {\n    ObjectName: 5\n  }\n\n  IptcMap.prototype.types = {\n    0: 'Uint16', // ApplicationRecordVersion\n    200: 'Uint16', // ObjectPreviewFileFormat\n    201: 'Uint16', // ObjectPreviewFileVersion\n    202: 'binary' // ObjectPreviewData\n  }\n\n  /**\n   * Retrieves IPTC tag value\n   *\n   * @param {number|string} id IPTC tag code or name\n   * @returns {object} IPTC tag value\n   */\n  IptcMap.prototype.get = function (id) {\n    return this[id] || this[this.map[id]]\n  }\n\n  /**\n   * Retrieves string for the given DataView and range\n   *\n   * @param {DataView} dataView Data view interface\n   * @param {number} offset Offset start\n   * @param {number} length Offset length\n   * @returns {string} String value\n   */\n  function getStringValue(dataView, offset, length) {\n    var outstr = ''\n    var end = offset + length\n    for (var n = offset; n < end; n += 1) {\n      outstr += String.fromCharCode(dataView.getUint8(n))\n    }\n    return outstr\n  }\n\n  /**\n   * Retrieves tag value for the given DataView and range\n   *\n   * @param {number} tagCode tag code\n   * @param {IptcMap} map IPTC tag map\n   * @param {DataView} dataView Data view interface\n   * @param {number} offset Range start\n   * @param {number} length Range length\n   * @returns {object} Tag value\n   */\n  function getTagValue(tagCode, map, dataView, offset, length) {\n    if (map.types[tagCode] === 'binary') {\n      return new Blob([dataView.buffer.slice(offset, offset + length)])\n    }\n    if (map.types[tagCode] === 'Uint16') {\n      return dataView.getUint16(offset)\n    }\n    return getStringValue(dataView, offset, length)\n  }\n\n  /**\n   * Combines IPTC value with existing ones.\n   *\n   * @param {object} value Existing IPTC field value\n   * @param {object} newValue New IPTC field value\n   * @returns {object} Resulting IPTC field value\n   */\n  function combineTagValues(value, newValue) {\n    if (value === undefined) return newValue\n    if (value instanceof Array) {\n      value.push(newValue)\n      return value\n    }\n    return [value, newValue]\n  }\n\n  /**\n   * Parses IPTC tags.\n   *\n   * @param {DataView} dataView Data view interface\n   * @param {number} segmentOffset Segment offset\n   * @param {number} segmentLength Segment length\n   * @param {object} data Data export object\n   * @param {object} includeTags Map of tags to include\n   * @param {object} excludeTags Map of tags to exclude\n   */\n  function parseIptcTags(\n    dataView,\n    segmentOffset,\n    segmentLength,\n    data,\n    includeTags,\n    excludeTags\n  ) {\n    var value, tagSize, tagCode\n    var segmentEnd = segmentOffset + segmentLength\n    var offset = segmentOffset\n    while (offset < segmentEnd) {\n      if (\n        dataView.getUint8(offset) === 0x1c && // tag marker\n        dataView.getUint8(offset + 1) === 0x02 // record number, only handles v2\n      ) {\n        tagCode = dataView.getUint8(offset + 2)\n        if (\n          (!includeTags || includeTags[tagCode]) &&\n          (!excludeTags || !excludeTags[tagCode])\n        ) {\n          tagSize = dataView.getInt16(offset + 3)\n          value = getTagValue(tagCode, data.iptc, dataView, offset + 5, tagSize)\n          data.iptc[tagCode] = combineTagValues(data.iptc[tagCode], value)\n          if (data.iptcOffsets) {\n            data.iptcOffsets[tagCode] = offset\n          }\n        }\n      }\n      offset += 1\n    }\n  }\n\n  /**\n   * Tests if field segment starts at offset.\n   *\n   * @param {DataView} dataView Data view interface\n   * @param {number} offset Segment offset\n   * @returns {boolean} True if '8BIM<EOT><EOT>' exists at offset\n   */\n  function isSegmentStart(dataView, offset) {\n    return (\n      dataView.getUint32(offset) === 0x3842494d && // Photoshop segment start\n      dataView.getUint16(offset + 4) === 0x0404 // IPTC segment start\n    )\n  }\n\n  /**\n   * Returns header length.\n   *\n   * @param {DataView} dataView Data view interface\n   * @param {number} offset Segment offset\n   * @returns {number} Header length\n   */\n  function getHeaderLength(dataView, offset) {\n    var length = dataView.getUint8(offset + 7)\n    if (length % 2 !== 0) length += 1\n    // Check for pre photoshop 6 format\n    if (length === 0) {\n      // Always 4\n      length = 4\n    }\n    return length\n  }\n\n  loadImage.parseIptcData = function (dataView, offset, length, data, options) {\n    if (options.disableIptc) {\n      return\n    }\n    var markerLength = offset + length\n    while (offset + 8 < markerLength) {\n      if (isSegmentStart(dataView, offset)) {\n        var headerLength = getHeaderLength(dataView, offset)\n        var segmentOffset = offset + 8 + headerLength\n        if (segmentOffset > markerLength) {\n          // eslint-disable-next-line no-console\n          console.log('Invalid IPTC data: Invalid segment offset.')\n          break\n        }\n        var segmentLength = dataView.getUint16(offset + 6 + headerLength)\n        if (offset + segmentLength > markerLength) {\n          // eslint-disable-next-line no-console\n          console.log('Invalid IPTC data: Invalid segment size.')\n          break\n        }\n        // Create the iptc object to store the tags:\n        data.iptc = new IptcMap()\n        if (!options.disableIptcOffsets) {\n          data.iptcOffsets = new IptcMap()\n        }\n        parseIptcTags(\n          dataView,\n          segmentOffset,\n          segmentLength,\n          data,\n          options.includeIptcTags,\n          options.excludeIptcTags || { 202: true } // ObjectPreviewData\n        )\n        return\n      }\n      // eslint-disable-next-line no-param-reassign\n      offset += 1\n    }\n  }\n\n  // Registers this IPTC parser for the APP13 JPEG metadata segment:\n  loadImage.metaDataParsers.jpeg[0xffed].push(loadImage.parseIptcData)\n\n  loadImage.IptcMap = IptcMap\n\n  // Adds the following properties to the parseMetaData callback data:\n  // - iptc: The iptc tags, parsed by the parseIptcData method\n\n  // Adds the following options to the parseMetaData method:\n  // - disableIptc: Disables IPTC parsing when true.\n  // - disableIptcOffsets: Disables storing IPTC tag offsets when true.\n  // - includeIptcTags: A map of IPTC tags to include for parsing.\n  // - excludeIptcTags: A map of IPTC tags to exclude from parsing.\n})\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image-orientation.js":"/*\n * JavaScript Load Image Orientation\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2013, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/*\nExif orientation values to correctly display the letter F:\n\n    1             2\n  \u2588\u2588\u2588\u2588\u2588\u2588        \u2588\u2588\u2588\u2588\u2588\u2588\n  \u2588\u2588                \u2588\u2588\n  \u2588\u2588\u2588\u2588            \u2588\u2588\u2588\u2588\n  \u2588\u2588                \u2588\u2588\n  \u2588\u2588                \u2588\u2588\n\n    3             4\n      \u2588\u2588        \u2588\u2588\n      \u2588\u2588        \u2588\u2588\n    \u2588\u2588\u2588\u2588        \u2588\u2588\u2588\u2588\n      \u2588\u2588        \u2588\u2588\n  \u2588\u2588\u2588\u2588\u2588\u2588        \u2588\u2588\u2588\u2588\u2588\u2588\n\n    5             6\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588    \u2588\u2588\n\u2588\u2588  \u2588\u2588        \u2588\u2588  \u2588\u2588\n\u2588\u2588            \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\n\n    7             8\n        \u2588\u2588    \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\n    \u2588\u2588  \u2588\u2588        \u2588\u2588  \u2588\u2588\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588            \u2588\u2588\n\n*/\n\n/* global define, module, require */\n\n;(function (factory) {\n  'use strict'\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery/fileUploader/vendor/blueimp-load-image/js/load-image', 'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-scale', 'jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta'], factory)\n  } else if (typeof module === 'object' && module.exports) {\n    factory(\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-scale'),\n      require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta')\n    )\n  } else {\n    // Browser globals:\n    factory(window.loadImage)\n  }\n})(function (loadImage) {\n  'use strict'\n\n  var originalTransform = loadImage.transform\n  var originalRequiresCanvas = loadImage.requiresCanvas\n  var originalRequiresMetaData = loadImage.requiresMetaData\n  var originalTransformCoordinates = loadImage.transformCoordinates\n  var originalGetTransformedOptions = loadImage.getTransformedOptions\n\n  ;(function ($) {\n    // Guard for non-browser environments (e.g. server-side rendering):\n    if (!$.global.document) return\n    // black+white 3x2 JPEG, with the following meta information set:\n    // - EXIF Orientation: 6 (Rotated 90\u00b0 CCW)\n    // Image data layout (B=black, F=white):\n    // BFF\n    // BBB\n    var testImageURL =\n      'data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' +\n      'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' +\n      'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' +\n      'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAIAAwMBEQACEQEDEQH/x' +\n      'ABRAAEAAAAAAAAAAAAAAAAAAAAKEAEBAQADAQEAAAAAAAAAAAAGBQQDCAkCBwEBAAAAAAA' +\n      'AAAAAAAAAAAAAABEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AG8T9NfSMEVMhQ' +\n      'voP3fFiRZ+MTHDifa/95OFSZU5OzRzxkyejv8ciEfhSceSXGjS8eSdLnZc2HDm4M3BxcXw' +\n      'H/9k='\n    var img = document.createElement('img')\n    img.onload = function () {\n      // Check if the browser supports automatic image orientation:\n      $.orientation = img.width === 2 && img.height === 3\n      if ($.orientation) {\n        var canvas = $.createCanvas(1, 1, true)\n        var ctx = canvas.getContext('2d')\n        ctx.drawImage(img, 1, 1, 1, 1, 0, 0, 1, 1)\n        // Check if the source image coordinates (sX, sY, sWidth, sHeight) are\n        // correctly applied to the auto-orientated image, which should result\n        // in a white opaque pixel (e.g. in Safari).\n        // Browsers that show a transparent pixel (e.g. Chromium) fail to crop\n        // auto-oriented images correctly and require a workaround, e.g.\n        // drawing the complete source image to an intermediate canvas first.\n        // See https://bugs.chromium.org/p/chromium/issues/detail?id=1074354\n        $.orientationCropBug =\n          ctx.getImageData(0, 0, 1, 1).data.toString() !== '255,255,255,255'\n      }\n    }\n    img.src = testImageURL\n  })(loadImage)\n\n  /**\n   * Determines if the orientation requires a canvas element.\n   *\n   * @param {object} [options] Options object\n   * @param {boolean} [withMetaData] Is metadata required for orientation\n   * @returns {boolean} Returns true if orientation requires canvas/meta\n   */\n  function requiresCanvasOrientation(options, withMetaData) {\n    var orientation = options && options.orientation\n    return (\n      // Exif orientation for browsers without automatic image orientation:\n      (orientation === true && !loadImage.orientation) ||\n      // Orientation reset for browsers with automatic image orientation:\n      (orientation === 1 && loadImage.orientation) ||\n      // Orientation to defined value, requires meta for orientation reset only:\n      ((!withMetaData || loadImage.orientation) &&\n        orientation > 1 &&\n        orientation < 9)\n    )\n  }\n\n  /**\n   * Determines if the image requires an orientation change.\n   *\n   * @param {number} [orientation] Defined orientation value\n   * @param {number} [autoOrientation] Auto-orientation based on Exif data\n   * @returns {boolean} Returns true if an orientation change is required\n   */\n  function requiresOrientationChange(orientation, autoOrientation) {\n    return (\n      orientation !== autoOrientation &&\n      ((orientation === 1 && autoOrientation > 1 && autoOrientation < 9) ||\n        (orientation > 1 && orientation < 9))\n    )\n  }\n\n  /**\n   * Determines orientation combinations that require a rotation by 180\u00b0.\n   *\n   * The following is a list of combinations that return true:\n   *\n   * 2 (flip) => 5 (rot90,flip), 7 (rot90,flip), 6 (rot90), 8 (rot90)\n   * 4 (flip) => 5 (rot90,flip), 7 (rot90,flip), 6 (rot90), 8 (rot90)\n   *\n   * 5 (rot90,flip) => 2 (flip), 4 (flip), 6 (rot90), 8 (rot90)\n   * 7 (rot90,flip) => 2 (flip), 4 (flip), 6 (rot90), 8 (rot90)\n   *\n   * 6 (rot90) => 2 (flip), 4 (flip), 5 (rot90,flip), 7 (rot90,flip)\n   * 8 (rot90) => 2 (flip), 4 (flip), 5 (rot90,flip), 7 (rot90,flip)\n   *\n   * @param {number} [orientation] Defined orientation value\n   * @param {number} [autoOrientation] Auto-orientation based on Exif data\n   * @returns {boolean} Returns true if rotation by 180\u00b0 is required\n   */\n  function requiresRot180(orientation, autoOrientation) {\n    if (autoOrientation > 1 && autoOrientation < 9) {\n      switch (orientation) {\n        case 2:\n        case 4:\n          return autoOrientation > 4\n        case 5:\n        case 7:\n          return autoOrientation % 2 === 0\n        case 6:\n        case 8:\n          return (\n            autoOrientation === 2 ||\n            autoOrientation === 4 ||\n            autoOrientation === 5 ||\n            autoOrientation === 7\n          )\n      }\n    }\n    return false\n  }\n\n  // Determines if the target image should be a canvas element:\n  loadImage.requiresCanvas = function (options) {\n    return (\n      requiresCanvasOrientation(options) ||\n      originalRequiresCanvas.call(loadImage, options)\n    )\n  }\n\n  // Determines if metadata should be loaded automatically:\n  loadImage.requiresMetaData = function (options) {\n    return (\n      requiresCanvasOrientation(options, true) ||\n      originalRequiresMetaData.call(loadImage, options)\n    )\n  }\n\n  loadImage.transform = function (img, options, callback, file, data) {\n    originalTransform.call(\n      loadImage,\n      img,\n      options,\n      function (img, data) {\n        if (data) {\n          var autoOrientation =\n            loadImage.orientation && data.exif && data.exif.get('Orientation')\n          if (autoOrientation > 4 && autoOrientation < 9) {\n            // Automatic image orientation switched image dimensions\n            var originalWidth = data.originalWidth\n            var originalHeight = data.originalHeight\n            data.originalWidth = originalHeight\n            data.originalHeight = originalWidth\n          }\n        }\n        callback(img, data)\n      },\n      file,\n      data\n    )\n  }\n\n  // Transforms coordinate and dimension options\n  // based on the given orientation option:\n  loadImage.getTransformedOptions = function (img, opts, data) {\n    var options = originalGetTransformedOptions.call(loadImage, img, opts)\n    var exifOrientation = data.exif && data.exif.get('Orientation')\n    var orientation = options.orientation\n    var autoOrientation = loadImage.orientation && exifOrientation\n    if (orientation === true) orientation = exifOrientation\n    if (!requiresOrientationChange(orientation, autoOrientation)) {\n      return options\n    }\n    var top = options.top\n    var right = options.right\n    var bottom = options.bottom\n    var left = options.left\n    var newOptions = {}\n    for (var i in options) {\n      if (Object.prototype.hasOwnProperty.call(options, i)) {\n        newOptions[i] = options[i]\n      }\n    }\n    newOptions.orientation = orientation\n    if (\n      (orientation > 4 && !(autoOrientation > 4)) ||\n      (orientation < 5 && autoOrientation > 4)\n    ) {\n      // Image dimensions and target dimensions are switched\n      newOptions.maxWidth = options.maxHeight\n      newOptions.maxHeight = options.maxWidth\n      newOptions.minWidth = options.minHeight\n      newOptions.minHeight = options.minWidth\n      newOptions.sourceWidth = options.sourceHeight\n      newOptions.sourceHeight = options.sourceWidth\n    }\n    if (autoOrientation > 1) {\n      // Browsers which correctly apply source image coordinates to\n      // auto-oriented images\n      switch (autoOrientation) {\n        case 2:\n          // Horizontal flip\n          right = options.left\n          left = options.right\n          break\n        case 3:\n          // 180\u00b0 Rotate CCW\n          top = options.bottom\n          right = options.left\n          bottom = options.top\n          left = options.right\n          break\n        case 4:\n          // Vertical flip\n          top = options.bottom\n          bottom = options.top\n          break\n        case 5:\n          // Horizontal flip + 90\u00b0 Rotate CCW\n          top = options.left\n          right = options.bottom\n          bottom = options.right\n          left = options.top\n          break\n        case 6:\n          // 90\u00b0 Rotate CCW\n          top = options.left\n          right = options.top\n          bottom = options.right\n          left = options.bottom\n          break\n        case 7:\n          // Vertical flip + 90\u00b0 Rotate CCW\n          top = options.right\n          right = options.top\n          bottom = options.left\n          left = options.bottom\n          break\n        case 8:\n          // 90\u00b0 Rotate CW\n          top = options.right\n          right = options.bottom\n          bottom = options.left\n          left = options.top\n          break\n      }\n      // Some orientation combinations require additional rotation by 180\u00b0:\n      if (requiresRot180(orientation, autoOrientation)) {\n        var tmpTop = top\n        var tmpRight = right\n        top = bottom\n        right = left\n        bottom = tmpTop\n        left = tmpRight\n      }\n    }\n    newOptions.top = top\n    newOptions.right = right\n    newOptions.bottom = bottom\n    newOptions.left = left\n    // Account for defined browser orientation:\n    switch (orientation) {\n      case 2:\n        // Horizontal flip\n        newOptions.right = left\n        newOptions.left = right\n        break\n      case 3:\n        // 180\u00b0 Rotate CCW\n        newOptions.top = bottom\n        newOptions.right = left\n        newOptions.bottom = top\n        newOptions.left = right\n        break\n      case 4:\n        // Vertical flip\n        newOptions.top = bottom\n        newOptions.bottom = top\n        break\n      case 5:\n        // Vertical flip + 90\u00b0 Rotate CW\n        newOptions.top = left\n        newOptions.right = bottom\n        newOptions.bottom = right\n        newOptions.left = top\n        break\n      case 6:\n        // 90\u00b0 Rotate CW\n        newOptions.top = right\n        newOptions.right = bottom\n        newOptions.bottom = left\n        newOptions.left = top\n        break\n      case 7:\n        // Horizontal flip + 90\u00b0 Rotate CW\n        newOptions.top = right\n        newOptions.right = top\n        newOptions.bottom = left\n        newOptions.left = bottom\n        break\n      case 8:\n        // 90\u00b0 Rotate CCW\n        newOptions.top = left\n        newOptions.right = top\n        newOptions.bottom = right\n        newOptions.left = bottom\n        break\n    }\n    return newOptions\n  }\n\n  // Transform image orientation based on the given EXIF orientation option:\n  loadImage.transformCoordinates = function (canvas, options, data) {\n    originalTransformCoordinates.call(loadImage, canvas, options, data)\n    var orientation = options.orientation\n    var autoOrientation =\n      loadImage.orientation && data.exif && data.exif.get('Orientation')\n    if (!requiresOrientationChange(orientation, autoOrientation)) {\n      return\n    }\n    var ctx = canvas.getContext('2d')\n    var width = canvas.width\n    var height = canvas.height\n    var sourceWidth = width\n    var sourceHeight = height\n    if (\n      (orientation > 4 && !(autoOrientation > 4)) ||\n      (orientation < 5 && autoOrientation > 4)\n    ) {\n      // Image dimensions and target dimensions are switched\n      canvas.width = height\n      canvas.height = width\n    }\n    if (orientation > 4) {\n      // Destination and source dimensions are switched\n      sourceWidth = height\n      sourceHeight = width\n    }\n    // Reset automatic browser orientation:\n    switch (autoOrientation) {\n      case 2:\n        // Horizontal flip\n        ctx.translate(sourceWidth, 0)\n        ctx.scale(-1, 1)\n        break\n      case 3:\n        // 180\u00b0 Rotate CCW\n        ctx.translate(sourceWidth, sourceHeight)\n        ctx.rotate(Math.PI)\n        break\n      case 4:\n        // Vertical flip\n        ctx.translate(0, sourceHeight)\n        ctx.scale(1, -1)\n        break\n      case 5:\n        // Horizontal flip + 90\u00b0 Rotate CCW\n        ctx.rotate(-0.5 * Math.PI)\n        ctx.scale(-1, 1)\n        break\n      case 6:\n        // 90\u00b0 Rotate CCW\n        ctx.rotate(-0.5 * Math.PI)\n        ctx.translate(-sourceWidth, 0)\n        break\n      case 7:\n        // Vertical flip + 90\u00b0 Rotate CCW\n        ctx.rotate(-0.5 * Math.PI)\n        ctx.translate(-sourceWidth, sourceHeight)\n        ctx.scale(1, -1)\n        break\n      case 8:\n        // 90\u00b0 Rotate CW\n        ctx.rotate(0.5 * Math.PI)\n        ctx.translate(0, -sourceHeight)\n        break\n    }\n    // Some orientation combinations require additional rotation by 180\u00b0:\n    if (requiresRot180(orientation, autoOrientation)) {\n      ctx.translate(sourceWidth, sourceHeight)\n      ctx.rotate(Math.PI)\n    }\n    switch (orientation) {\n      case 2:\n        // Horizontal flip\n        ctx.translate(width, 0)\n        ctx.scale(-1, 1)\n        break\n      case 3:\n        // 180\u00b0 Rotate CCW\n        ctx.translate(width, height)\n        ctx.rotate(Math.PI)\n        break\n      case 4:\n        // Vertical flip\n        ctx.translate(0, height)\n        ctx.scale(1, -1)\n        break\n      case 5:\n        // Vertical flip + 90\u00b0 Rotate CW\n        ctx.rotate(0.5 * Math.PI)\n        ctx.scale(1, -1)\n        break\n      case 6:\n        // 90\u00b0 Rotate CW\n        ctx.rotate(0.5 * Math.PI)\n        ctx.translate(0, -height)\n        break\n      case 7:\n        // Horizontal flip + 90\u00b0 Rotate CW\n        ctx.rotate(0.5 * Math.PI)\n        ctx.translate(width, -height)\n        ctx.scale(-1, 1)\n        break\n      case 8:\n        // 90\u00b0 Rotate CCW\n        ctx.rotate(-0.5 * Math.PI)\n        ctx.translate(-width, 0)\n        break\n    }\n  }\n})\n","jquery/fileUploader/vendor/blueimp-load-image/js/index.js":"/* global module, require */\n\nmodule.exports = require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image')\n\nrequire('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-scale')\nrequire('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta')\nrequire('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-fetch')\nrequire('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-exif')\nrequire('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-exif-map')\nrequire('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-iptc')\nrequire('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-iptc-map')\nrequire('jquery/fileUploader/vendor/blueimp-load-image/js/load-image-orientation')\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image-fetch.js":"/*\n * JavaScript Load Image Fetch\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2017, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, module, require, Promise */\n\n;(function (factory) {\n  'use strict'\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery/fileUploader/vendor/blueimp-load-image/js/load-image'], factory)\n  } else if (typeof module === 'object' && module.exports) {\n    factory(require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'))\n  } else {\n    // Browser globals:\n    factory(window.loadImage)\n  }\n})(function (loadImage) {\n  'use strict'\n\n  var global = loadImage.global\n\n  if (\n    global.fetch &&\n    global.Request &&\n    global.Response &&\n    global.Response.prototype.blob\n  ) {\n    loadImage.fetchBlob = function (url, callback, options) {\n      /**\n       * Fetch response handler.\n       *\n       * @param {Response} response Fetch response\n       * @returns {Blob} Fetched Blob.\n       */\n      function responseHandler(response) {\n        return response.blob()\n      }\n      if (global.Promise && typeof callback !== 'function') {\n        return fetch(new Request(url, callback)).then(responseHandler)\n      }\n      fetch(new Request(url, options))\n        .then(responseHandler)\n        .then(callback)\n        [\n          // Avoid parsing error in IE<9, where catch is a reserved word.\n          // eslint-disable-next-line dot-notation\n          'catch'\n        ](function (err) {\n          callback(null, err)\n        })\n    }\n  } else if (\n    global.XMLHttpRequest &&\n    // https://xhr.spec.whatwg.org/#the-responsetype-attribute\n    new XMLHttpRequest().responseType === ''\n  ) {\n    loadImage.fetchBlob = function (url, callback, options) {\n      /**\n       * Promise executor\n       *\n       * @param {Function} resolve Resolution function\n       * @param {Function} reject Rejection function\n       */\n      function executor(resolve, reject) {\n        options = options || {} // eslint-disable-line no-param-reassign\n        var req = new XMLHttpRequest()\n        req.open(options.method || 'GET', url)\n        if (options.headers) {\n          Object.keys(options.headers).forEach(function (key) {\n            req.setRequestHeader(key, options.headers[key])\n          })\n        }\n        req.withCredentials = options.credentials === 'include'\n        req.responseType = 'blob'\n        req.onload = function () {\n          resolve(req.response)\n        }\n        req.onerror = req.onabort = req.ontimeout = function (err) {\n          if (resolve === reject) {\n            // Not using Promises\n            reject(null, err)\n          } else {\n            reject(err)\n          }\n        }\n        req.send(options.body)\n      }\n      if (global.Promise && typeof callback !== 'function') {\n        options = callback // eslint-disable-line no-param-reassign\n        return new Promise(executor)\n      }\n      return executor(callback, callback)\n    }\n  }\n})\n","jquery/fileUploader/vendor/blueimp-load-image/js/load-image-meta.js":"/*\n * JavaScript Load Image Meta\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2013, Sebastian Tschan\n * https://blueimp.net\n *\n * Image metadata handling implementation\n * based on the help and contribution of\n * Achim St\u00f6hr.\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, module, require, Promise, DataView, Uint8Array, ArrayBuffer */\n\n;(function (factory) {\n  'use strict'\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery/fileUploader/vendor/blueimp-load-image/js/load-image'], factory)\n  } else if (typeof module === 'object' && module.exports) {\n    factory(require('jquery/fileUploader/vendor/blueimp-load-image/js/load-image'))\n  } else {\n    // Browser globals:\n    factory(window.loadImage)\n  }\n})(function (loadImage) {\n  'use strict'\n\n  var global = loadImage.global\n  var originalTransform = loadImage.transform\n\n  var blobSlice =\n    global.Blob &&\n    (Blob.prototype.slice ||\n      Blob.prototype.webkitSlice ||\n      Blob.prototype.mozSlice)\n\n  var bufferSlice =\n    (global.ArrayBuffer && ArrayBuffer.prototype.slice) ||\n    function (begin, end) {\n      // Polyfill for IE10, which does not support ArrayBuffer.slice\n      // eslint-disable-next-line no-param-reassign\n      end = end || this.byteLength - begin\n      var arr1 = new Uint8Array(this, begin, end)\n      var arr2 = new Uint8Array(end)\n      arr2.set(arr1)\n      return arr2.buffer\n    }\n\n  var metaDataParsers = {\n    jpeg: {\n      0xffe1: [], // APP1 marker\n      0xffed: [] // APP13 marker\n    }\n  }\n\n  /**\n   * Parses image metadata and calls the callback with an object argument\n   * with the following property:\n   * - imageHead: The complete image head as ArrayBuffer\n   * The options argument accepts an object and supports the following\n   * properties:\n   * - maxMetaDataSize: Defines the maximum number of bytes to parse.\n   * - disableImageHead: Disables creating the imageHead property.\n   *\n   * @param {Blob} file Blob object\n   * @param {Function} [callback] Callback function\n   * @param {object} [options] Parsing options\n   * @param {object} [data] Result data object\n   * @returns {Promise<object>|undefined} Returns Promise if no callback given.\n   */\n  function parseMetaData(file, callback, options, data) {\n    var that = this\n    /**\n     * Promise executor\n     *\n     * @param {Function} resolve Resolution function\n     * @param {Function} reject Rejection function\n     * @returns {undefined} Undefined\n     */\n    function executor(resolve, reject) {\n      if (\n        !(\n          global.DataView &&\n          blobSlice &&\n          file &&\n          file.size >= 12 &&\n          file.type === 'image/jpeg'\n        )\n      ) {\n        // Nothing to parse\n        return resolve(data)\n      }\n      // 256 KiB should contain all EXIF/ICC/IPTC segments:\n      var maxMetaDataSize = options.maxMetaDataSize || 262144\n      if (\n        !loadImage.readFile(\n          blobSlice.call(file, 0, maxMetaDataSize),\n          function (buffer) {\n            // Note on endianness:\n            // Since the marker and length bytes in JPEG files are always\n            // stored in big endian order, we can leave the endian parameter\n            // of the DataView methods undefined, defaulting to big endian.\n            var dataView = new DataView(buffer)\n            // Check for the JPEG marker (0xffd8):\n            if (dataView.getUint16(0) !== 0xffd8) {\n              return reject(\n                new Error('Invalid JPEG file: Missing JPEG marker.')\n              )\n            }\n            var offset = 2\n            var maxOffset = dataView.byteLength - 4\n            var headLength = offset\n            var markerBytes\n            var markerLength\n            var parsers\n            var i\n            while (offset < maxOffset) {\n              markerBytes = dataView.getUint16(offset)\n              // Search for APPn (0xffeN) and COM (0xfffe) markers,\n              // which contain application-specific metadata like\n              // Exif, ICC and IPTC data and text comments:\n              if (\n                (markerBytes >= 0xffe0 && markerBytes <= 0xffef) ||\n                markerBytes === 0xfffe\n              ) {\n                // The marker bytes (2) are always followed by\n                // the length bytes (2), indicating the length of the\n                // marker segment, which includes the length bytes,\n                // but not the marker bytes, so we add 2:\n                markerLength = dataView.getUint16(offset + 2) + 2\n                if (offset + markerLength > dataView.byteLength) {\n                  // eslint-disable-next-line no-console\n                  console.log('Invalid JPEG metadata: Invalid segment size.')\n                  break\n                }\n                parsers = metaDataParsers.jpeg[markerBytes]\n                if (parsers && !options.disableMetaDataParsers) {\n                  for (i = 0; i < parsers.length; i += 1) {\n                    parsers[i].call(\n                      that,\n                      dataView,\n                      offset,\n                      markerLength,\n                      data,\n                      options\n                    )\n                  }\n                }\n                offset += markerLength\n                headLength = offset\n              } else {\n                // Not an APPn or COM marker, probably safe to\n                // assume that this is the end of the metadata\n                break\n              }\n            }\n            // Meta length must be longer than JPEG marker (2)\n            // plus APPn marker (2), followed by length bytes (2):\n            if (!options.disableImageHead && headLength > 6) {\n              data.imageHead = bufferSlice.call(buffer, 0, headLength)\n            }\n            resolve(data)\n          },\n          reject,\n          'readAsArrayBuffer'\n        )\n      ) {\n        // No support for the FileReader interface, nothing to parse\n        resolve(data)\n      }\n    }\n    options = options || {} // eslint-disable-line no-param-reassign\n    if (global.Promise && typeof callback !== 'function') {\n      options = callback || {} // eslint-disable-line no-param-reassign\n      data = options // eslint-disable-line no-param-reassign\n      return new Promise(executor)\n    }\n    data = data || {} // eslint-disable-line no-param-reassign\n    return executor(callback, callback)\n  }\n\n  /**\n   * Replaces the head of a JPEG Blob\n   *\n   * @param {Blob} blob Blob object\n   * @param {ArrayBuffer} oldHead Old JPEG head\n   * @param {ArrayBuffer} newHead New JPEG head\n   * @returns {Blob} Combined Blob\n   */\n  function replaceJPEGHead(blob, oldHead, newHead) {\n    if (!blob || !oldHead || !newHead) return null\n    return new Blob([newHead, blobSlice.call(blob, oldHead.byteLength)], {\n      type: 'image/jpeg'\n    })\n  }\n\n  /**\n   * Replaces the image head of a JPEG blob with the given one.\n   * Returns a Promise or calls the callback with the new Blob.\n   *\n   * @param {Blob} blob Blob object\n   * @param {ArrayBuffer} head New JPEG head\n   * @param {Function} [callback] Callback function\n   * @returns {Promise<Blob|null>|undefined} Combined Blob\n   */\n  function replaceHead(blob, head, callback) {\n    var options = { maxMetaDataSize: 256, disableMetaDataParsers: true }\n    if (!callback && global.Promise) {\n      return parseMetaData(blob, options).then(function (data) {\n        return replaceJPEGHead(blob, data.imageHead, head)\n      })\n    }\n    parseMetaData(\n      blob,\n      function (data) {\n        callback(replaceJPEGHead(blob, data.imageHead, head))\n      },\n      options\n    )\n  }\n\n  loadImage.transform = function (img, options, callback, file, data) {\n    if (loadImage.requiresMetaData(options)) {\n      data = data || {} // eslint-disable-line no-param-reassign\n      parseMetaData(\n        file,\n        function (result) {\n          if (result !== data) {\n            // eslint-disable-next-line no-console\n            if (global.console) console.log(result)\n            result = data // eslint-disable-line no-param-reassign\n          }\n          originalTransform.call(\n            loadImage,\n            img,\n            options,\n            callback,\n            file,\n            result\n          )\n        },\n        options,\n        data\n      )\n    } else {\n      originalTransform.apply(loadImage, arguments)\n    }\n  }\n\n  loadImage.blobSlice = blobSlice\n  loadImage.bufferSlice = bufferSlice\n  loadImage.replaceHead = replaceHead\n  loadImage.parseMetaData = parseMetaData\n  loadImage.metaDataParsers = metaDataParsers\n})\n","jquery/fileUploader/vendor/blueimp-tmpl/js/tmpl.js":"/*\n * JavaScript Templates\n * https://github.com/blueimp/JavaScript-Templates\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n *\n * Inspired by John Resig's JavaScript Micro-Templating:\n * http://ejohn.org/blog/javascript-micro-templating/\n */\n\n/* global define */\n\n/* eslint-disable strict */\n\n;(function ($) {\n  'use strict'\n  var tmpl = function (str, data) {\n    var f = !/[^\\w\\-.:]/.test(str)\n      ? (tmpl.cache[str] = tmpl.cache[str] || tmpl(tmpl.load(str)))\n      : new Function( // eslint-disable-line no-new-func\n          tmpl.arg + ',tmpl',\n          'var _e=tmpl.encode' +\n            tmpl.helper +\n            \",_s='\" +\n            str.replace(tmpl.regexp, tmpl.func) +\n            \"';return _s;\"\n        )\n    return data\n      ? f(data, tmpl)\n      : function (data) {\n          return f(data, tmpl)\n        }\n  }\n  tmpl.cache = {}\n  tmpl.load = function (id) {\n    return document.getElementById(id).innerHTML\n  }\n  tmpl.regexp = /([\\s'\\\\])(?!(?:[^{]|\\{(?!%))*%\\})|(?:\\{%(=|#)([\\s\\S]+?)%\\})|(\\{%)|(%\\})/g\n  tmpl.func = function (s, p1, p2, p3, p4, p5) {\n    if (p1) {\n      // whitespace, quote and backspace in HTML context\n      return (\n        {\n          '\\n': '\\\\n',\n          '\\r': '\\\\r',\n          '\\t': '\\\\t',\n          ' ': ' '\n        }[p1] || '\\\\' + p1\n      )\n    }\n    if (p2) {\n      // interpolation: {%=prop%}, or unescaped: {%#prop%}\n      if (p2 === '=') {\n        return \"'+_e(\" + p3 + \")+'\"\n      }\n      return \"'+(\" + p3 + \"==null?'':\" + p3 + \")+'\"\n    }\n    if (p4) {\n      // evaluation start tag: {%\n      return \"';\"\n    }\n    if (p5) {\n      // evaluation end tag: %}\n      return \"_s+='\"\n    }\n  }\n  tmpl.encReg = /[<>&\"'\\x00]/g // eslint-disable-line no-control-regex\n  tmpl.encMap = {\n    '<': '&lt;',\n    '>': '&gt;',\n    '&': '&amp;',\n    '\"': '&quot;',\n    \"'\": '&#39;'\n  }\n  tmpl.encode = function (s) {\n    // eslint-disable-next-line eqeqeq\n    return (s == null ? '' : '' + s).replace(tmpl.encReg, function (c) {\n      return tmpl.encMap[c] || ''\n    })\n  }\n  tmpl.arg = 'o'\n  tmpl.helper =\n    \",print=function(s,e){_s+=e?(s==null?'':s):_e(s);}\" +\n    ',include=function(s,d){_s+=tmpl(s,d);}'\n  if (typeof define === 'function' && define.amd) {\n    define(function () {\n      return tmpl\n    })\n  } else if (typeof module === 'object' && module.exports) {\n    module.exports = tmpl\n  } else {\n    $.tmpl = tmpl\n  }\n})(this)\n","jquery/fileUploader/vendor/blueimp-tmpl/js/runtime.js":"/*\n * JavaScript Templates Runtime\n * https://github.com/blueimp/JavaScript-Templates\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define */\n\n/* eslint-disable strict */\n\n;(function ($) {\n  'use strict'\n  var tmpl = function (id, data) {\n    var f = tmpl.cache[id]\n    return data\n      ? f(data, tmpl)\n      : function (data) {\n          return f(data, tmpl)\n        }\n  }\n  tmpl.cache = {}\n  tmpl.encReg = /[<>&\"'\\x00]/g // eslint-disable-line no-control-regex\n  tmpl.encMap = {\n    '<': '&lt;',\n    '>': '&gt;',\n    '&': '&amp;',\n    '\"': '&quot;',\n    \"'\": '&#39;'\n  }\n  tmpl.encode = function (s) {\n    // eslint-disable-next-line eqeqeq\n    return (s == null ? '' : '' + s).replace(tmpl.encReg, function (c) {\n      return tmpl.encMap[c] || ''\n    })\n  }\n  if (typeof define === 'function' && define.amd) {\n    define(function () {\n      return tmpl\n    })\n  } else if (typeof module === 'object' && module.exports) {\n    module.exports = tmpl\n  } else {\n    $.tmpl = tmpl\n  }\n})(this)\n","jquery/fileUploader/vendor/blueimp-tmpl/js/compile.js":"#!/usr/bin/env node\n/*\n * JavaScript Templates Compiler\n * https://github.com/blueimp/JavaScript-Templates\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* eslint-disable strict */\n/* eslint-disable no-console */\n\n;(function () {\n  'use strict'\n  var path = require('path')\n  var tmpl = require(path.join(__dirname, 'tmpl.js'))\n  var fs = require('fs')\n  // Retrieve the content of the minimal runtime:\n  var runtime = fs.readFileSync(path.join(__dirname, 'runtime.js'), 'utf8')\n  // A regular expression to parse templates from script tags in a HTML page:\n  var regexp = /<script( id=\"([\\w-]+)\")? type=\"text\\/x-tmpl\"( id=\"([\\w-]+)\")?>([\\s\\S]+?)<\\/script>/gi\n  // A regular expression to match the helper function names:\n  var helperRegexp = new RegExp(\n    tmpl.helper.match(/\\w+(?=\\s*=\\s*function\\s*\\()/g).join('\\\\s*\\\\(|') +\n      '\\\\s*\\\\('\n  )\n  // A list to store the function bodies:\n  var list = []\n  var code\n  // Extend the Templating engine with a print method for the generated functions:\n  tmpl.print = function (str) {\n    // Only add helper functions if they are used inside of the template:\n    var helper = helperRegexp.test(str) ? tmpl.helper : ''\n    var body = str.replace(tmpl.regexp, tmpl.func)\n    if (helper || /_e\\s*\\(/.test(body)) {\n      helper = '_e=tmpl.encode' + helper + ','\n    }\n    return (\n      'function(' +\n      tmpl.arg +\n      ',tmpl){' +\n      ('var ' + helper + \"_s='\" + body + \"';return _s;\")\n        .split(\"_s+='';\")\n        .join('') +\n      '}'\n    )\n  }\n  // Loop through the command line arguments:\n  process.argv.forEach(function (file, index) {\n    var listLength = list.length\n    var stats\n    var content\n    var result\n    var id\n    // Skip the first two arguments, which are \"node\" and the script:\n    if (index > 1) {\n      stats = fs.statSync(file)\n      if (!stats.isFile()) {\n        console.error(file + ' is not a file.')\n        return\n      }\n      content = fs.readFileSync(file, 'utf8')\n      // eslint-disable-next-line no-constant-condition\n      while (true) {\n        // Find templates in script tags:\n        result = regexp.exec(content)\n        if (!result) {\n          break\n        }\n        id = result[2] || result[4]\n        list.push(\"'\" + id + \"':\" + tmpl.print(result[5]))\n      }\n      if (listLength === list.length) {\n        // No template script tags found, use the complete content:\n        id = path.basename(file, path.extname(file))\n        list.push(\"'\" + id + \"':\" + tmpl.print(content))\n      }\n    }\n  })\n  if (!list.length) {\n    console.error('Missing input file.')\n    return\n  }\n  // Combine the generated functions as cache of the minimal runtime:\n  code = runtime.replace('{}', '{' + list.join(',') + '}')\n  // Print the resulting code to the console output:\n  console.log(code)\n})()\n","jquery/fileUploader/cors/jquery.xdr-transport.js":"/*\n * jQuery XDomainRequest Transport Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n *\n * Based on Julian Aubourg's ajaxHooks xdr.js:\n * https://github.com/jaubourg/ajaxHooks/\n */\n\n/* global define, require, XDomainRequest */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery'], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(require('jquery'));\n  } else {\n    // Browser globals:\n    factory(window.jQuery);\n  }\n})(function ($) {\n  'use strict';\n  if (window.XDomainRequest && !$.support.cors) {\n    $.ajaxTransport(function (s) {\n      if (s.crossDomain && s.async) {\n        if (s.timeout) {\n          s.xdrTimeout = s.timeout;\n          delete s.timeout;\n        }\n        var xdr;\n        return {\n          send: function (headers, completeCallback) {\n            var addParamChar = /\\?/.test(s.url) ? '&' : '?';\n            /**\n             * Callback wrapper function\n             *\n             * @param {number} status HTTP status code\n             * @param {string} statusText HTTP status text\n             * @param {object} [responses] Content-type specific responses\n             * @param {string} [responseHeaders] Response headers string\n             */\n            function callback(status, statusText, responses, responseHeaders) {\n              xdr.onload = xdr.onerror = xdr.ontimeout = $.noop;\n              xdr = null;\n              completeCallback(status, statusText, responses, responseHeaders);\n            }\n            xdr = new XDomainRequest();\n            // XDomainRequest only supports GET and POST:\n            if (s.type === 'DELETE') {\n              s.url = s.url + addParamChar + '_method=DELETE';\n              s.type = 'POST';\n            } else if (s.type === 'PUT') {\n              s.url = s.url + addParamChar + '_method=PUT';\n              s.type = 'POST';\n            } else if (s.type === 'PATCH') {\n              s.url = s.url + addParamChar + '_method=PATCH';\n              s.type = 'POST';\n            }\n            xdr.open(s.type, s.url);\n            xdr.onload = function () {\n              callback(\n                200,\n                'OK',\n                { text: xdr.responseText },\n                'Content-Type: ' + xdr.contentType\n              );\n            };\n            xdr.onerror = function () {\n              callback(404, 'Not Found');\n            };\n            if (s.xdrTimeout) {\n              xdr.ontimeout = function () {\n                callback(0, 'timeout');\n              };\n              xdr.timeout = s.xdrTimeout;\n            }\n            xdr.send((s.hasContent && s.data) || null);\n          },\n          abort: function () {\n            if (xdr) {\n              xdr.onerror = $.noop();\n              xdr.abort();\n            }\n          }\n        };\n      }\n    });\n  }\n});\n","jquery/fileUploader/cors/jquery.postmessage-transport.js":"/*\n * jQuery postMessage Transport Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n */\n\n/* global define, require */\n\n(function (factory) {\n  'use strict';\n  if (typeof define === 'function' && define.amd) {\n    // Register as an anonymous AMD module:\n    define(['jquery'], factory);\n  } else if (typeof exports === 'object') {\n    // Node/CommonJS:\n    factory(require('jquery'));\n  } else {\n    // Browser globals:\n    factory(window.jQuery);\n  }\n})(function ($) {\n  'use strict';\n\n  var counter = 0,\n    names = [\n      'accepts',\n      'cache',\n      'contents',\n      'contentType',\n      'crossDomain',\n      'data',\n      'dataType',\n      'headers',\n      'ifModified',\n      'mimeType',\n      'password',\n      'processData',\n      'timeout',\n      'traditional',\n      'type',\n      'url',\n      'username'\n    ],\n    convert = function (p) {\n      return p;\n    };\n\n  $.ajaxSetup({\n    converters: {\n      'postmessage text': convert,\n      'postmessage json': convert,\n      'postmessage html': convert\n    }\n  });\n\n  $.ajaxTransport('postmessage', function (options) {\n    if (options.postMessage && window.postMessage) {\n      var iframe,\n        loc = $('<a></a>').prop('href', options.postMessage)[0],\n        target = loc.protocol + '//' + loc.host,\n        xhrUpload = options.xhr().upload;\n      // IE always includes the port for the host property of a link\n      // element, but not in the location.host or origin property for the\n      // default http port 80 and https port 443, so we strip it:\n      if (/^(http:\\/\\/.+:80)|(https:\\/\\/.+:443)$/.test(target)) {\n        target = target.replace(/:(80|443)$/, '');\n      }\n      return {\n        send: function (_, completeCallback) {\n          counter += 1;\n          var message = {\n              id: 'postmessage-transport-' + counter\n            },\n            eventName = 'message.' + message.id;\n          iframe = $(\n            '<iframe style=\"display:none;\" src=\"' +\n              options.postMessage +\n              '\" name=\"' +\n              message.id +\n              '\"></iframe>'\n          )\n            .on('load', function () {\n              $.each(names, function (i, name) {\n                message[name] = options[name];\n              });\n              message.dataType = message.dataType.replace('postmessage ', '');\n              $(window).on(eventName, function (event) {\n                var e = event.originalEvent;\n                var data = e.data;\n                var ev;\n                if (e.origin === target && data.id === message.id) {\n                  if (data.type === 'progress') {\n                    ev = document.createEvent('Event');\n                    ev.initEvent(data.type, false, true);\n                    $.extend(ev, data);\n                    xhrUpload.dispatchEvent(ev);\n                  } else {\n                    completeCallback(\n                      data.status,\n                      data.statusText,\n                      { postmessage: data.result },\n                      data.headers\n                    );\n                    iframe.remove();\n                    $(window).off(eventName);\n                  }\n                }\n              });\n              iframe[0].contentWindow.postMessage(message, target);\n            })\n            .appendTo(document.body);\n        },\n        abort: function () {\n          if (iframe) {\n            iframe.remove();\n          }\n        }\n      };\n    }\n  });\n});\n","jquery/editableMultiselect/js/jquery.editable.js":"/**\n * @file Jeditable - jQuery in place edit plugin\n * @home https://github.com/NicolasCARPi/jquery_jeditable\n * @author Mika Tuupola, Dylan Verheul, Nicolas CARPi\n * @copyright \u00a9 2006 Mika Tuupola, Dylan Verheul, Nicolas CARPi\n * @licence MIT (see LICENCE file)\n * @name Jquery-jeditable\n * @type  jQuery\n *\n * @param {String|Function} target - URL or Function to send edited content to. Can also be 'disable', 'enable', or 'destroy'\n * @param {Object} [options] - Additional options\n * @param {Object} [options.ajaxoptions] - jQuery Ajax options. See https://api.jquery.com/jQuery.ajax/\n * @param {Function} [options.before] - Function to be executed before going into edit mode\n * @param {Function} [options.callback] - function(result, settings, submitdata) Function to run after submitting edited content\n * @param {String} [options.cancel] - Cancel button value, empty means no button\n * @param {String} [options.cancelcssclass] - CSS class to apply to cancel button\n * @param {Number} [options.cols] - Number of columns if using textarea\n * @param {String} [options.cssclass] - CSS class to apply to input form; use 'inherit' to copy from parent\n * @param {String} [options.inputcssclass] - CSS class to apply to input. 'inherit' to copy from parent\n * @param {Function} [options.intercept] - Intercept the returned data so you have a chance to process it before returning it in the page\n * @param {String|Function} [options.data] - Content loaded in the form\n * @param {String} [options.event='click'] - jQuery event such as 'click' or 'dblclick'. See https://api.jquery.com/category/events/\n * @param {String} [options.formid] - Give an id to the form that is produced\n * @param {String|Number} [options.height='auto'] - Height of the element in pixels or 'auto' or 'none'\n * @param {String} [options.id='id'] - POST parameter name of edited div id\n * @param {String} [options.indicator] - Indicator html to show when saving\n * @param {String} [options.label] - Label for the form\n * @param {String} [options.list] - HTML5 attribute for text input. Will suggest from a datalist with id of the list option\n * @param {String|Function} [options.loaddata] - Extra parameters to pass when fetching content before editing\n * @param {String} [options.loadtext='Loading\u2026'] - Text to display while loading external content\n * @param {String} [options.loadtype='GET'] - Request type for loadurl (GET or POST)\n * @param {String} [options.loadurl] - URL to fetch input content before editing\n * @param {Number} [options.max] - Maximum value for number type\n * @param {String} [options.maxlength] - The maximum number of character in the text field\n * @param {String} [options.method] - Method to use to send edited content (POST or PUT)\n * @param {Number} [options.min] - Minimum value for number type\n * @param {Boolean} [options.multiple] - Allow multiple selections in a select input\n * @param {String} [options.name='value'] - POST parameter name of edited content\n * @param {String|Function} [options.onblur='cancel'] - Use 'cancel', 'submit', 'ignore' or function. If function returns false, the form is cancelled.\n * @param {Function} [options.onedit] - function triggered upon edition; will cancel edition if it returns false\n * @param {Function} [options.onerror] - function(settings, original, xhr) { ... } called on error\n * @param {Function} [options.onreset] - function(settings, original) { ... } called before reset\n * @param {Function} [options.onsubmit] - function(settings, original) { ... } called before submit\n * @param {String} [options.pattern] - HTML5 attribute for text or URL input\n * @param {String} [options.placeholder='Click to edit'] - Placeholder text or html to insert when element is empty\n * @param {Number} [options.rows] - number of rows if using textarea\n * @param {Boolean} [options.select] - When true text is selected\n * @param {Function} [options.showfn]- Function that can animate the element when switching to edit mode\n * @param {String} [options.size] - The size of the text field\n * @param {String} [options.sortselectoptions] - Sort the options of a select form\n * @param {Number} [options.step] - Step size for number type\n * @param {String} [options.style] - Style to apply to input form; 'inherit' to copy from parent\n * @param {String} [options.submit] - submit button value, empty means no button\n * @param {String} [options.submitcssclass] - CSS class to apply to submit button\n * @param {Object|Function} [options.submitdata] - Extra parameters to send when submitting edited content. function(revert, settings, submitdata)\n * @param {String} [options.tooltip] - Tooltip text that appears on hover (via title attribute)\n * @param {String} [options.type='text'] - text, textarea, select, email, number, url (or any 3rd party input type)\n * @param {String|Number} [options.width='auto'] - The width of the element in pixels or 'auto' or 'none'\n *\n * @example <caption>Simple usage example:</caption>\n * $(\".editable\").editable(\"save.php\", {\n *     cancel : 'Cancel',\n *     submit : 'Save',\n *     tooltip : \"Click to edit...\",\n * });\n */\n(function($) {\n\n    'use strict';\n\n    // Keyboard accessibility/WAI-ARIA - allow users to navigate to an editable element using TAB/Shift+TAB\n    $.fn.editableAriaShim = function () {\n        this.attr({\n            role: 'button',\n            tabindex: 0\n        });\n        return this; // <-- object chaining.\n    };\n\n    // EDITABLE function\n    $.fn.editable = function(target, options) {\n\n        if ('disable' === target) {\n            $(this).data('disabled.editable', true);\n            return;\n        }\n        if ('enable' === target) {\n            $(this).data('disabled.editable', false);\n            return;\n        }\n        if ('destroy' === target) {\n            $(this)\n                .off($(this).data('event.editable'))\n                .removeData('disabled.editable')\n                .removeData('event.editable');\n            return;\n        }\n        var settings = $.extend({}, $.fn.editable.defaults, {target:target}, options);\n\n        /* setup some functions */\n        var plugin   = $.editable.types[settings.type].plugin || function() { };\n        var submit   = $.editable.types[settings.type].submit || function() { };\n        var buttons  = $.editable.types[settings.type].buttons || $.editable.types.defaults.buttons;\n        var content  = $.editable.types[settings.type].content || $.editable.types.defaults.content;\n        var element  = $.editable.types[settings.type].element || $.editable.types.defaults.element;\n        var reset    = $.editable.types[settings.type].reset || $.editable.types.defaults.reset;\n        var destroy  = $.editable.types[settings.type].destroy || $.editable.types.defaults.destroy;\n        var callback = settings.callback || function() { };\n        var intercept = settings.intercept || function(s) { return s; };\n        var onedit   = settings.onedit   || function() { };\n        var onsubmit = settings.onsubmit || function() { };\n        var onreset  = settings.onreset  || function() { };\n        var onerror  = settings.onerror  || reset;\n        var before   = settings.before || false;\n\n        // TOOLTIP\n        if (settings.tooltip) {\n            $(this).attr('title', settings.tooltip);\n        }\n\n        return this.each(function() {\n\n            /* Save this to self because this changes when scope changes. */\n            var self = this;\n\n            /* Save so it can be later used by $.editable('destroy') */\n            $(this).data('event.editable', settings.event);\n\n            /* If element is empty add something clickable (if requested) */\n            if (!$(this).html().trim()) {\n                $(this).html(settings.placeholder);\n            }\n\n            if ('destroy' === target) {\n                destroy.apply($(this).find('form'), [settings, self]);\n                return;\n            }\n\n            // EVENT IS FIRED\n            $(this).on(settings.event, function(e) {\n\n                /* Abort if element is disabled. */\n                if (true === $(this).data('disabled.editable')) {\n                    return;\n                }\n\n                // do nothing if user press Tab again, just go to next element, not into edit mode\n                if (e.which === 9) {\n                    return;\n                }\n\n                /* Prevent throwing an exception if edit field is clicked again. */\n                if (self.editing) {\n                    return;\n                }\n\n                /* Abort if onedit hook returns false. */\n                if (false === onedit.apply(this, [settings, self, e])) {\n                    return;\n                }\n\n                /* execute the before function if any was specified */\n                if (settings.before && (typeof settings.before === 'function')) {\n                    settings.before(e);\n                } else if (settings.before && !(typeof settings.before === 'function')) {\n                    throw \"The 'before' option needs to be provided as a function!\";\n                }\n\n                /* Prevent default action and bubbling. */\n                e.preventDefault();\n                e.stopPropagation();\n\n                /* Remove tooltip. */\n                if (settings.tooltip) {\n                    $(self).removeAttr('title');\n                }\n\n                /* Remove placeholder text, replace is here because of IE. */\n                if ($(this).html().toLowerCase().replace(/(;|\"|\\/)/g, '') ===\n                    settings.placeholder.toLowerCase().replace(/(;|\"|\\/)/g, '')) {\n                    $(this).html('');\n                }\n\n                self.editing    = true;\n                self.revert     = $(self).text();\n                $(self).html('');\n\n                /* Create the form object. */\n                var form = $('<form />');\n\n                /* Apply css or style or both. */\n                if (settings.cssclass) {\n                    if ('inherit' === settings.cssclass) {\n                        form.attr('class', $(self).attr('class'));\n                    } else {\n                        form.attr('class', settings.cssclass);\n                    }\n                }\n\n                if (settings.style) {\n                    if ('inherit' === settings.style) {\n                        form.attr('style', $(self).attr('style'));\n                        /* IE needs the second line or display won't be inherited. */\n                        form.css('display', $(self).css('display'));\n                    } else {\n                        form.attr('style', settings.style);\n                    }\n                }\n\n                // add a label if it exists\n                if (settings.label) {\n                    form.append('<label>' + settings.label + '</label>');\n                }\n\n                // add an ID to the form\n                if (settings.formid) {\n                    form.attr('id', settings.formid);\n                }\n\n                /* Add main input element to form and store it in input. */\n                var input = element.apply(form, [settings, self]);\n\n                if (settings.inputcssclass) {\n                    if ('inherit' === settings.inputcssclass) {\n                        input.attr('class', $(self).attr('class'));\n                    } else {\n                        input.attr('class', settings.inputcssclass);\n                    }\n                }\n\n                /* Set input content via POST, GET, given data or existing value. */\n                var input_content;\n\n                // timeout function\n                var t;\n                var isSubmitting = false;\n\n                if (settings.loadurl) {\n                    t = self.setTimeout(function() {\n                        input.disabled = true;\n                    }, 100);\n                    $(self).html(settings.loadtext);\n\n                    var loaddata = {};\n                    loaddata[settings.id] = self.id;\n                    if (typeof settings.loaddata === 'function') {\n                        $.extend(loaddata, settings.loaddata.apply(self, [self.revert, settings]));\n                    } else {\n                        $.extend(loaddata, settings.loaddata);\n                    }\n                    $.ajax({\n                        type : settings.loadtype,\n                        url  : settings.loadurl,\n                        data : loaddata,\n                        async: false,\n                        cache : false,\n                        success: function(result) {\n                            self.clearTimeout(t);\n                            input_content = result;\n                            input.disabled = false;\n                        }\n                    });\n                } else if (settings.data) {\n                    input_content = settings.data;\n                    if (typeof settings.data === 'function') {\n                        input_content = settings.data.apply(self, [self.revert, settings]);\n                    }\n                } else {\n                    input_content = self.revert;\n                }\n                content.apply(form, [input_content, settings, self]);\n\n                input.attr('name', settings.name);\n\n                /* adjust the width of the element to account for the margin/padding/border */\n                if (settings.width !== 'none') {\n                    var adj_width = settings.width - (input.outerWidth(true) - settings.width);\n                    input.width(adj_width);\n                }\n\n                /* Add buttons to the form. */\n                buttons.apply(form, [settings, self]);\n\n                /* Add created form to self. */\n                if (settings.showfn && (typeof settings.showfn === 'function')) {\n                    form.hide();\n                }\n\n                // clear the loadtext that we put here before\n                $(self).html('');\n\n                $(self).append(form);\n\n                // execute the showfn\n                if (settings.showfn && (typeof settings.showfn === 'function')) {\n                    settings.showfn(form);\n                }\n\n                /* Attach 3rd party plugin if requested. */\n                plugin.apply(form, [settings, self]);\n\n                /* Focus to first visible form element. */\n                form.find(':input:visible:enabled:first').trigger('focus');\n\n                /* Highlight input contents when requested. */\n                if (settings.select) {\n                    input.trigger('select');\n                }\n\n                /* discard changes if pressing esc */\n                $(this).on('keydown', function(e) {\n                    if (e.which === 27) {\n                        e.preventDefault();\n                        reset.apply(form, [settings, self]);\n                        /* allow shift+enter to submit form (required for textarea) */\n                    } else if (e.which == 13 && e.shiftKey){\n                        e.preventDefault();\n                        form.trigger('submit');\n                    }\n                });\n\n                /* Discard, submit or nothing with changes when clicking outside. */\n                /* Do nothing is usable when navigating with tab. */\n                if ('cancel' === settings.onblur) {\n                    input.on('blur', function(e) {\n                        /* Prevent canceling if submit was clicked. */\n                        t = self.setTimeout(function() {\n                            reset.apply(form, [settings, self]);\n                        }, 500);\n                    });\n                } else if ('submit' === settings.onblur) {\n                    input.on('blur', function(e) {\n                        /* Prevent double submit if submit was clicked. */\n                        t = self.setTimeout(function() {\n                            form.trigger('submit');\n                        }, 200);\n                    });\n                } else if (typeof settings.onblur === 'function') {\n                    input.on('blur', function(e) {\n                        // reset the form if the onblur function returns false\n                        if (false === settings.onblur.apply(self, [input.val(), settings, form])) {\n                            reset.apply(form, [settings, self]);\n                        }\n                    });\n                }\n\n                form.on('submit', function(e) {\n\n                    /* Do no submit. */\n                    e.preventDefault();\n                    e.stopPropagation();\n\n                    if (isSubmitting) {\n                        // we are already submitting! Stop right here.\n                        return false;\n                    } else {\n                        isSubmitting = true;\n                    }\n\n                    if (t) {\n                        self.clearTimeout(t);\n                    }\n\n                    /* Call before submit hook. */\n                    /* If it returns false abort submitting. */\n                    isSubmitting = false !== onsubmit.apply(form, [settings, self]);\n                    if (isSubmitting) {\n                        /* Custom inputs call before submit hook. */\n                        /* If it returns false abort submitting. */\n                        isSubmitting = false !== submit.apply(form, [settings, self]);\n                        if (isSubmitting) {\n\n                            /* Check if given target is function */\n                            if (typeof settings.target === 'function') {\n                                /* Callback function to handle the target response */\n                                var responseHandler = function(value, complete) {\n                                    isSubmitting = false;\n                                    if (false !== complete) {\n                                        $(self).html(value);\n                                        self.editing = false;\n                                        callback.apply(self, [self.innerText, settings]);\n                                        if (!$(self).html().trim()) {\n                                            $(self).html(settings.placeholder);\n                                        }\n                                    }\n                                };\n                                /* Call the user target function */\n                                var userTarget = settings.target.apply(self, [input.val(), settings, responseHandler]);\n                                /* Handle the target function return for compatibility */\n                                if (false !== userTarget && undefined !== userTarget) {\n                                    responseHandler(userTarget, userTarget);\n                                }\n\n                            } else {\n                                /* Add edited content and id of edited element to POST. */\n                                var submitdata = {};\n                                submitdata[settings.name] = input.val();\n                                submitdata[settings.id] = self.id;\n                                /* Add extra data to be POST:ed. */\n                                if (typeof settings.submitdata === 'function') {\n                                    $.extend(submitdata, settings.submitdata.apply(self, [self.revert, settings, submitdata]));\n                                } else {\n                                    $.extend(submitdata, settings.submitdata);\n                                }\n\n                                /* Quick and dirty PUT support. */\n                                if ('PUT' === settings.method) {\n                                    submitdata._method = 'put';\n                                }\n\n                                // SHOW INDICATOR\n                                $(self).html(settings.indicator);\n\n                                /* Defaults for ajaxoptions. */\n                                var ajaxoptions = {\n                                    type    : 'POST',\n                                    complete: function (xhr, status) {\n                                        isSubmitting = false;\n                                    },\n                                    data    : submitdata,\n                                    dataType: 'html',\n                                    url     : settings.target,\n                                    success : function(result, status) {\n\n                                        // INTERCEPT\n                                        result = intercept.apply(self, [result, status]);\n\n                                        if (ajaxoptions.dataType === 'html') {\n                                            $(self).html(result);\n                                        }\n                                        self.editing = false;\n                                        callback.apply(self, [result, settings, submitdata]);\n                                        if (!$(self).html().trim()) {\n                                            $(self).html(settings.placeholder);\n                                        }\n                                    },\n                                    error   : function(xhr, status, error) {\n                                        onerror.apply(form, [settings, self, xhr]);\n                                    }\n                                };\n\n                                /* Override with what is given in settings.ajaxoptions. */\n                                $.extend(ajaxoptions, settings.ajaxoptions);\n                                $.ajax(ajaxoptions);\n                            }\n                        }\n                    }\n\n                    /* Show tooltip again. */\n                    $(self).attr('title', settings.tooltip);\n                    return false;\n                });\n            });\n\n            // PRIVILEGED METHODS\n\n            // RESET\n            self.reset = function(form) {\n                /* Prevent calling reset twice when blurring. */\n                if (self.editing) {\n                    /* Before reset hook, if it returns false abort resetting. */\n                    if (false !== onreset.apply(form, [settings, self])) {\n                        $(self).text(self.revert);\n                        self.editing   = false;\n                        if (!$(self).html().trim()) {\n                            $(self).html(settings.placeholder);\n                        }\n                        /* Show tooltip again. */\n                        if (settings.tooltip) {\n                            $(self).attr('title', settings.tooltip);\n                        }\n                    }\n                }\n            };\n\n            // DESTROY\n            self.destroy = function(form) {\n                $(self)\n                    .off($(self).data('event.editable'))\n                    .removeData('disabled.editable')\n                    .removeData('event.editable');\n\n                self.clearTimeouts();\n\n                if (self.editing) {\n                    reset.apply(form, [settings, self]);\n                }\n            };\n\n            // CLEARTIMEOUT\n            self.clearTimeout = function(t) {\n                var timeouts = $(self).data('timeouts');\n                clearTimeout(t);\n                if(timeouts) {\n                    var i = timeouts.indexOf(t);\n                    if(i > -1) {\n                        timeouts.splice(i, 1);\n                        if(timeouts.length <= 0) {\n                            $(self).removeData('timeouts');\n                        }\n                    } else {\n                        console.warn('jeditable clearTimeout could not find timeout '+t);\n                    }\n                }\n            };\n\n            // CLEAR ALL TIMEOUTS\n            self.clearTimeouts = function () {\n                var timeouts = $(self).data('timeouts');\n                if(timeouts) {\n                    for(var i = 0, n = timeouts.length; i < n; ++i) {\n                        clearTimeout(timeouts[i]);\n                    }\n                    timeouts.length = 0;\n                    $(self).removeData('timeouts');\n                }\n            };\n\n            // SETTIMEOUT\n            self.setTimeout = function(callback, time) {\n                var timeouts = $(self).data('timeouts');\n                var t = setTimeout(function() {\n                    callback();\n                    self.clearTimeout(t);\n                }, time);\n                if(!timeouts) {\n                    timeouts = [];\n                    $(self).data('timeouts', timeouts);\n                }\n                timeouts.push(t);\n                return t;\n            };\n        });\n    };\n\n    var _supportInType = function (type) {\n        var i = document.createElement('input');\n        i.setAttribute('type', type);\n        return i.type !== 'text' ? type : 'text';\n    };\n\n\n    $.editable = {\n        types: {\n            defaults: {\n                element : function(settings, original) {\n                    var input = $('<input type=\"hidden\"></input>');\n                    $(this).append(input);\n                    return(input);\n                },\n                content : function(string, settings, original) {\n                    $(this).find(':input:first').val(string);\n                },\n                reset : function(settings, original) {\n                    original.reset(this);\n                },\n                destroy: function(settings, original) {\n                    original.destroy(this);\n                },\n                buttons : function(settings, original) {\n                    var form = this;\n                    var submit;\n                    if (settings.submit) {\n                        /* If given html string use that. */\n                        if (settings.submit.match(/>$/)) {\n                            submit = $(settings.submit).on('click', function() {\n                                if (submit.attr('type') !== 'submit') {\n                                    form.trigger('submit');\n                                }\n                            });\n                            /* Otherwise use button with given string as text. */\n                        } else {\n                            submit = $('<button type=\"submit\" />');\n                            submit.html(settings.submit);\n                            if (settings.submitcssclass) {\n                                submit.addClass(settings.submitcssclass);\n                            }\n                        }\n                        $(this).append(submit);\n                    }\n                    if (settings.cancel) {\n                        var cancel;\n                        /* If given html string use that. */\n                        if (settings.cancel.match(/>$/)) {\n                            cancel = $(settings.cancel);\n                            /* otherwise use button with given string as text */\n                        } else {\n                            cancel = $('<button type=\"cancel\" />');\n                            cancel.html(settings.cancel);\n                            if (settings.cancelcssclass) {\n                                cancel.addClass(settings.cancelcssclass);\n                            }\n                        }\n                        $(this).append(cancel);\n\n                        $(cancel).on('click', function(event) {\n                            var reset;\n                            if (typeof $.editable.types[settings.type].reset === 'function') {\n                                reset = $.editable.types[settings.type].reset;\n                            } else {\n                                reset = $.editable.types.defaults.reset;\n                            }\n                            reset.apply(form, [settings, original]);\n                            return false;\n                        });\n                    }\n                }\n            },\n            text: {\n                element : function(settings, original) {\n                    var input = $('<input />').attr({\n                        autocomplete: 'off',\n                        list: settings.list,\n                        maxlength: settings.maxlength,\n                        pattern: settings.pattern,\n                        placeholder: settings.placeholder,\n                        tooltip: settings.tooltip,\n                        type: 'text'\n                    });\n\n                    if (settings.width  !== 'none') {\n                        input.css('width', settings.width);\n                    }\n\n                    if (settings.height !== 'none') {\n                        input.css('height', settings.height);\n                    }\n\n                    if (settings.size) {\n                        input.attr('size', settings.size);\n                    }\n\n                    if (settings.maxlength) {\n                        input.attr('maxlength', settings.maxlength);\n                    }\n\n                    $(this).append(input);\n                    return(input);\n                }\n            },\n\n            // TEXTAREA\n            textarea: {\n                element : function(settings, original) {\n                    var textarea = $('<textarea></textarea>');\n                    if (settings.rows) {\n                        textarea.attr('rows', settings.rows);\n                    } else if (settings.height !== 'none') {\n                        textarea.height(settings.height);\n                    }\n                    if (settings.cols) {\n                        textarea.attr('cols', settings.cols);\n                    } else if (settings.width !== 'none') {\n                        textarea.width(settings.width);\n                    }\n\n                    if (settings.maxlength) {\n                        textarea.attr('maxlength', settings.maxlength);\n                    }\n\n                    $(this).append(textarea);\n                    return(textarea);\n                }\n            },\n\n            // SELECT\n            select: {\n                element : function(settings, original) {\n                    var select = $('<select />');\n\n                    if (settings.multiple) {\n                        select.attr('multiple', 'multiple');\n                    }\n\n                    $(this).append(select);\n                    return(select);\n                },\n                content : function(data, settings, original) {\n                    var json;\n                    // If it is string assume it is json\n                    if (String === data.constructor) {\n                        json = JSON.parse(data);\n                    } else {\n                        // Otherwise assume it is a hash already\n                        json = data;\n                    }\n\n                    // Create tuples for sorting\n                    var tuples = [];\n                    var key;\n\n                    if (Array.isArray(json) && json.every(Array.isArray)) {\n                        // Process list of tuples\n                        tuples = json // JSON already contains list of [key, value]\n                        json = {};\n                        tuples.forEach(function(e) {\n                            json[e[0]] = e[1]; // Recreate json object to comply with following code\n                        });\n                    } else {\n                        // Process object\n                        for (key in json) {\n                            tuples.push([key, json[key]]); // Store: [key, value]\n                        }\n                    }\n\n                    if (settings.sortselectoptions) {\n                        // sort it\n                        tuples.sort(function (a, b) {\n                            a = a[1];\n                            b = b[1];\n                            return a < b ? -1 : (a > b ? 1 : 0);\n                        });\n                    }\n                    // now add the options to our select\n                    var option;\n                    for (var i = 0; i < tuples.length; i++) {\n                        key = tuples[i][0];\n                        var value = tuples[i][1];\n\n                        if (!json.hasOwnProperty(key)) {\n                            continue;\n                        }\n\n                        if (key !== 'selected') {\n                            option = $('<option />').val(key).append(value);\n\n                            // add the selected prop if it's the same as original or if the key is 'selected'\n                            if (json.selected === key || key === String.prototype.trim.call(original.revert == null ? \"\" : original.revert)) {\n                                $(option).prop('selected', 'selected');\n                            }\n\n                            $(this).find('select').append(option);\n                        }\n                    }\n\n                    // submit on change if no submit button defined\n                    if (!settings.submit) {\n                        var form = this;\n                        $(this).find('select').change(function() {\n                            form.trigger('submit');\n                        });\n                    }\n                }\n            },\n\n            // NUMBER\n            number: {\n                element: function (settings, original) {\n                    var input = $('<input />').attr({\n                        maxlength: settings.maxlength,\n                        placeholder: settings.placeholder,\n                        min : settings.min,\n                        max : settings.max,\n                        step: settings.step,\n                        tooltip: settings.tooltip,\n                        type: _supportInType('number')\n                    });\n                    if (settings.width  !== 'none') {\n                        input.css('width', settings.width);\n                    }\n                    $(this).append(input);\n                    return input;\n                }\n            },\n\n            // EMAIL\n            email: {\n                element: function (settings, original) {\n                    var input = $('<input />').attr({\n                        maxlength: settings.maxlength,\n                        placeholder: settings.placeholder,\n                        tooltip: settings.tooltip,\n                        type: _supportInType('email')\n                    });\n                    if (settings.width  !== 'none') {\n                        input.css('width', settings.width);\n                    }\n                    $(this).append(input);\n                    return input;\n                }\n            },\n\n            // URL\n            url: {\n                element: function (settings, original) {\n                    var input = $('<input />').attr({\n                        maxlength: settings.maxlength,\n                        pattern: settings.pattern,\n                        placeholder: settings.placeholder,\n                        tooltip: settings.tooltip,\n                        type: _supportInType('url')\n                    });\n                    if (settings.width  !== 'none') {\n                        input.css('width', settings.width);\n                    }\n                    $(this).append(input);\n                    return input;\n                }\n            }\n        },\n\n        // add new input type\n        addInputType: function(name, input) {\n            $.editable.types[name] = input;\n        }\n    };\n\n    /* Publicly accessible defaults. */\n    $.fn.editable.defaults = {\n        name       : 'value',\n        id         : 'id',\n        type       : 'text',\n        width      : 'auto',\n        height     : 'auto',\n        // Keyboard accessibility - use mouse click OR press any key to enable editing\n        event      : 'click.editable keydown.editable',\n        onblur     : 'cancel',\n        tooltip    : 'Click to edit',\n        loadtype   : 'GET',\n        loadtext   : 'Loading...',\n        placeholder: 'Click to edit',\n        sortselectoptions: false,\n        loaddata   : {},\n        submitdata : {},\n        ajaxoptions: {}\n    };\n\n})(jQuery);\n","jquery/editableMultiselect/js/jquery.multiselect.js":"define([\n    \"jquery\"\n], function($){\n\n    /*\n     * jQuery.multiselect plugin\n     *\n     * Form control: allow select several values from list and add new value(s) to list\n     *\n     * Licensed under the BSD License:\n     *   http://www.opensource.org/licenses/bsd-license\n     *\n     * Version: 0.9.0\n     *\n     * @author Dmitry (dio) Levashov, dio@std42.ru\n     * @example\n     *  html: <select name=\"my-select\" multiple=\"on\"><option .... </select>\n     * js   : $('select[name=\"my-select\"]').multiselect()\n     *  or\n     * var opts = { ... };\n     * $('select[name=\"my-select\"]').multiselect(opts);\n     */\n    $.fn.multiselect = function(opts) {\n        var o = $.extend({\n            mselectHiddenClass: 'mselect-hidden',\n            mselectItemNotEditableClass: 'mselect-list-item-not-editable',\n            mselectItemNotRemovableClass: 'mselect-list-item-not-removable',\n            mselectListClass: 'mselect-list',\n            mselectItemsWrapperClass: 'mselect-items-wrapper',\n            mselectButtonAddClass: 'mselect-button-add',\n            mselectInputContainerClass: 'mselect-input-container',\n            mselectInputClass: 'mselect-input',\n            mselectButtonCancelClass: 'mselect-cancel',\n            mselectButtonSaveClass: 'mselect-save',\n            mselectListItemClass: 'mselect-list-item',\n            mselectItemsWrapperOverflowClass: 'mselect-fixed',\n            mselectDisabledClass: 'mselect-disabled',\n            mselectCheckedClass: 'mselect-checked',\n            layout: '<section class=\"block %mselectListClass%\">'\n                +'<div class=\"block-content\"><div class=\"%mselectItemsWrapperClass%\">'\n                +'%items%'\n                +'</div></div>'\n                +'<footer class=\"block-footer\">'\n                +'<span class=\"action-add %mselectButtonAddClass%\">%addText%</span>'\n                +'</footer>'\n                +'<div class=\"%mselectInputContainerClass%\">'\n                +'<input type=\"text\" class=\"%mselectInputClass%\" title=\"%inputTitle%\"/>'\n                +'<span class=\"%mselectButtonCancelClass%\" title=\"%cancelText%\"></span>'\n                +'<span class=\"%mselectButtonSaveClass%\" title=\"Add\"></span>'\n                +'</div>'\n                +'</section>',\n            item : '<div  class=\"%mselectListItemClass% %mselectDisabledClass% %iseditable% %isremovable%\"><label><input type=\"checkbox\" class=\"%mselectCheckedClass%\" value=\"%value%\" %checked% %disabled% /><span>%label%</span></label>' +\n                '<span class=\"mselect-edit\" title=\"Edit\">Edit</span>' +\n                '<span class=\"mselect-delete\" title=\"Delete\">Delete</span> ' +\n                '</div>',\n            addText: 'Add new value',\n            cancelText: 'Cancel',\n            inputTitle: 'Enter new option',\n            size: 5,\n            keyCodes: {\n                Enter: 13,\n                Esc: 27\n            },\n            toggleAddButton: true,\n            // New option for callback\n            mselectInputSubmitCallback: null,\n            parse : function(v) { return v.split(/\\s*,\\s*/); }\n        }, opts||{});\n\n        return this.filter('select[multiple]:not(.' + o.mselectHiddenClass + ')').each(function() {\n            var select = $(this).addClass(o.mselectHiddenClass).hide(),\n                size = select.attr('size') > 0 ? select.attr('size') : o.size,\n                items = (function() {\n                    var str = '';\n\n                    select.children('option').each(function(i, option) {\n                        option = $(option);\n\n                        str += o.item\n                            .replace(/%value%/gi,  option.val())\n                            .replace(/%checked%/gi, option.prop('selected') ? 'checked' : '')\n                            .replace(/%mselectCheckedClass%/gi, option.prop('selected') ? ''+o.mselectCheckedClass+'' : '')\n                            .replace(/%disabled%/gi, option.prop('disabled') ? 'disabled' : '')\n                            .replace(/%mselectDisabledClass%/gi, option.prop('disabled') ? ''+o.mselectDisabledClass+'' : '')\n                            .replace(/%mselectListItemClass%/gi, o.mselectListItemClass)\n                            .replace(/%iseditable%/gi, option.attr('data-is-editable') ? ''+o.mselectItemNotEditableClass+'' : '')\n                            .replace(/%isremovable%/i, option.attr('data-is-removable') ? ''+o.mselectItemNotRemovableClass+'' : '')\n                            .replace(/%label%/gi,  option.html());\n                    });\n\n                    return str;\n                })(),\n                html = o.layout\n                    .replace(/%items%/gi, items)\n                    .replace(/%mselectListClass%/gi, o.mselectListClass)\n                    .replace(/%mselectButtonAddClass%/gi, o.mselectButtonAddClass)\n                    .replace(/%mselectButtonSaveClass%/gi, o.mselectButtonSaveClass)\n                    .replace(/%mselectButtonCancelClass%/gi, o.mselectButtonCancelClass)\n                    .replace(/%mselectItemsWrapperClass%/gi, o.mselectItemsWrapperClass)\n                    .replace(/%mselectInputContainerClass%/gi, o.mselectInputContainerClass)\n                    .replace(/%mselectInputClass%/gi, o.mselectInputClass)\n                    .replace(/%addText%/gi, o.addText)\n                    .replace(/%cancelText%/gi, o.cancelText)\n                    .replace(/%inputTitle%/gi, o.inputTitle),\n                widget = $(html)\n                    .insertAfter(this)\n                    .on('change.mselectCheck', '[type=checkbox]', function() {\n                        var checkbox = $(this),\n                            index = checkbox.closest('.' + o.mselectListItemClass + '').index();\n\n                        select.find('option').eq(index).prop('selected', !!checkbox.prop('checked'));\n                    }),\n                list = widget.find('.' + o.mselectItemsWrapperClass + ''),\n                buttonAdd = widget.find('.' + o.mselectButtonAddClass + '')\n                    .on('click.mselectAdd', function(e) {\n                        e.preventDefault();\n                        o.toggleAddButton && buttonAdd.hide();\n                        container.show();\n                        input.trigger('focus');\n                        if (input.parents(o.mselectListClass).length) {\n                            list.scrollTop(list.height());\n                        }\n                    }),\n                container = widget.find('.' + o.mselectInputContainerClass + ''),\n                input = container.find('[type=text].' + o.mselectInputClass + '')\n                    .on('blur.mselectReset', function() {\n                        reset();\n                    })\n                    .on('keydown.mselectAddNewOption', function(e) {\n                        var c = e.keyCode;\n\n                        if (c == o.keyCodes.Enter || c == o.keyCodes.Esc) {\n                            e.preventDefault();\n                            c == o.keyCodes.Enter ? append(input.val())  : reset();\n                        }\n                    }),\n                buttonSave = container.find('.' + o.mselectButtonSaveClass + '')\n                    .on('mousedown.mselectSave', function(e) {\n                        append(input.val());\n                    }),\n                buttonCancel = container.find('.' + o.mselectButtonCancelClass + '')\n                    .on('mousedown.mdelectCancel', function(e) {\n                        input.val('');\n                    }),\n                append = function(v) {\n                    // Add ability to define custom handler for adding new values\n                    if ($.isFunction(o.mselectInputSubmitCallback)) {\n                        o.mselectInputSubmitCallback(v, o);\n                        return;\n                    }\n                    // end of callback implementation\n                    $.each(typeof(o.parse) == 'function' ? o.parse(v) : [$.trim(v)], function(i, v) {\n                        var item;\n\n                        if (v && !select.children('[value=\"' + v + '\"]').length) {\n                            item = $(o.item.replace(/%value%|%label%/gi, v)\n                                .replace(/%mselectDisabledClass%|%iseditable%|%isremovable%/gi,'')\n                                .replace(/%mselectListItemClass%/gi,o.mselectListItemClass))\n                                .find('[type=checkbox]')\n                                .addClass(o.mselectCheckedClass)\n                                .prop('checked', true)\n                                .end();\n\n                            list.children('.' + o.mselectListItemClass + '').length\n                                ? list.children('.' + o.mselectListItemClas ).last().after(item)\n                                : list.prepend(item);\n\n                            select.append('<option value=\"' + v + '\" selected=\"selected\">' + v + '</option>');\n                        }\n                    });\n\n                    reset();\n                    list.scrollTop(list.height());\n                },\n                reset = function() {\n                    var ch = select.children();\n\n                    input.val('');\n                    container.hide();\n                    buttonAdd.show();\n                    list[list.children().length ? 'show' : 'hide']();\n\n                    if (ch.length >= size && !list.hasClass(o.mselectItemsWrapperOverflowClass)) {\n                        list.height(list.children('.' + o.mselectListItemClass)\n                            .first()\n                            .outerHeight(true) * size)\n                            .addClass(o.mselectItemsWrapperOverflowClass);\n                    }\n                };\n            reset();\n        }).end();\n    };\n});\n","jquery/jstree/jquery.jstree.js":"/*globals jQuery, define, module, exports, require, window, document, postMessage */\n(function (factory) {\n    \"use strict\";\n    if (typeof define === 'function' && define.amd) {\n        define(['jquery'], factory);\n    }\n    else if(typeof module !== 'undefined' && module.exports) {\n        module.exports = factory(require('jquery'));\n    }\n    else {\n        factory(jQuery);\n    }\n}(function ($, undefined) {\n    \"use strict\";\n    /*!\n * jsTree 3.3.12\n * http://jstree.com/\n *\n * Copyright (c) 2014 Ivan Bozhanov (http://vakata.com)\n *\n * Licensed same as jquery - under the terms of the MIT License\n *   http://www.opensource.org/licenses/mit-license.php\n */\n    /*!\n * if using jslint please allow for the jQuery global and use following options:\n * jslint: loopfunc: true, browser: true, ass: true, bitwise: true, continue: true, nomen: true, plusplus: true, regexp: true, unparam: true, todo: true, white: true\n */\n    /*jshint -W083 */\n\n    // prevent another load? maybe there is a better way?\n    if($.jstree) {\n        return;\n    }\n\n    /**\n     * ### jsTree core functionality\n     */\n\n        // internal variables\n    var instance_counter = 0,\n        ccp_node = false,\n        ccp_mode = false,\n        ccp_inst = false,\n        themes_loaded = [],\n        src = $('script:last').attr('src'),\n        document = window.document; // local variable is always faster to access then a global\n\n    var setImmediate = window.setImmediate;\n    var Promise = window.Promise;\n    if (!setImmediate && Promise) {\n        // Good enough approximation of setImmediate\n        setImmediate = function (cb, arg) {\n            Promise.resolve(arg).then(cb);\n        };\n    }\n\n    /**\n     * holds all jstree related functions and variables, including the actual class and methods to create, access and manipulate instances.\n     * @name $.jstree\n     */\n    $.jstree = {\n        /**\n         * specifies the jstree version in use\n         * @name $.jstree.version\n         */\n        version : '3.3.12',\n        /**\n         * holds all the default options used when creating new instances\n         * @name $.jstree.defaults\n         */\n        defaults : {\n            /**\n             * configure which plugins will be active on an instance. Should be an array of strings, where each element is a plugin name. The default is `[]`\n             * @name $.jstree.defaults.plugins\n             */\n            plugins : []\n        },\n        /**\n         * stores all loaded jstree plugins (used internally)\n         * @name $.jstree.plugins\n         */\n        plugins : {},\n        path : src && src.indexOf('/') !== -1 ? src.replace(/\\/[^\\/]+$/,'') : '',\n        idregex : /[\\\\:&!^|()\\[\\]<>@*'+~#\";.,=\\- \\/${}%?`]/g,\n        root : '#'\n    };\n\n    /**\n     * creates a jstree instance\n     * @name $.jstree.create(el [, options])\n     * @param {DOMElement|jQuery|String} el the element to create the instance on, can be jQuery extended or a selector\n     * @param {Object} options options for this instance (extends `$.jstree.defaults`)\n     * @return {jsTree} the new instance\n     */\n    $.jstree.create = function (el, options) {\n        var tmp = new $.jstree.core(++instance_counter),\n            opt = options;\n        options = $.extend(true, {}, $.jstree.defaults, options);\n        if(opt && opt.plugins) {\n            options.plugins = opt.plugins;\n        }\n        $.each(options.plugins, function (i, k) {\n            if(i !== 'core') {\n                tmp = tmp.plugin(k, options[k]);\n            }\n        });\n        $(el).data('jstree', tmp);\n        tmp.init(el, options);\n        return tmp;\n    };\n    /**\n     * remove all traces of jstree from the DOM and destroy all instances\n     * @name $.jstree.destroy()\n     */\n    $.jstree.destroy = function () {\n        $('.jstree:jstree').jstree('destroy');\n        $(document).off('.jstree');\n    };\n    /**\n     * the jstree class constructor, used only internally\n     * @private\n     * @name $.jstree.core(id)\n     * @param {Number} id this instance's index\n     */\n    $.jstree.core = function (id) {\n        this._id = id;\n        this._cnt = 0;\n        this._wrk = null;\n        this._data = {\n            core : {\n                themes : {\n                    name : false,\n                    dots : false,\n                    icons : false,\n                    ellipsis : false\n                },\n                selected : [],\n                last_error : {},\n                working : false,\n                worker_queue : [],\n                focused : null\n            }\n        };\n    };\n    /**\n     * get a reference to an existing instance\n     *\n     * __Examples__\n     *\n     *\t// provided a container with an ID of \"tree\", and a nested node with an ID of \"branch\"\n     *\t// all of there will return the same instance\n     *\t$.jstree.reference('tree');\n     *\t$.jstree.reference('#tree');\n     *\t$.jstree.reference($('#tree'));\n     *\t$.jstree.reference(document.getElementByID('tree'));\n     *\t$.jstree.reference('branch');\n     *\t$.jstree.reference('#branch');\n     *\t$.jstree.reference($('#branch'));\n     *\t$.jstree.reference(document.getElementByID('branch'));\n     *\n     * @name $.jstree.reference(needle)\n     * @param {DOMElement|jQuery|String} needle\n     * @return {jsTree|null} the instance or `null` if not found\n     */\n    $.jstree.reference = function (needle) {\n        var tmp = null,\n            obj = null;\n        if(needle && needle.id && (!needle.tagName || !needle.nodeType)) { needle = needle.id; }\n\n        if(!obj || !obj.length) {\n            try { obj = $(needle); } catch (ignore) { }\n        }\n        if(!obj || !obj.length) {\n            try { obj = $('#' + needle.replace($.jstree.idregex,'\\\\$&')); } catch (ignore) { }\n        }\n        if(obj && obj.length && (obj = obj.closest('.jstree')).length && (obj = obj.data('jstree'))) {\n            tmp = obj;\n        }\n        else {\n            $('.jstree').each(function () {\n                var inst = $(this).data('jstree');\n                if(inst && inst._model.data[needle]) {\n                    tmp = inst;\n                    return false;\n                }\n            });\n        }\n        return tmp;\n    };\n    /**\n     * Create an instance, get an instance or invoke a command on a instance.\n     *\n     * If there is no instance associated with the current node a new one is created and `arg` is used to extend `$.jstree.defaults` for this new instance. There would be no return value (chaining is not broken).\n     *\n     * If there is an existing instance and `arg` is a string the command specified by `arg` is executed on the instance, with any additional arguments passed to the function. If the function returns a value it will be returned (chaining could break depending on function).\n     *\n     * If there is an existing instance and `arg` is not a string the instance itself is returned (similar to `$.jstree.reference`).\n     *\n     * In any other case - nothing is returned and chaining is not broken.\n     *\n     * __Examples__\n     *\n     *\t$('#tree1').jstree(); // creates an instance\n     *\t$('#tree2').jstree({ plugins : [] }); // create an instance with some options\n     *\t$('#tree1').jstree('open_node', '#branch_1'); // call a method on an existing instance, passing additional arguments\n     *\t$('#tree2').jstree(); // get an existing instance (or create an instance)\n     *\t$('#tree2').jstree(true); // get an existing instance (will not create new instance)\n     *\t$('#branch_1').jstree().select_node('#branch_1'); // get an instance (using a nested element and call a method)\n     *\n     * @name $().jstree([arg])\n     * @param {String|Object} arg\n     * @return {Mixed}\n     */\n    $.fn.jstree = function (arg) {\n        // check for string argument\n        var is_method\t= (typeof arg === 'string'),\n            args\t\t= Array.prototype.slice.call(arguments, 1),\n            result\t\t= null;\n        if(arg === true && !this.length) { return false; }\n        this.each(function () {\n            // get the instance (if there is one) and method (if it exists)\n            var instance = $.jstree.reference(this),\n                method = is_method && instance ? instance[arg] : null;\n            // if calling a method, and method is available - execute on the instance\n            result = is_method && method ?\n                method.apply(instance, args) :\n                null;\n            // if there is no instance and no method is being called - create one\n            if(!instance && !is_method && (arg === undefined || $.isPlainObject(arg))) {\n                $.jstree.create(this, arg);\n            }\n            // if there is an instance and no method is called - return the instance\n            if( (instance && !is_method) || arg === true ) {\n                result = instance || false;\n            }\n            // if there was a method call which returned a result - break and return the value\n            if(result !== null && result !== undefined) {\n                return false;\n            }\n        });\n        // if there was a method call with a valid return value - return that, otherwise continue the chain\n        return result !== null && result !== undefined ?\n            result : this;\n    };\n    /**\n     * used to find elements containing an instance\n     *\n     * __Examples__\n     *\n     *\t$('div:jstree').each(function () {\n     *\t\t$(this).jstree('destroy');\n     *\t});\n     *\n     * @name $(':jstree')\n     * @return {jQuery}\n     */\n    $.expr.pseudos.jstree = $.expr.createPseudo(function(search) {\n        return function(a) {\n            return $(a).hasClass('jstree') &&\n                $(a).data('jstree') !== undefined;\n        };\n    });\n\n    /**\n     * stores all defaults for the core\n     * @name $.jstree.defaults.core\n     */\n    $.jstree.defaults.core = {\n        /**\n         * data configuration\n         *\n         * If left as `false` the HTML inside the jstree container element is used to populate the tree (that should be an unordered list with list items).\n         *\n         * You can also pass in a HTML string or a JSON array here.\n         *\n         * It is possible to pass in a standard jQuery-like AJAX config and jstree will automatically determine if the response is JSON or HTML and use that to populate the tree.\n         * In addition to the standard jQuery ajax options here you can supply functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node is being loaded, the return value of those functions will be used.\n         *\n         * The last option is to specify a function, that function will receive the node being loaded as argument and a second param which is a function which should be called with the result.\n         *\n         * __Examples__\n         *\n         *\t// AJAX\n         *\t$('#tree').jstree({\n         *\t\t'core' : {\n         *\t\t\t'data' : {\n         *\t\t\t\t'url' : '/get/children/',\n         *\t\t\t\t'data' : function (node) {\n         *\t\t\t\t\treturn { 'id' : node.id };\n         *\t\t\t\t}\n         *\t\t\t}\n         *\t\t});\n         *\n         *\t// direct data\n         *\t$('#tree').jstree({\n         *\t\t'core' : {\n         *\t\t\t'data' : [\n         *\t\t\t\t'Simple root node',\n         *\t\t\t\t{\n         *\t\t\t\t\t'id' : 'node_2',\n         *\t\t\t\t\t'text' : 'Root node with options',\n         *\t\t\t\t\t'state' : { 'opened' : true, 'selected' : true },\n         *\t\t\t\t\t'children' : [ { 'text' : 'Child 1' }, 'Child 2']\n         *\t\t\t\t}\n         *\t\t\t]\n         *\t\t}\n         *\t});\n         *\n         *\t// function\n         *\t$('#tree').jstree({\n         *\t\t'core' : {\n         *\t\t\t'data' : function (obj, callback) {\n         *\t\t\t\tcallback.call(this, ['Root 1', 'Root 2']);\n         *\t\t\t}\n         *\t\t});\n         *\n         * @name $.jstree.defaults.core.data\n         */\n        data\t\t\t: false,\n        /**\n         * configure the various strings used throughout the tree\n         *\n         * You can use an object where the key is the string you need to replace and the value is your replacement.\n         * Another option is to specify a function which will be called with an argument of the needed string and should return the replacement.\n         * If left as `false` no replacement is made.\n         *\n         * __Examples__\n         *\n         *\t$('#tree').jstree({\n         *\t\t'core' : {\n         *\t\t\t'strings' : {\n         *\t\t\t\t'Loading ...' : 'Please wait ...'\n         *\t\t\t}\n         *\t\t}\n         *\t});\n         *\n         * @name $.jstree.defaults.core.strings\n         */\n        strings\t\t\t: false,\n        /**\n         * determines what happens when a user tries to modify the structure of the tree\n         * If left as `false` all operations like create, rename, delete, move or copy are prevented.\n         * You can set this to `true` to allow all interactions or use a function to have better control.\n         *\n         * __Examples__\n         *\n         *\t$('#tree').jstree({\n         *\t\t'core' : {\n         *\t\t\t'check_callback' : function (operation, node, node_parent, node_position, more) {\n         *\t\t\t\t// operation can be 'create_node', 'rename_node', 'delete_node', 'move_node', 'copy_node' or 'edit'\n         *\t\t\t\t// in case of 'rename_node' node_position is filled with the new node name\n         *\t\t\t\treturn operation === 'rename_node' ? true : false;\n         *\t\t\t}\n         *\t\t}\n         *\t});\n         *\n         * @name $.jstree.defaults.core.check_callback\n         */\n        check_callback\t: false,\n        /**\n         * a callback called with a single object parameter in the instance's scope when something goes wrong (operation prevented, ajax failed, etc)\n         * @name $.jstree.defaults.core.error\n         */\n        error\t\t\t: $.noop,\n        /**\n         * the open / close animation duration in milliseconds - set this to `false` to disable the animation (default is `200`)\n         * @name $.jstree.defaults.core.animation\n         */\n        animation\t\t: 200,\n        /**\n         * a boolean indicating if multiple nodes can be selected\n         * @name $.jstree.defaults.core.multiple\n         */\n        multiple\t\t: true,\n        /**\n         * theme configuration object\n         * @name $.jstree.defaults.core.themes\n         */\n        themes\t\t\t: {\n            /**\n             * the name of the theme to use (if left as `false` the default theme is used)\n             * @name $.jstree.defaults.core.themes.name\n             */\n            name\t\t\t: false,\n            /**\n             * the URL of the theme's CSS file, leave this as `false` if you have manually included the theme CSS (recommended). You can set this to `true` too which will try to autoload the theme.\n             * @name $.jstree.defaults.core.themes.url\n             */\n            url\t\t\t\t: false,\n            /**\n             * the location of all jstree themes - only used if `url` is set to `true`\n             * @name $.jstree.defaults.core.themes.dir\n             */\n            dir\t\t\t\t: false,\n            /**\n             * a boolean indicating if connecting dots are shown\n             * @name $.jstree.defaults.core.themes.dots\n             */\n            dots\t\t\t: true,\n            /**\n             * a boolean indicating if node icons are shown\n             * @name $.jstree.defaults.core.themes.icons\n             */\n            icons\t\t\t: true,\n            /**\n             * a boolean indicating if node ellipsis should be shown - this only works with a fixed with on the container\n             * @name $.jstree.defaults.core.themes.ellipsis\n             */\n            ellipsis\t\t: false,\n            /**\n             * a boolean indicating if the tree background is striped\n             * @name $.jstree.defaults.core.themes.stripes\n             */\n            stripes\t\t\t: false,\n            /**\n             * a string (or boolean `false`) specifying the theme variant to use (if the theme supports variants)\n             * @name $.jstree.defaults.core.themes.variant\n             */\n            variant\t\t\t: false,\n            /**\n             * a boolean specifying if a reponsive version of the theme should kick in on smaller screens (if the theme supports it). Defaults to `false`.\n             * @name $.jstree.defaults.core.themes.responsive\n             */\n            responsive\t\t: false\n        },\n        /**\n         * if left as `true` all parents of all selected nodes will be opened once the tree loads (so that all selected nodes are visible to the user)\n         * @name $.jstree.defaults.core.expand_selected_onload\n         */\n        expand_selected_onload : true,\n        /**\n         * if left as `true` web workers will be used to parse incoming JSON data where possible, so that the UI will not be blocked by large requests. Workers are however about 30% slower. Defaults to `true`\n         * @name $.jstree.defaults.core.worker\n         */\n        worker : true,\n        /**\n         * Force node text to plain text (and escape HTML). Defaults to `false`\n         * @name $.jstree.defaults.core.force_text\n         */\n        force_text : false,\n        /**\n         * Should the node be toggled if the text is double clicked. Defaults to `true`\n         * @name $.jstree.defaults.core.dblclick_toggle\n         */\n        dblclick_toggle : true,\n        /**\n         * Should the loaded nodes be part of the state. Defaults to `false`\n         * @name $.jstree.defaults.core.loaded_state\n         */\n        loaded_state : false,\n        /**\n         * Should the last active node be focused when the tree container is blurred and the focused again. This helps working with screen readers. Defaults to `true`\n         * @name $.jstree.defaults.core.restore_focus\n         */\n        restore_focus : true,\n        /**\n         * Force to compute and set \"aria-setsize\" and \"aria-posinset\" explicitly for each treeitem.\n         * Some browsers may compute incorrect elements position and produce wrong announcements for screen readers. Defaults to `false`\n         * @name $.jstree.defaults.core.compute_elements_positions\n         */\n        compute_elements_positions : false,\n        /**\n         * Default keyboard shortcuts (an object where each key is the button name or combo - like 'enter', 'ctrl-space', 'p', etc and the value is the function to execute in the instance's scope)\n         * @name $.jstree.defaults.core.keyboard\n         */\n        keyboard : {\n            'ctrl-space': function (e) {\n                // aria defines space only with Ctrl\n                e.type = \"click\";\n                $(e.currentTarget).trigger(e);\n            },\n            'enter': function (e) {\n                // enter\n                e.type = \"click\";\n                $(e.currentTarget).trigger(e);\n            },\n            'left': function (e) {\n                // left\n                e.preventDefault();\n                if(this.is_open(e.currentTarget)) {\n                    this.close_node(e.currentTarget);\n                }\n                else {\n                    var o = this.get_parent(e.currentTarget);\n                    if(o && o.id !== $.jstree.root) { this.get_node(o, true).children('.jstree-anchor').trigger('focus'); }\n                }\n            },\n            'up': function (e) {\n                // up\n                e.preventDefault();\n                var o = this.get_prev_dom(e.currentTarget);\n                if(o && o.length) { o.children('.jstree-anchor').trigger('focus'); }\n            },\n            'right': function (e) {\n                // right\n                e.preventDefault();\n                if(this.is_closed(e.currentTarget)) {\n                    this.open_node(e.currentTarget, function (o) { this.get_node(o, true).children('.jstree-anchor').trigger('focus'); });\n                }\n                else if (this.is_open(e.currentTarget)) {\n                    var o = this.get_node(e.currentTarget, true).children('.jstree-children')[0];\n                    if(o) { $(this._firstChild(o)).children('.jstree-anchor').trigger('focus'); }\n                }\n            },\n            'down': function (e) {\n                // down\n                e.preventDefault();\n                var o = this.get_next_dom(e.currentTarget);\n                if(o && o.length) { o.children('.jstree-anchor').trigger('focus'); }\n            },\n            '*': function (e) {\n                // aria defines * on numpad as open_all - not very common\n                this.open_all();\n            },\n            'home': function (e) {\n                // home\n                e.preventDefault();\n                var o = this._firstChild(this.get_container_ul()[0]);\n                if(o) { $(o).children('.jstree-anchor').filter(':visible').trigger('focus'); }\n            },\n            'end': function (e) {\n                // end\n                e.preventDefault();\n                this.element.find('.jstree-anchor').filter(':visible').last().trigger('focus');\n            },\n            'f2': function (e) {\n                // f2 - safe to include - if check_callback is false it will fail\n                e.preventDefault();\n                this.edit(e.currentTarget);\n            }\n        }\n    };\n    $.jstree.core.prototype = {\n        /**\n         * used to decorate an instance with a plugin. Used internally.\n         * @private\n         * @name plugin(deco [, opts])\n         * @param  {String} deco the plugin to decorate with\n         * @param  {Object} opts options for the plugin\n         * @return {jsTree}\n         */\n        plugin : function (deco, opts) {\n            var Child = $.jstree.plugins[deco];\n            if(Child) {\n                this._data[deco] = {};\n                Child.prototype = this;\n                return new Child(opts, this);\n            }\n            return this;\n        },\n        /**\n         * initialize the instance. Used internally.\n         * @private\n         * @name init(el, optons)\n         * @param {DOMElement|jQuery|String} el the element we are transforming\n         * @param {Object} options options for this instance\n         * @trigger init.jstree, loading.jstree, loaded.jstree, ready.jstree, changed.jstree\n         */\n        init : function (el, options) {\n            this._model = {\n                data : {},\n                changed : [],\n                force_full_redraw : false,\n                redraw_timeout : false,\n                default_state : {\n                    loaded : true,\n                    opened : false,\n                    selected : false,\n                    disabled : false\n                }\n            };\n            this._model.data[$.jstree.root] = {\n                id : $.jstree.root,\n                parent : null,\n                parents : [],\n                children : [],\n                children_d : [],\n                state : { loaded : false }\n            };\n\n            this.element = $(el).addClass('jstree jstree-' + this._id);\n            this.settings = options;\n\n            this._data.core.ready = false;\n            this._data.core.loaded = false;\n            this._data.core.rtl = (this.element.css(\"direction\") === \"rtl\");\n            this.element[this._data.core.rtl ? 'addClass' : 'removeClass'](\"jstree-rtl\");\n            this.element.attr('role','tree');\n            if(this.settings.core.multiple) {\n                this.element.attr('aria-multiselectable', true);\n            }\n            if(!this.element.attr('tabindex')) {\n                this.element.attr('tabindex','0');\n            }\n\n            this.bind();\n            /**\n             * triggered after all events are bound\n             * @event\n             * @name init.jstree\n             */\n            this.trigger(\"init\");\n\n            this._data.core.original_container_html = this.element.find(\" > ul > li\").clone(true);\n            this._data.core.original_container_html\n                .find(\"li\").addBack()\n                .contents().filter(function() {\n                return this.nodeType === 3 && (!this.nodeValue || /^\\s+$/.test(this.nodeValue));\n            })\n                .remove();\n            this.element.html(\"<\"+\"ul class='jstree-container-ul jstree-children' role='group'><\"+\"li id='j\"+this._id+\"_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='none'><i class='jstree-icon jstree-ocl'></i><\"+\"a class='jstree-anchor' role='treeitem' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>\" + this.get_string(\"Loading ...\") + \"</a></li></ul>\");\n            this.element.attr('aria-activedescendant','j' + this._id + '_loading');\n            this._data.core.li_height = this.get_container_ul().children(\"li\").first().outerHeight() || 24;\n            this._data.core.node = this._create_prototype_node();\n            /**\n             * triggered after the loading text is shown and before loading starts\n             * @event\n             * @name loading.jstree\n             */\n            this.trigger(\"loading\");\n            this.load_node($.jstree.root);\n        },\n        /**\n         * destroy an instance\n         * @name destroy()\n         * @param  {Boolean} keep_html if not set to `true` the container will be emptied, otherwise the current DOM elements will be kept intact\n         */\n        destroy : function (keep_html) {\n            /**\n             * triggered before the tree is destroyed\n             * @event\n             * @name destroy.jstree\n             */\n            this.trigger(\"destroy\");\n            if(this._wrk) {\n                try {\n                    window.URL.revokeObjectURL(this._wrk);\n                    this._wrk = null;\n                }\n                catch (ignore) { }\n            }\n            if(!keep_html) { this.element.empty(); }\n            this.teardown();\n        },\n        /**\n         * Create a prototype node\n         * @name _create_prototype_node()\n         * @return {DOMElement}\n         */\n        _create_prototype_node : function () {\n            var _node = document.createElement('LI'), _temp1, _temp2;\n            _node.setAttribute('role', 'none');\n            _temp1 = document.createElement('I');\n            _temp1.className = 'jstree-icon jstree-ocl';\n            _temp1.setAttribute('role', 'presentation');\n            _node.appendChild(_temp1);\n            _temp1 = document.createElement('A');\n            _temp1.className = 'jstree-anchor';\n            _temp1.setAttribute('href','#');\n            _temp1.setAttribute('tabindex','-1');\n            _temp1.setAttribute('role', 'treeitem');\n            _temp2 = document.createElement('I');\n            _temp2.className = 'jstree-icon jstree-themeicon';\n            _temp2.setAttribute('role', 'presentation');\n            _temp1.appendChild(_temp2);\n            _node.appendChild(_temp1);\n            _temp1 = _temp2 = null;\n\n            return _node;\n        },\n        _kbevent_to_func : function (e) {\n            var keys = {\n                8: \"Backspace\", 9: \"Tab\", 13: \"Enter\", 19: \"Pause\", 27: \"Esc\",\n                32: \"Space\", 33: \"PageUp\", 34: \"PageDown\", 35: \"End\", 36: \"Home\",\n                37: \"Left\", 38: \"Up\", 39: \"Right\", 40: \"Down\", 44: \"Print\", 45: \"Insert\",\n                46: \"Delete\", 96: \"Numpad0\", 97: \"Numpad1\", 98: \"Numpad2\", 99 : \"Numpad3\",\n                100: \"Numpad4\", 101: \"Numpad5\", 102: \"Numpad6\", 103: \"Numpad7\",\n                104: \"Numpad8\", 105: \"Numpad9\", '-13': \"NumpadEnter\", 112: \"F1\",\n                113: \"F2\", 114: \"F3\", 115: \"F4\", 116: \"F5\", 117: \"F6\", 118: \"F7\",\n                119: \"F8\", 120: \"F9\", 121: \"F10\", 122: \"F11\", 123: \"F12\", 144: \"Numlock\",\n                145: \"Scrolllock\", 16: 'Shift', 17: 'Ctrl', 18: 'Alt',\n                48: '0',  49: '1',  50: '2',  51: '3',  52: '4', 53:  '5',\n                54: '6',  55: '7',  56: '8',  57: '9',  59: ';',  61: '=', 65:  'a',\n                66: 'b',  67: 'c',  68: 'd',  69: 'e',  70: 'f',  71: 'g', 72:  'h',\n                73: 'i',  74: 'j',  75: 'k',  76: 'l',  77: 'm',  78: 'n', 79:  'o',\n                80: 'p',  81: 'q',  82: 'r',  83: 's',  84: 't',  85: 'u', 86:  'v',\n                87: 'w',  88: 'x',  89: 'y',  90: 'z', 107: '+', 109: '-', 110: '.',\n                186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`',\n                219: '[', 220: '\\\\',221: ']', 222: \"'\", 111: '/', 106: '*', 173: '-'\n            };\n            var parts = [];\n            if (e.ctrlKey) { parts.push('ctrl'); }\n            if (e.altKey) { parts.push('alt'); }\n            if (e.shiftKey) { parts.push('shift'); }\n            parts.push(keys[e.which] || e.which);\n            parts = parts.sort().join('-').toLowerCase();\n            if (parts === 'shift-shift' || parts === 'ctrl-ctrl' || parts === 'alt-alt') {\n                return null;\n            }\n\n            var kb = this.settings.core.keyboard, i, tmp;\n            for (i in kb) {\n                if (kb.hasOwnProperty(i)) {\n                    tmp = i;\n                    if (tmp !== '-' && tmp !== '+') {\n                        tmp = tmp.replace('--', '-MINUS').replace('+-', '-MINUS').replace('++', '-PLUS').replace('-+', '-PLUS');\n                        tmp = tmp.split(/-|\\+/).sort().join('-').replace('MINUS', '-').replace('PLUS', '+').toLowerCase();\n                    }\n                    if (tmp === parts) {\n                        return kb[i];\n                    }\n                }\n            }\n            return null;\n        },\n        /**\n         * part of the destroying of an instance. Used internally.\n         * @private\n         * @name teardown()\n         */\n        teardown : function () {\n            this.unbind();\n            this.element\n                .removeClass('jstree')\n                .removeData('jstree')\n                .find(\"[class^='jstree']\")\n                .addBack()\n                .attr(\"class\", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); });\n            this.element = null;\n        },\n        /**\n         * bind all events. Used internally.\n         * @private\n         * @name bind()\n         */\n        bind : function () {\n            var word = '',\n                tout = null,\n                was_click = 0;\n            this.element\n                .on(\"dblclick.jstree\", function (e) {\n                    if(e.target.tagName && e.target.tagName.toLowerCase() === \"input\") { return true; }\n                    if(document.selection && document.selection.empty) {\n                        document.selection.empty();\n                    }\n                    else {\n                        if(window.getSelection) {\n                            var sel = window.getSelection();\n                            try {\n                                sel.removeAllRanges();\n                                sel.collapse();\n                            } catch (ignore) { }\n                        }\n                    }\n                })\n                .on(\"mousedown.jstree\", function (e) {\n                    if(e.target === this.element[0]) {\n                        e.preventDefault(); // prevent losing focus when clicking scroll arrows (FF, Chrome)\n                        was_click = +(new Date()); // ie does not allow to prevent losing focus\n                    }\n                }.bind(this))\n                .on(\"mousedown.jstree\", \".jstree-ocl\", function (e) {\n                    e.preventDefault(); // prevent any node inside from losing focus when clicking the open/close icon\n                })\n                .on(\"click.jstree\", \".jstree-ocl\", function (e) {\n                    this.toggle_node(e.target);\n                }.bind(this))\n                .on(\"dblclick.jstree\", \".jstree-anchor\", function (e) {\n                    if(e.target.tagName && e.target.tagName.toLowerCase() === \"input\") { return true; }\n                    if(this.settings.core.dblclick_toggle) {\n                        this.toggle_node(e.target);\n                    }\n                }.bind(this))\n                .on(\"click.jstree\", \".jstree-anchor\", function (e) {\n                    e.preventDefault();\n                    if(e.currentTarget !== document.activeElement) { $(e.currentTarget).trigger('focus'); }\n                    this.activate_node(e.currentTarget, e);\n                }.bind(this))\n                .on('keydown.jstree', '.jstree-anchor', function (e) {\n                    if(e.target.tagName && e.target.tagName.toLowerCase() === \"input\") { return true; }\n                    if(this._data.core.rtl) {\n                        if(e.which === 37) { e.which = 39; }\n                        else if(e.which === 39) { e.which = 37; }\n                    }\n                    var f = this._kbevent_to_func(e);\n                    if (f) {\n                        var r = f.call(this, e);\n                        if (r === false || r === true) {\n                            return r;\n                        }\n                    }\n                }.bind(this))\n                .on(\"load_node.jstree\", function (e, data) {\n                    if(data.status) {\n                        if(data.node.id === $.jstree.root && !this._data.core.loaded) {\n                            this._data.core.loaded = true;\n                            if(this._firstChild(this.get_container_ul()[0])) {\n                                this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);\n                            }\n                            /**\n                             * triggered after the root node is loaded for the first time\n                             * @event\n                             * @name loaded.jstree\n                             */\n                            this.trigger(\"loaded\");\n                        }\n                        if(!this._data.core.ready) {\n                            setTimeout(function() {\n                                if(this.element && !this.get_container_ul().find('.jstree-loading').length) {\n                                    this._data.core.ready = true;\n                                    if(this._data.core.selected.length) {\n                                        if(this.settings.core.expand_selected_onload) {\n                                            var tmp = [], i, j;\n                                            for(i = 0, j = this._data.core.selected.length; i < j; i++) {\n                                                tmp = tmp.concat(this._model.data[this._data.core.selected[i]].parents);\n                                            }\n                                            tmp = $.vakata.array_unique(tmp);\n                                            for(i = 0, j = tmp.length; i < j; i++) {\n                                                this.open_node(tmp[i], false, 0);\n                                            }\n                                        }\n                                        this.trigger('changed', { 'action' : 'ready', 'selected' : this._data.core.selected });\n                                    }\n                                    /**\n                                     * triggered after all nodes are finished loading\n                                     * @event\n                                     * @name ready.jstree\n                                     */\n                                    this.trigger(\"ready\");\n                                }\n                            }.bind(this), 0);\n                        }\n                    }\n                }.bind(this))\n                // quick searching when the tree is focused\n                .on('keypress.jstree', function (e) {\n                    if(e.target.tagName && e.target.tagName.toLowerCase() === \"input\") { return true; }\n                    if(tout) { clearTimeout(tout); }\n                    tout = setTimeout(function () {\n                        word = '';\n                    }, 500);\n\n                    var chr = String.fromCharCode(e.which).toLowerCase(),\n                        col = this.element.find('.jstree-anchor').filter(':visible'),\n                        ind = col.index(document.activeElement) || 0,\n                        end = false;\n                    word += chr;\n\n                    // match for whole word from current node down (including the current node)\n                    if(word.length > 1) {\n                        col.slice(ind).each(function (i, v) {\n                            if($(v).text().toLowerCase().indexOf(word) === 0) {\n                                $(v).trigger('focus');\n                                end = true;\n                                return false;\n                            }\n                        }.bind(this));\n                        if(end) { return; }\n\n                        // match for whole word from the beginning of the tree\n                        col.slice(0, ind).each(function (i, v) {\n                            if($(v).text().toLowerCase().indexOf(word) === 0) {\n                                $(v).trigger('focus');\n                                end = true;\n                                return false;\n                            }\n                        }.bind(this));\n                        if(end) { return; }\n                    }\n                    // list nodes that start with that letter (only if word consists of a single char)\n                    if(new RegExp('^' + chr.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&') + '+$').test(word)) {\n                        // search for the next node starting with that letter\n                        col.slice(ind + 1).each(function (i, v) {\n                            if($(v).text().toLowerCase().charAt(0) === chr) {\n                                $(v).trigger('focus');\n                                end = true;\n                                return false;\n                            }\n                        }.bind(this));\n                        if(end) { return; }\n\n                        // search from the beginning\n                        col.slice(0, ind + 1).each(function (i, v) {\n                            if($(v).text().toLowerCase().charAt(0) === chr) {\n                                $(v).trigger('focus');\n                                end = true;\n                                return false;\n                            }\n                        }.bind(this));\n                        if(end) { return; }\n                    }\n                }.bind(this))\n                // THEME RELATED\n                .on(\"init.jstree\", function () {\n                    var s = this.settings.core.themes;\n                    this._data.core.themes.dots\t\t\t= s.dots;\n                    this._data.core.themes.stripes\t\t= s.stripes;\n                    this._data.core.themes.icons\t\t= s.icons;\n                    this._data.core.themes.ellipsis\t\t= s.ellipsis;\n                    this.set_theme(s.name || \"default\", s.url);\n                    this.set_theme_variant(s.variant);\n                }.bind(this))\n                .on(\"loading.jstree\", function () {\n                    this[ this._data.core.themes.dots ? \"show_dots\" : \"hide_dots\" ]();\n                    this[ this._data.core.themes.icons ? \"show_icons\" : \"hide_icons\" ]();\n                    this[ this._data.core.themes.stripes ? \"show_stripes\" : \"hide_stripes\" ]();\n                    this[ this._data.core.themes.ellipsis ? \"show_ellipsis\" : \"hide_ellipsis\" ]();\n                }.bind(this))\n                .on('blur.jstree', '.jstree-anchor', function (e) {\n                    this._data.core.focused = null;\n                    $(e.currentTarget).filter('.jstree-hovered').trigger('mouseleave');\n                    this.element.attr('tabindex', '0');\n                }.bind(this))\n                .on('focus.jstree', '.jstree-anchor', function (e) {\n                    var tmp = this.get_node(e.currentTarget);\n                    if(tmp && tmp.id) {\n                        this._data.core.focused = tmp.id;\n                    }\n                    this.element.find('.jstree-hovered').not(e.currentTarget).trigger('mouseleave');\n                    $(e.currentTarget).trigger('mouseenter');\n                    this.element.attr('tabindex', '-1');\n                }.bind(this))\n                .on('focus.jstree', function () {\n                    if(+(new Date()) - was_click > 500 && !this._data.core.focused && this.settings.core.restore_focus) {\n                        was_click = 0;\n                        var act = this.get_node(this.element.attr('aria-activedescendant'), true);\n                        if(act) {\n                            act.find('> .jstree-anchor').trigger('focus');\n                        }\n                    }\n                }.bind(this))\n                .on('mouseenter.jstree', '.jstree-anchor', function (e) {\n                    this.hover_node(e.currentTarget);\n                }.bind(this))\n                .on('mouseleave.jstree', '.jstree-anchor', function (e) {\n                    this.dehover_node(e.currentTarget);\n                }.bind(this));\n        },\n        /**\n         * part of the destroying of an instance. Used internally.\n         * @private\n         * @name unbind()\n         */\n        unbind : function () {\n            this.element.off('.jstree');\n            $(document).off('.jstree-' + this._id);\n        },\n        /**\n         * trigger an event. Used internally.\n         * @private\n         * @name trigger(ev [, data])\n         * @param  {String} ev the name of the event to trigger\n         * @param  {Object} data additional data to pass with the event\n         */\n        trigger : function (ev, data) {\n            if(!data) {\n                data = {};\n            }\n            data.instance = this;\n            this.element.triggerHandler(ev.replace('.jstree','') + '.jstree', data);\n        },\n        /**\n         * returns the jQuery extended instance container\n         * @name get_container()\n         * @return {jQuery}\n         */\n        get_container : function () {\n            return this.element;\n        },\n        /**\n         * returns the jQuery extended main UL node inside the instance container. Used internally.\n         * @private\n         * @name get_container_ul()\n         * @return {jQuery}\n         */\n        get_container_ul : function () {\n            return this.element.children(\".jstree-children\").first();\n        },\n        /**\n         * gets string replacements (localization). Used internally.\n         * @private\n         * @name get_string(key)\n         * @param  {String} key\n         * @return {String}\n         */\n        get_string : function (key) {\n            var a = this.settings.core.strings;\n            if($.vakata.is_function(a)) { return a.call(this, key); }\n            if(a && a[key]) { return a[key]; }\n            return key;\n        },\n        /**\n         * gets the first child of a DOM node. Used internally.\n         * @private\n         * @name _firstChild(dom)\n         * @param  {DOMElement} dom\n         * @return {DOMElement}\n         */\n        _firstChild : function (dom) {\n            dom = dom ? dom.firstChild : null;\n            while(dom !== null && dom.nodeType !== 1) {\n                dom = dom.nextSibling;\n            }\n            return dom;\n        },\n        /**\n         * gets the next sibling of a DOM node. Used internally.\n         * @private\n         * @name _nextSibling(dom)\n         * @param  {DOMElement} dom\n         * @return {DOMElement}\n         */\n        _nextSibling : function (dom) {\n            dom = dom ? dom.nextSibling : null;\n            while(dom !== null && dom.nodeType !== 1) {\n                dom = dom.nextSibling;\n            }\n            return dom;\n        },\n        /**\n         * gets the previous sibling of a DOM node. Used internally.\n         * @private\n         * @name _previousSibling(dom)\n         * @param  {DOMElement} dom\n         * @return {DOMElement}\n         */\n        _previousSibling : function (dom) {\n            dom = dom ? dom.previousSibling : null;\n            while(dom !== null && dom.nodeType !== 1) {\n                dom = dom.previousSibling;\n            }\n            return dom;\n        },\n        /**\n         * get the JSON representation of a node (or the actual jQuery extended DOM node) by using any input (child DOM element, ID string, selector, etc)\n         * @name get_node(obj [, as_dom])\n         * @param  {mixed} obj\n         * @param  {Boolean} as_dom\n         * @return {Object|jQuery}\n         */\n        get_node : function (obj, as_dom) {\n            if(obj && obj.id) {\n                obj = obj.id;\n            }\n            if (obj instanceof $ && obj.length && obj[0].id) {\n                obj = obj[0].id;\n            }\n            var dom;\n            try {\n                if(this._model.data[obj]) {\n                    obj = this._model.data[obj];\n                }\n                else if(typeof obj === \"string\" && this._model.data[obj.replace(/^#/, '')]) {\n                    obj = this._model.data[obj.replace(/^#/, '')];\n                }\n                else if(typeof obj === \"string\" && (dom = $('#' + obj.replace($.jstree.idregex,'\\\\$&'), this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) {\n                    obj = this._model.data[dom.closest('.jstree-node').attr('id')];\n                }\n                else if((dom = this.element.find(obj)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) {\n                    obj = this._model.data[dom.closest('.jstree-node').attr('id')];\n                }\n                else if((dom = this.element.find(obj)).length && dom.hasClass('jstree')) {\n                    obj = this._model.data[$.jstree.root];\n                }\n                else {\n                    return false;\n                }\n\n                if(as_dom) {\n                    obj = obj.id === $.jstree.root ? this.element : $('#' + obj.id.replace($.jstree.idregex,'\\\\$&'), this.element);\n                }\n                return obj;\n            } catch (ex) { return false; }\n        },\n        /**\n         * get the path to a node, either consisting of node texts, or of node IDs, optionally glued together (otherwise an array)\n         * @name get_path(obj [, glue, ids])\n         * @param  {mixed} obj the node\n         * @param  {String} glue if you want the path as a string - pass the glue here (for example '/'), if a falsy value is supplied here, an array is returned\n         * @param  {Boolean} ids if set to true build the path using ID, otherwise node text is used\n         * @return {mixed}\n         */\n        get_path : function (obj, glue, ids) {\n            obj = obj.parents ? obj : this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root || !obj.parents) {\n                return false;\n            }\n            var i, j, p = [];\n            p.push(ids ? obj.id : obj.text);\n            for(i = 0, j = obj.parents.length; i < j; i++) {\n                p.push(ids ? obj.parents[i] : this.get_text(obj.parents[i]));\n            }\n            p = p.reverse().slice(1);\n            return glue ? p.join(glue) : p;\n        },\n        /**\n         * get the next visible node that is below the `obj` node. If `strict` is set to `true` only sibling nodes are returned.\n         * @name get_next_dom(obj [, strict])\n         * @param  {mixed} obj\n         * @param  {Boolean} strict\n         * @return {jQuery}\n         */\n        get_next_dom : function (obj, strict) {\n            var tmp;\n            obj = this.get_node(obj, true);\n            if(obj[0] === this.element[0]) {\n                tmp = this._firstChild(this.get_container_ul()[0]);\n                while (tmp && tmp.offsetHeight === 0) {\n                    tmp = this._nextSibling(tmp);\n                }\n                return tmp ? $(tmp) : false;\n            }\n            if(!obj || !obj.length) {\n                return false;\n            }\n            if(strict) {\n                tmp = obj[0];\n                do {\n                    tmp = this._nextSibling(tmp);\n                } while (tmp && tmp.offsetHeight === 0);\n                return tmp ? $(tmp) : false;\n            }\n            if(obj.hasClass(\"jstree-open\")) {\n                tmp = this._firstChild(obj.children('.jstree-children')[0]);\n                while (tmp && tmp.offsetHeight === 0) {\n                    tmp = this._nextSibling(tmp);\n                }\n                if(tmp !== null) {\n                    return $(tmp);\n                }\n            }\n            tmp = obj[0];\n            do {\n                tmp = this._nextSibling(tmp);\n            } while (tmp && tmp.offsetHeight === 0);\n            if(tmp !== null) {\n                return $(tmp);\n            }\n            return obj.parentsUntil(\".jstree\",\".jstree-node\").nextAll(\".jstree-node:visible\").first();\n        },\n        /**\n         * get the previous visible node that is above the `obj` node. If `strict` is set to `true` only sibling nodes are returned.\n         * @name get_prev_dom(obj [, strict])\n         * @param  {mixed} obj\n         * @param  {Boolean} strict\n         * @return {jQuery}\n         */\n        get_prev_dom : function (obj, strict) {\n            var tmp;\n            obj = this.get_node(obj, true);\n            if(obj[0] === this.element[0]) {\n                tmp = this.get_container_ul()[0].lastChild;\n                while (tmp && tmp.offsetHeight === 0) {\n                    tmp = this._previousSibling(tmp);\n                }\n                return tmp ? $(tmp) : false;\n            }\n            if(!obj || !obj.length) {\n                return false;\n            }\n            if(strict) {\n                tmp = obj[0];\n                do {\n                    tmp = this._previousSibling(tmp);\n                } while (tmp && tmp.offsetHeight === 0);\n                return tmp ? $(tmp) : false;\n            }\n            tmp = obj[0];\n            do {\n                tmp = this._previousSibling(tmp);\n            } while (tmp && tmp.offsetHeight === 0);\n            if(tmp !== null) {\n                obj = $(tmp);\n                while(obj.hasClass(\"jstree-open\")) {\n                    obj = obj.children(\".jstree-children\").first().children(\".jstree-node:visible:last\");\n                }\n                return obj;\n            }\n            tmp = obj[0].parentNode.parentNode;\n            return tmp && tmp.className && tmp.className.indexOf('jstree-node') !== -1 ? $(tmp) : false;\n        },\n        /**\n         * get the parent ID of a node\n         * @name get_parent(obj)\n         * @param  {mixed} obj\n         * @return {String}\n         */\n        get_parent : function (obj) {\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            return obj.parent;\n        },\n        /**\n         * get a jQuery collection of all the children of a node (node must be rendered), returns false on error\n         * @name get_children_dom(obj)\n         * @param  {mixed} obj\n         * @return {jQuery}\n         */\n        get_children_dom : function (obj) {\n            obj = this.get_node(obj, true);\n            if(obj[0] === this.element[0]) {\n                return this.get_container_ul().children(\".jstree-node\");\n            }\n            if(!obj || !obj.length) {\n                return false;\n            }\n            return obj.children(\".jstree-children\").children(\".jstree-node\");\n        },\n        /**\n         * checks if a node has children\n         * @name is_parent(obj)\n         * @param  {mixed} obj\n         * @return {Boolean}\n         */\n        is_parent : function (obj) {\n            obj = this.get_node(obj);\n            return obj && (obj.state.loaded === false || obj.children.length > 0);\n        },\n        /**\n         * checks if a node is loaded (its children are available)\n         * @name is_loaded(obj)\n         * @param  {mixed} obj\n         * @return {Boolean}\n         */\n        is_loaded : function (obj) {\n            obj = this.get_node(obj);\n            return obj && obj.state.loaded;\n        },\n        /**\n         * check if a node is currently loading (fetching children)\n         * @name is_loading(obj)\n         * @param  {mixed} obj\n         * @return {Boolean}\n         */\n        is_loading : function (obj) {\n            obj = this.get_node(obj);\n            return obj && obj.state && obj.state.loading;\n        },\n        /**\n         * check if a node is opened\n         * @name is_open(obj)\n         * @param  {mixed} obj\n         * @return {Boolean}\n         */\n        is_open : function (obj) {\n            obj = this.get_node(obj);\n            return obj && obj.state.opened;\n        },\n        /**\n         * check if a node is in a closed state\n         * @name is_closed(obj)\n         * @param  {mixed} obj\n         * @return {Boolean}\n         */\n        is_closed : function (obj) {\n            obj = this.get_node(obj);\n            return obj && this.is_parent(obj) && !obj.state.opened;\n        },\n        /**\n         * check if a node has no children\n         * @name is_leaf(obj)\n         * @param  {mixed} obj\n         * @return {Boolean}\n         */\n        is_leaf : function (obj) {\n            return !this.is_parent(obj);\n        },\n        /**\n         * loads a node (fetches its children using the `core.data` setting). Multiple nodes can be passed to by using an array.\n         * @name load_node(obj [, callback])\n         * @param  {mixed} obj\n         * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives two arguments - the node and a boolean status\n         * @return {Boolean}\n         * @trigger load_node.jstree\n         */\n        load_node : function (obj, callback) {\n            var k, l, i, j, c;\n            if($.vakata.is_array(obj)) {\n                this._load_nodes(obj.slice(), callback);\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj) {\n                if(callback) { callback.call(this, obj, false); }\n                return false;\n            }\n            // if(obj.state.loading) { } // the node is already loading - just wait for it to load and invoke callback? but if called implicitly it should be loaded again?\n            if(obj.state.loaded) {\n                obj.state.loaded = false;\n                for(i = 0, j = obj.parents.length; i < j; i++) {\n                    this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) {\n                        return $.inArray(v, obj.children_d) === -1;\n                    });\n                }\n                for(k = 0, l = obj.children_d.length; k < l; k++) {\n                    if(this._model.data[obj.children_d[k]].state.selected) {\n                        c = true;\n                    }\n                    delete this._model.data[obj.children_d[k]];\n                }\n                if (c) {\n                    this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) {\n                        return $.inArray(v, obj.children_d) === -1;\n                    });\n                }\n                obj.children = [];\n                obj.children_d = [];\n                if(c) {\n                    this.trigger('changed', { 'action' : 'load_node', 'node' : obj, 'selected' : this._data.core.selected });\n                }\n            }\n            obj.state.failed = false;\n            obj.state.loading = true;\n            this.get_node(obj, true).addClass(\"jstree-loading\").attr('aria-busy',true);\n            this._load_node(obj, function (status) {\n                obj = this._model.data[obj.id];\n                obj.state.loading = false;\n                obj.state.loaded = status;\n                obj.state.failed = !obj.state.loaded;\n                var dom = this.get_node(obj, true), i = 0, j = 0, m = this._model.data, has_children = false;\n                for(i = 0, j = obj.children.length; i < j; i++) {\n                    if(m[obj.children[i]] && !m[obj.children[i]].state.hidden) {\n                        has_children = true;\n                        break;\n                    }\n                }\n                if(obj.state.loaded && dom && dom.length) {\n                    dom.removeClass('jstree-closed jstree-open jstree-leaf');\n                    if (!has_children) {\n                        dom.addClass('jstree-leaf');\n                    }\n                    else {\n                        if (obj.id !== '#') {\n                            dom.addClass(obj.state.opened ? 'jstree-open' : 'jstree-closed');\n                        }\n                    }\n                }\n                dom.removeClass(\"jstree-loading\").attr('aria-busy',false);\n                /**\n                 * triggered after a node is loaded\n                 * @event\n                 * @name load_node.jstree\n                 * @param {Object} node the node that was loading\n                 * @param {Boolean} status was the node loaded successfully\n                 */\n                this.trigger('load_node', { \"node\" : obj, \"status\" : status });\n                if(callback) {\n                    callback.call(this, obj, status);\n                }\n            }.bind(this));\n            return true;\n        },\n        /**\n         * load an array of nodes (will also load unavailable nodes as soon as they appear in the structure). Used internally.\n         * @private\n         * @name _load_nodes(nodes [, callback])\n         * @param  {array} nodes\n         * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - the array passed to _load_nodes\n         */\n        _load_nodes : function (nodes, callback, is_callback, force_reload) {\n            var r = true,\n                c = function () { this._load_nodes(nodes, callback, true); },\n                m = this._model.data, i, j, tmp = [];\n            for(i = 0, j = nodes.length; i < j; i++) {\n                if(m[nodes[i]] && ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || (!is_callback && force_reload) )) {\n                    if(!this.is_loading(nodes[i])) {\n                        this.load_node(nodes[i], c);\n                    }\n                    r = false;\n                }\n            }\n            if(r) {\n                for(i = 0, j = nodes.length; i < j; i++) {\n                    if(m[nodes[i]] && m[nodes[i]].state.loaded) {\n                        tmp.push(nodes[i]);\n                    }\n                }\n                if(callback && !callback.done) {\n                    callback.call(this, tmp);\n                    callback.done = true;\n                }\n            }\n        },\n        /**\n         * loads all unloaded nodes\n         * @name load_all([obj, callback])\n         * @param {mixed} obj the node to load recursively, omit to load all nodes in the tree\n         * @param {function} callback a function to be executed once loading all the nodes is complete,\n         * @trigger load_all.jstree\n         */\n        load_all : function (obj, callback) {\n            if(!obj) { obj = $.jstree.root; }\n            obj = this.get_node(obj);\n            if(!obj) { return false; }\n            var to_load = [],\n                m = this._model.data,\n                c = m[obj.id].children_d,\n                i, j;\n            if(obj.state && !obj.state.loaded) {\n                to_load.push(obj.id);\n            }\n            for(i = 0, j = c.length; i < j; i++) {\n                if(m[c[i]] && m[c[i]].state && !m[c[i]].state.loaded) {\n                    to_load.push(c[i]);\n                }\n            }\n            if(to_load.length) {\n                this._load_nodes(to_load, function () {\n                    this.load_all(obj, callback);\n                });\n            }\n            else {\n                /**\n                 * triggered after a load_all call completes\n                 * @event\n                 * @name load_all.jstree\n                 * @param {Object} node the recursively loaded node\n                 */\n                if(callback) { callback.call(this, obj); }\n                this.trigger('load_all', { \"node\" : obj });\n            }\n        },\n        /**\n         * handles the actual loading of a node. Used only internally.\n         * @private\n         * @name _load_node(obj [, callback])\n         * @param  {mixed} obj\n         * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - a boolean status\n         * @return {Boolean}\n         */\n        _load_node : function (obj, callback) {\n            var s = this.settings.core.data, t;\n            var notTextOrCommentNode = function notTextOrCommentNode () {\n                return this.nodeType !== 3 && this.nodeType !== 8;\n            };\n            // use original HTML\n            if(!s) {\n                if(obj.id === $.jstree.root) {\n                    return this._append_html_data(obj, this._data.core.original_container_html.clone(true), function (status) {\n                        callback.call(this, status);\n                    });\n                }\n                else {\n                    return callback.call(this, false);\n                }\n                // return callback.call(this, obj.id === $.jstree.root ? this._append_html_data(obj, this._data.core.original_container_html.clone(true)) : false);\n            }\n            if($.vakata.is_function(s)) {\n                return s.call(this, obj, function (d) {\n                    if(d === false) {\n                        callback.call(this, false);\n                    }\n                    else {\n                        this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(notTextOrCommentNode) : d, function (status) {\n                            callback.call(this, status);\n                        });\n                    }\n                    // return d === false ? callback.call(this, false) : callback.call(this, this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $(d) : d));\n                }.bind(this));\n            }\n            if(typeof s === 'object') {\n                if(s.url) {\n                    s = $.extend(true, {}, s);\n                    if($.vakata.is_function(s.url)) {\n                        s.url = s.url.call(this, obj);\n                    }\n                    if($.vakata.is_function(s.data)) {\n                        s.data = s.data.call(this, obj);\n                    }\n                    return $.ajax(s)\n                        .done(function (d,t,x) {\n                            var type = x.getResponseHeader('Content-Type');\n                            if((type && type.indexOf('json') !== -1) || typeof d === \"object\") {\n                                return this._append_json_data(obj, d, function (status) { callback.call(this, status); });\n                                //return callback.call(this, this._append_json_data(obj, d));\n                            }\n                            if((type && type.indexOf('html') !== -1) || typeof d === \"string\") {\n                                return this._append_html_data(obj, $($.parseHTML(d)).filter(notTextOrCommentNode), function (status) { callback.call(this, status); });\n                                // return callback.call(this, this._append_html_data(obj, $(d)));\n                            }\n                            this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : x }) };\n                            this.settings.core.error.call(this, this._data.core.last_error);\n                            return callback.call(this, false);\n                        }.bind(this))\n                        .fail(function (f) {\n                            this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : f }) };\n                            callback.call(this, false);\n                            this.settings.core.error.call(this, this._data.core.last_error);\n                        }.bind(this));\n                }\n                if ($.vakata.is_array(s)) {\n                    t = $.extend(true, [], s);\n                } else if ($.isPlainObject(s)) {\n                    t = $.extend(true, {}, s);\n                } else {\n                    t = s;\n                }\n                if(obj.id === $.jstree.root) {\n                    return this._append_json_data(obj, t, function (status) {\n                        callback.call(this, status);\n                    });\n                }\n                else {\n                    this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_05', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) };\n                    this.settings.core.error.call(this, this._data.core.last_error);\n                    return callback.call(this, false);\n                }\n                //return callback.call(this, (obj.id === $.jstree.root ? this._append_json_data(obj, t) : false) );\n            }\n            if(typeof s === 'string') {\n                if(obj.id === $.jstree.root) {\n                    return this._append_html_data(obj, $($.parseHTML(s)).filter(notTextOrCommentNode), function (status) {\n                        callback.call(this, status);\n                    });\n                }\n                else {\n                    this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_06', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) };\n                    this.settings.core.error.call(this, this._data.core.last_error);\n                    return callback.call(this, false);\n                }\n                //return callback.call(this, (obj.id === $.jstree.root ? this._append_html_data(obj, $(s)) : false) );\n            }\n            return callback.call(this, false);\n        },\n        /**\n         * adds a node to the list of nodes to redraw. Used only internally.\n         * @private\n         * @name _node_changed(obj [, callback])\n         * @param  {mixed} obj\n         */\n        _node_changed : function (obj) {\n            obj = this.get_node(obj);\n            if (obj && $.inArray(obj.id, this._model.changed) === -1) {\n                this._model.changed.push(obj.id);\n            }\n        },\n        /**\n         * appends HTML content to the tree. Used internally.\n         * @private\n         * @name _append_html_data(obj, data)\n         * @param  {mixed} obj the node to append to\n         * @param  {String} data the HTML string to parse and append\n         * @trigger model.jstree, changed.jstree\n         */\n        _append_html_data : function (dom, data, cb) {\n            dom = this.get_node(dom);\n            dom.children = [];\n            dom.children_d = [];\n            var dat = data.is('ul') ? data.children() : data,\n                par = dom.id,\n                chd = [],\n                dpc = [],\n                m = this._model.data,\n                p = m[par],\n                s = this._data.core.selected.length,\n                tmp, i, j;\n            dat.each(function (i, v) {\n                tmp = this._parse_model_from_html($(v), par, p.parents.concat());\n                if(tmp) {\n                    chd.push(tmp);\n                    dpc.push(tmp);\n                    if(m[tmp].children_d.length) {\n                        dpc = dpc.concat(m[tmp].children_d);\n                    }\n                }\n            }.bind(this));\n            p.children = chd;\n            p.children_d = dpc;\n            for(i = 0, j = p.parents.length; i < j; i++) {\n                m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);\n            }\n            /**\n             * triggered when new data is inserted to the tree model\n             * @event\n             * @name model.jstree\n             * @param {Array} nodes an array of node IDs\n             * @param {String} parent the parent ID of the nodes\n             */\n            this.trigger('model', { \"nodes\" : dpc, 'parent' : par });\n            if(par !== $.jstree.root) {\n                this._node_changed(par);\n                this.redraw();\n            }\n            else {\n                this.get_container_ul().children('.jstree-initial-node').remove();\n                this.redraw(true);\n            }\n            if(this._data.core.selected.length !== s) {\n                this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected });\n            }\n            cb.call(this, true);\n        },\n        /**\n         * appends JSON content to the tree. Used internally.\n         * @private\n         * @name _append_json_data(obj, data)\n         * @param  {mixed} obj the node to append to\n         * @param  {String} data the JSON object to parse and append\n         * @param  {Boolean} force_processing internal param - do not set\n         * @trigger model.jstree, changed.jstree\n         */\n        _append_json_data : function (dom, data, cb, force_processing) {\n            if(this.element === null) { return; }\n            dom = this.get_node(dom);\n            dom.children = [];\n            dom.children_d = [];\n            // *%$@!!!\n            if(data.d) {\n                data = data.d;\n                if(typeof data === \"string\") {\n                    data = JSON.parse(data);\n                }\n            }\n            if(!$.vakata.is_array(data)) { data = [data]; }\n            var w = null,\n                args = {\n                    'df'\t: this._model.default_state,\n                    'dat'\t: data,\n                    'par'\t: dom.id,\n                    'm'\t\t: this._model.data,\n                    't_id'\t: this._id,\n                    't_cnt'\t: this._cnt,\n                    'sel'\t: this._data.core.selected\n                },\n                inst = this,\n                func = function (data, undefined) {\n                    if(data.data) { data = data.data; }\n                    var dat = data.dat,\n                        par = data.par,\n                        chd = [],\n                        dpc = [],\n                        add = [],\n                        df = data.df,\n                        t_id = data.t_id,\n                        t_cnt = data.t_cnt,\n                        m = data.m,\n                        p = m[par],\n                        sel = data.sel,\n                        tmp, i, j, rslt,\n                        parse_flat = function (d, p, ps) {\n                            if(!ps) { ps = []; }\n                            else { ps = ps.concat(); }\n                            if(p) { ps.unshift(p); }\n                            var tid = d.id.toString(),\n                                i, j, c, e,\n                                tmp = {\n                                    id\t\t\t: tid,\n                                    text\t\t: d.text || '',\n                                    icon\t\t: d.icon !== undefined ? d.icon : true,\n                                    parent\t\t: p,\n                                    parents\t\t: ps,\n                                    children\t: d.children || [],\n                                    children_d\t: d.children_d || [],\n                                    data\t\t: d.data,\n                                    state\t\t: { },\n                                    li_attr\t\t: { id : false },\n                                    a_attr\t\t: { href : '#' },\n                                    original\t: false\n                                };\n                            for(i in df) {\n                                if(df.hasOwnProperty(i)) {\n                                    tmp.state[i] = df[i];\n                                }\n                            }\n                            if(d && d.data && d.data.jstree && d.data.jstree.icon) {\n                                tmp.icon = d.data.jstree.icon;\n                            }\n                            if(tmp.icon === undefined || tmp.icon === null || tmp.icon === \"\") {\n                                tmp.icon = true;\n                            }\n                            if(d && d.data) {\n                                tmp.data = d.data;\n                                if(d.data.jstree) {\n                                    for(i in d.data.jstree) {\n                                        if(d.data.jstree.hasOwnProperty(i)) {\n                                            tmp.state[i] = d.data.jstree[i];\n                                        }\n                                    }\n                                }\n                            }\n                            if(d && typeof d.state === 'object') {\n                                for (i in d.state) {\n                                    if(d.state.hasOwnProperty(i)) {\n                                        tmp.state[i] = d.state[i];\n                                    }\n                                }\n                            }\n                            if(d && typeof d.li_attr === 'object') {\n                                for (i in d.li_attr) {\n                                    if(d.li_attr.hasOwnProperty(i)) {\n                                        tmp.li_attr[i] = d.li_attr[i];\n                                    }\n                                }\n                            }\n                            if(!tmp.li_attr.id) {\n                                tmp.li_attr.id = tid;\n                            }\n                            if(d && typeof d.a_attr === 'object') {\n                                for (i in d.a_attr) {\n                                    if(d.a_attr.hasOwnProperty(i)) {\n                                        tmp.a_attr[i] = d.a_attr[i];\n                                    }\n                                }\n                            }\n                            if(d && d.children && d.children === true) {\n                                tmp.state.loaded = false;\n                                tmp.children = [];\n                                tmp.children_d = [];\n                            }\n                            m[tmp.id] = tmp;\n                            for(i = 0, j = tmp.children.length; i < j; i++) {\n                                c = parse_flat(m[tmp.children[i]], tmp.id, ps);\n                                e = m[c];\n                                tmp.children_d.push(c);\n                                if(e.children_d.length) {\n                                    tmp.children_d = tmp.children_d.concat(e.children_d);\n                                }\n                            }\n                            delete d.data;\n                            delete d.children;\n                            m[tmp.id].original = d;\n                            if(tmp.state.selected) {\n                                add.push(tmp.id);\n                            }\n                            return tmp.id;\n                        },\n                        parse_nest = function (d, p, ps) {\n                            if(!ps) { ps = []; }\n                            else { ps = ps.concat(); }\n                            if(p) { ps.unshift(p); }\n                            var tid = false, i, j, c, e, tmp;\n                            do {\n                                tid = 'j' + t_id + '_' + (++t_cnt);\n                            } while(m[tid]);\n\n                            tmp = {\n                                id\t\t\t: false,\n                                text\t\t: typeof d === 'string' ? d : '',\n                                icon\t\t: typeof d === 'object' && d.icon !== undefined ? d.icon : true,\n                                parent\t\t: p,\n                                parents\t\t: ps,\n                                children\t: [],\n                                children_d\t: [],\n                                data\t\t: null,\n                                state\t\t: { },\n                                li_attr\t\t: { id : false },\n                                a_attr\t\t: { href : '#' },\n                                original\t: false\n                            };\n                            for(i in df) {\n                                if(df.hasOwnProperty(i)) {\n                                    tmp.state[i] = df[i];\n                                }\n                            }\n                            if(d && d.id) { tmp.id = d.id.toString(); }\n                            if(d && d.text) { tmp.text = d.text; }\n                            if(d && d.data && d.data.jstree && d.data.jstree.icon) {\n                                tmp.icon = d.data.jstree.icon;\n                            }\n                            if(tmp.icon === undefined || tmp.icon === null || tmp.icon === \"\") {\n                                tmp.icon = true;\n                            }\n                            if(d && d.data) {\n                                tmp.data = d.data;\n                                if(d.data.jstree) {\n                                    for(i in d.data.jstree) {\n                                        if(d.data.jstree.hasOwnProperty(i)) {\n                                            tmp.state[i] = d.data.jstree[i];\n                                        }\n                                    }\n                                }\n                            }\n                            if(d && typeof d.state === 'object') {\n                                for (i in d.state) {\n                                    if(d.state.hasOwnProperty(i)) {\n                                        tmp.state[i] = d.state[i];\n                                    }\n                                }\n                            }\n                            if(d && typeof d.li_attr === 'object') {\n                                for (i in d.li_attr) {\n                                    if(d.li_attr.hasOwnProperty(i)) {\n                                        tmp.li_attr[i] = d.li_attr[i];\n                                    }\n                                }\n                            }\n                            if(tmp.li_attr.id && !tmp.id) {\n                                tmp.id = tmp.li_attr.id.toString();\n                            }\n                            if(!tmp.id) {\n                                tmp.id = tid;\n                            }\n                            if(!tmp.li_attr.id) {\n                                tmp.li_attr.id = tmp.id;\n                            }\n                            if(d && typeof d.a_attr === 'object') {\n                                for (i in d.a_attr) {\n                                    if(d.a_attr.hasOwnProperty(i)) {\n                                        tmp.a_attr[i] = d.a_attr[i];\n                                    }\n                                }\n                            }\n                            if(d && d.children && d.children.length) {\n                                for(i = 0, j = d.children.length; i < j; i++) {\n                                    c = parse_nest(d.children[i], tmp.id, ps);\n                                    e = m[c];\n                                    tmp.children.push(c);\n                                    if(e.children_d.length) {\n                                        tmp.children_d = tmp.children_d.concat(e.children_d);\n                                    }\n                                }\n                                tmp.children_d = tmp.children_d.concat(tmp.children);\n                            }\n                            if(d && d.children && d.children === true) {\n                                tmp.state.loaded = false;\n                                tmp.children = [];\n                                tmp.children_d = [];\n                            }\n                            delete d.data;\n                            delete d.children;\n                            tmp.original = d;\n                            m[tmp.id] = tmp;\n                            if(tmp.state.selected) {\n                                add.push(tmp.id);\n                            }\n                            return tmp.id;\n                        };\n\n                    if(dat.length && dat[0].id !== undefined && dat[0].parent !== undefined) {\n                        // Flat JSON support (for easy import from DB):\n                        // 1) convert to object (foreach)\n                        for(i = 0, j = dat.length; i < j; i++) {\n                            if(!dat[i].children) {\n                                dat[i].children = [];\n                            }\n                            if(!dat[i].state) {\n                                dat[i].state = {};\n                            }\n                            m[dat[i].id.toString()] = dat[i];\n                        }\n                        // 2) populate children (foreach)\n                        for(i = 0, j = dat.length; i < j; i++) {\n                            if (!m[dat[i].parent.toString()]) {\n                                if (typeof inst !== \"undefined\") {\n                                    inst._data.core.last_error = { 'error' : 'parse', 'plugin' : 'core', 'id' : 'core_07', 'reason' : 'Node with invalid parent', 'data' : JSON.stringify({ 'id' : dat[i].id.toString(), 'parent' : dat[i].parent.toString() }) };\n                                    inst.settings.core.error.call(inst, inst._data.core.last_error);\n                                }\n                                continue;\n                            }\n\n                            m[dat[i].parent.toString()].children.push(dat[i].id.toString());\n                            // populate parent.children_d\n                            p.children_d.push(dat[i].id.toString());\n                        }\n                        // 3) normalize && populate parents and children_d with recursion\n                        for(i = 0, j = p.children.length; i < j; i++) {\n                            tmp = parse_flat(m[p.children[i]], par, p.parents.concat());\n                            dpc.push(tmp);\n                            if(m[tmp].children_d.length) {\n                                dpc = dpc.concat(m[tmp].children_d);\n                            }\n                        }\n                        for(i = 0, j = p.parents.length; i < j; i++) {\n                            m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);\n                        }\n                        // ?) three_state selection - p.state.selected && t - (if three_state foreach(dat => ch) -> foreach(parents) if(parent.selected) child.selected = true;\n                        rslt = {\n                            'cnt' : t_cnt,\n                            'mod' : m,\n                            'sel' : sel,\n                            'par' : par,\n                            'dpc' : dpc,\n                            'add' : add\n                        };\n                    }\n                    else {\n                        for(i = 0, j = dat.length; i < j; i++) {\n                            tmp = parse_nest(dat[i], par, p.parents.concat());\n                            if(tmp) {\n                                chd.push(tmp);\n                                dpc.push(tmp);\n                                if(m[tmp].children_d.length) {\n                                    dpc = dpc.concat(m[tmp].children_d);\n                                }\n                            }\n                        }\n                        p.children = chd;\n                        p.children_d = dpc;\n                        for(i = 0, j = p.parents.length; i < j; i++) {\n                            m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);\n                        }\n                        rslt = {\n                            'cnt' : t_cnt,\n                            'mod' : m,\n                            'sel' : sel,\n                            'par' : par,\n                            'dpc' : dpc,\n                            'add' : add\n                        };\n                    }\n                    if(typeof window === 'undefined' || typeof window.document === 'undefined') {\n                        postMessage(rslt);\n                    }\n                    else {\n                        return rslt;\n                    }\n                },\n                rslt = function (rslt, worker) {\n                    if(this.element === null) { return; }\n                    this._cnt = rslt.cnt;\n                    var i, m = this._model.data;\n                    for (i in m) {\n                        if (m.hasOwnProperty(i) && m[i].state && m[i].state.loading && rslt.mod[i]) {\n                            rslt.mod[i].state.loading = true;\n                        }\n                    }\n                    this._model.data = rslt.mod; // breaks the reference in load_node - careful\n\n                    if(worker) {\n                        var j, a = rslt.add, r = rslt.sel, s = this._data.core.selected.slice();\n                        m = this._model.data;\n                        // if selection was changed while calculating in worker\n                        if(r.length !== s.length || $.vakata.array_unique(r.concat(s)).length !== r.length) {\n                            // deselect nodes that are no longer selected\n                            for(i = 0, j = r.length; i < j; i++) {\n                                if($.inArray(r[i], a) === -1 && $.inArray(r[i], s) === -1) {\n                                    m[r[i]].state.selected = false;\n                                }\n                            }\n                            // select nodes that were selected in the mean time\n                            for(i = 0, j = s.length; i < j; i++) {\n                                if($.inArray(s[i], r) === -1) {\n                                    m[s[i]].state.selected = true;\n                                }\n                            }\n                        }\n                    }\n                    if(rslt.add.length) {\n                        this._data.core.selected = this._data.core.selected.concat(rslt.add);\n                    }\n\n                    this.trigger('model', { \"nodes\" : rslt.dpc, 'parent' : rslt.par });\n\n                    if(rslt.par !== $.jstree.root) {\n                        this._node_changed(rslt.par);\n                        this.redraw();\n                    }\n                    else {\n                        // this.get_container_ul().children('.jstree-initial-node').remove();\n                        this.redraw(true);\n                    }\n                    if(rslt.add.length) {\n                        this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected });\n                    }\n\n                    // If no worker, try to mimic worker behavioour, by invoking cb asynchronously\n                    if (!worker && setImmediate) {\n                        setImmediate(function(){\n                            cb.call(inst, true);\n                        });\n                    }\n                    else {\n                        cb.call(inst, true);\n                    }\n                };\n            if(this.settings.core.worker && window.Blob && window.URL && window.Worker) {\n                try {\n                    if(this._wrk === null) {\n                        this._wrk = window.URL.createObjectURL(\n                            new window.Blob(\n                                ['self.onmessage = ' + func.toString()],\n                                {type:\"text/javascript\"}\n                            )\n                        );\n                    }\n                    if(!this._data.core.working || force_processing) {\n                        this._data.core.working = true;\n                        w = new window.Worker(this._wrk);\n                        w.onmessage = function (e) {\n                            rslt.call(this, e.data, true);\n                            try { w.terminate(); w = null; } catch(ignore) { }\n                            if(this._data.core.worker_queue.length) {\n                                this._append_json_data.apply(this, this._data.core.worker_queue.shift());\n                            }\n                            else {\n                                this._data.core.working = false;\n                            }\n                        }.bind(this);\n                        if(!args.par) {\n                            if(this._data.core.worker_queue.length) {\n                                this._append_json_data.apply(this, this._data.core.worker_queue.shift());\n                            }\n                            else {\n                                this._data.core.working = false;\n                            }\n                        }\n                        else {\n                            w.postMessage(args);\n                        }\n                    }\n                    else {\n                        this._data.core.worker_queue.push([dom, data, cb, true]);\n                    }\n                }\n                catch(e) {\n                    rslt.call(this, func(args), false);\n                    if(this._data.core.worker_queue.length) {\n                        this._append_json_data.apply(this, this._data.core.worker_queue.shift());\n                    }\n                    else {\n                        this._data.core.working = false;\n                    }\n                }\n            }\n            else {\n                rslt.call(this, func(args), false);\n            }\n        },\n        /**\n         * parses a node from a jQuery object and appends them to the in memory tree model. Used internally.\n         * @private\n         * @name _parse_model_from_html(d [, p, ps])\n         * @param  {jQuery} d the jQuery object to parse\n         * @param  {String} p the parent ID\n         * @param  {Array} ps list of all parents\n         * @return {String} the ID of the object added to the model\n         */\n        _parse_model_from_html : function (d, p, ps) {\n            if(!ps) { ps = []; }\n            else { ps = [].concat(ps); }\n            if(p) { ps.unshift(p); }\n            var c, e, m = this._model.data,\n                data = {\n                    id\t\t\t: false,\n                    text\t\t: false,\n                    icon\t\t: true,\n                    parent\t\t: p,\n                    parents\t\t: ps,\n                    children\t: [],\n                    children_d\t: [],\n                    data\t\t: null,\n                    state\t\t: { },\n                    li_attr\t\t: { id : false },\n                    a_attr\t\t: { href : '#' },\n                    original\t: false\n                }, i, tmp, tid;\n            for(i in this._model.default_state) {\n                if(this._model.default_state.hasOwnProperty(i)) {\n                    data.state[i] = this._model.default_state[i];\n                }\n            }\n            tmp = $.vakata.attributes(d, true);\n            $.each(tmp, function (i, v) {\n                v = $.vakata.trim(v);\n                if(!v.length) { return true; }\n                data.li_attr[i] = v;\n                if(i === 'id') {\n                    data.id = v.toString();\n                }\n            });\n            tmp = d.children('a').first();\n            if(tmp.length) {\n                tmp = $.vakata.attributes(tmp, true);\n                $.each(tmp, function (i, v) {\n                    v = $.vakata.trim(v);\n                    if(v.length) {\n                        data.a_attr[i] = v;\n                    }\n                });\n            }\n            tmp = d.children(\"a\").first().length ? d.children(\"a\").first().clone() : d.clone();\n            tmp.children(\"ins, i, ul\").remove();\n            tmp = tmp.html();\n            tmp = $('<div></div>').html(tmp);\n            data.text = this.settings.core.force_text ? tmp.text() : tmp.html();\n            tmp = d.data();\n            data.data = tmp ? $.extend(true, {}, tmp) : null;\n            data.state.opened = d.hasClass('jstree-open');\n            data.state.selected = d.children('a').hasClass('jstree-clicked');\n            data.state.disabled = d.children('a').hasClass('jstree-disabled');\n            if(data.data && data.data.jstree) {\n                for(i in data.data.jstree) {\n                    if(data.data.jstree.hasOwnProperty(i)) {\n                        data.state[i] = data.data.jstree[i];\n                    }\n                }\n            }\n            tmp = d.children(\"a\").children(\".jstree-themeicon\");\n            if(tmp.length) {\n                data.icon = tmp.hasClass('jstree-themeicon-hidden') ? false : tmp.attr('rel');\n            }\n            if(data.state.icon !== undefined) {\n                data.icon = data.state.icon;\n            }\n            if(data.icon === undefined || data.icon === null || data.icon === \"\") {\n                data.icon = true;\n            }\n            tmp = d.children(\"ul\").children(\"li\");\n            do {\n                tid = 'j' + this._id + '_' + (++this._cnt);\n            } while(m[tid]);\n            data.id = data.li_attr.id ? data.li_attr.id.toString() : tid;\n            if(tmp.length) {\n                tmp.each(function (i, v) {\n                    c = this._parse_model_from_html($(v), data.id, ps);\n                    e = this._model.data[c];\n                    data.children.push(c);\n                    if(e.children_d.length) {\n                        data.children_d = data.children_d.concat(e.children_d);\n                    }\n                }.bind(this));\n                data.children_d = data.children_d.concat(data.children);\n            }\n            else {\n                if(d.hasClass('jstree-closed')) {\n                    data.state.loaded = false;\n                }\n            }\n            if(data.li_attr['class']) {\n                data.li_attr['class'] = data.li_attr['class'].replace('jstree-closed','').replace('jstree-open','');\n            }\n            if(data.a_attr['class']) {\n                data.a_attr['class'] = data.a_attr['class'].replace('jstree-clicked','').replace('jstree-disabled','');\n            }\n            m[data.id] = data;\n            if(data.state.selected) {\n                this._data.core.selected.push(data.id);\n            }\n            return data.id;\n        },\n        /**\n         * parses a node from a JSON object (used when dealing with flat data, which has no nesting of children, but has id and parent properties) and appends it to the in memory tree model. Used internally.\n         * @private\n         * @name _parse_model_from_flat_json(d [, p, ps])\n         * @param  {Object} d the JSON object to parse\n         * @param  {String} p the parent ID\n         * @param  {Array} ps list of all parents\n         * @return {String} the ID of the object added to the model\n         */\n        _parse_model_from_flat_json : function (d, p, ps) {\n            if(!ps) { ps = []; }\n            else { ps = ps.concat(); }\n            if(p) { ps.unshift(p); }\n            var tid = d.id.toString(),\n                m = this._model.data,\n                df = this._model.default_state,\n                i, j, c, e,\n                tmp = {\n                    id\t\t\t: tid,\n                    text\t\t: d.text || '',\n                    icon\t\t: d.icon !== undefined ? d.icon : true,\n                    parent\t\t: p,\n                    parents\t\t: ps,\n                    children\t: d.children || [],\n                    children_d\t: d.children_d || [],\n                    data\t\t: d.data,\n                    state\t\t: { },\n                    li_attr\t\t: { id : false },\n                    a_attr\t\t: { href : '#' },\n                    original\t: false\n                };\n            for(i in df) {\n                if(df.hasOwnProperty(i)) {\n                    tmp.state[i] = df[i];\n                }\n            }\n            if(d && d.data && d.data.jstree && d.data.jstree.icon) {\n                tmp.icon = d.data.jstree.icon;\n            }\n            if(tmp.icon === undefined || tmp.icon === null || tmp.icon === \"\") {\n                tmp.icon = true;\n            }\n            if(d && d.data) {\n                tmp.data = d.data;\n                if(d.data.jstree) {\n                    for(i in d.data.jstree) {\n                        if(d.data.jstree.hasOwnProperty(i)) {\n                            tmp.state[i] = d.data.jstree[i];\n                        }\n                    }\n                }\n            }\n            if(d && typeof d.state === 'object') {\n                for (i in d.state) {\n                    if(d.state.hasOwnProperty(i)) {\n                        tmp.state[i] = d.state[i];\n                    }\n                }\n            }\n            if(d && typeof d.li_attr === 'object') {\n                for (i in d.li_attr) {\n                    if(d.li_attr.hasOwnProperty(i)) {\n                        tmp.li_attr[i] = d.li_attr[i];\n                    }\n                }\n            }\n            if(!tmp.li_attr.id) {\n                tmp.li_attr.id = tid;\n            }\n            if(d && typeof d.a_attr === 'object') {\n                for (i in d.a_attr) {\n                    if(d.a_attr.hasOwnProperty(i)) {\n                        tmp.a_attr[i] = d.a_attr[i];\n                    }\n                }\n            }\n            if(d && d.children && d.children === true) {\n                tmp.state.loaded = false;\n                tmp.children = [];\n                tmp.children_d = [];\n            }\n            m[tmp.id] = tmp;\n            for(i = 0, j = tmp.children.length; i < j; i++) {\n                c = this._parse_model_from_flat_json(m[tmp.children[i]], tmp.id, ps);\n                e = m[c];\n                tmp.children_d.push(c);\n                if(e.children_d.length) {\n                    tmp.children_d = tmp.children_d.concat(e.children_d);\n                }\n            }\n            delete d.data;\n            delete d.children;\n            m[tmp.id].original = d;\n            if(tmp.state.selected) {\n                this._data.core.selected.push(tmp.id);\n            }\n            return tmp.id;\n        },\n        /**\n         * parses a node from a JSON object and appends it to the in memory tree model. Used internally.\n         * @private\n         * @name _parse_model_from_json(d [, p, ps])\n         * @param  {Object} d the JSON object to parse\n         * @param  {String} p the parent ID\n         * @param  {Array} ps list of all parents\n         * @return {String} the ID of the object added to the model\n         */\n        _parse_model_from_json : function (d, p, ps) {\n            if(!ps) { ps = []; }\n            else { ps = ps.concat(); }\n            if(p) { ps.unshift(p); }\n            var tid = false, i, j, c, e, m = this._model.data, df = this._model.default_state, tmp;\n            do {\n                tid = 'j' + this._id + '_' + (++this._cnt);\n            } while(m[tid]);\n\n            tmp = {\n                id\t\t\t: false,\n                text\t\t: typeof d === 'string' ? d : '',\n                icon\t\t: typeof d === 'object' && d.icon !== undefined ? d.icon : true,\n                parent\t\t: p,\n                parents\t\t: ps,\n                children\t: [],\n                children_d\t: [],\n                data\t\t: null,\n                state\t\t: { },\n                li_attr\t\t: { id : false },\n                a_attr\t\t: { href : '#' },\n                original\t: false\n            };\n            for(i in df) {\n                if(df.hasOwnProperty(i)) {\n                    tmp.state[i] = df[i];\n                }\n            }\n            if(d && d.id) { tmp.id = d.id.toString(); }\n            if(d && d.text) { tmp.text = d.text; }\n            if(d && d.data && d.data.jstree && d.data.jstree.icon) {\n                tmp.icon = d.data.jstree.icon;\n            }\n            if(tmp.icon === undefined || tmp.icon === null || tmp.icon === \"\") {\n                tmp.icon = true;\n            }\n            if(d && d.data) {\n                tmp.data = d.data;\n                if(d.data.jstree) {\n                    for(i in d.data.jstree) {\n                        if(d.data.jstree.hasOwnProperty(i)) {\n                            tmp.state[i] = d.data.jstree[i];\n                        }\n                    }\n                }\n            }\n            if(d && typeof d.state === 'object') {\n                for (i in d.state) {\n                    if(d.state.hasOwnProperty(i)) {\n                        tmp.state[i] = d.state[i];\n                    }\n                }\n            }\n            if(d && typeof d.li_attr === 'object') {\n                for (i in d.li_attr) {\n                    if(d.li_attr.hasOwnProperty(i)) {\n                        tmp.li_attr[i] = d.li_attr[i];\n                    }\n                }\n            }\n            if(tmp.li_attr.id && !tmp.id) {\n                tmp.id = tmp.li_attr.id.toString();\n            }\n            if(!tmp.id) {\n                tmp.id = tid;\n            }\n            if(!tmp.li_attr.id) {\n                tmp.li_attr.id = tmp.id;\n            }\n            if(d && typeof d.a_attr === 'object') {\n                for (i in d.a_attr) {\n                    if(d.a_attr.hasOwnProperty(i)) {\n                        tmp.a_attr[i] = d.a_attr[i];\n                    }\n                }\n            }\n            if(d && d.children && d.children.length) {\n                for(i = 0, j = d.children.length; i < j; i++) {\n                    c = this._parse_model_from_json(d.children[i], tmp.id, ps);\n                    e = m[c];\n                    tmp.children.push(c);\n                    if(e.children_d.length) {\n                        tmp.children_d = tmp.children_d.concat(e.children_d);\n                    }\n                }\n                tmp.children_d = tmp.children.concat(tmp.children_d);\n            }\n            if(d && d.children && d.children === true) {\n                tmp.state.loaded = false;\n                tmp.children = [];\n                tmp.children_d = [];\n            }\n            delete d.data;\n            delete d.children;\n            tmp.original = d;\n            m[tmp.id] = tmp;\n            if(tmp.state.selected) {\n                this._data.core.selected.push(tmp.id);\n            }\n            return tmp.id;\n        },\n        /**\n         * redraws all nodes that need to be redrawn. Used internally.\n         * @private\n         * @name _redraw()\n         * @trigger redraw.jstree\n         */\n        _redraw : function () {\n            var nodes = this._model.force_full_redraw ? this._model.data[$.jstree.root].children.concat([]) : this._model.changed.concat([]),\n                f = document.createElement('UL'), tmp, i, j, fe = this._data.core.focused;\n            for(i = 0, j = nodes.length; i < j; i++) {\n                tmp = this.redraw_node(nodes[i], true, this._model.force_full_redraw);\n                if(tmp && this._model.force_full_redraw) {\n                    f.appendChild(tmp);\n                }\n            }\n            if(this._model.force_full_redraw) {\n                f.className = this.get_container_ul()[0].className;\n                f.setAttribute('role','group');\n                this.element.empty().append(f);\n                //this.get_container_ul()[0].appendChild(f);\n            }\n            if(fe !== null && this.settings.core.restore_focus) {\n                tmp = this.get_node(fe, true);\n                if(tmp && tmp.length && tmp.children('.jstree-anchor')[0] !== document.activeElement) {\n                    tmp.children('.jstree-anchor').trigger('focus');\n                }\n                else {\n                    this._data.core.focused = null;\n                }\n            }\n            this._model.force_full_redraw = false;\n            this._model.changed = [];\n            /**\n             * triggered after nodes are redrawn\n             * @event\n             * @name redraw.jstree\n             * @param {array} nodes the redrawn nodes\n             */\n            this.trigger('redraw', { \"nodes\" : nodes });\n        },\n        /**\n         * redraws all nodes that need to be redrawn or optionally - the whole tree\n         * @name redraw([full])\n         * @param {Boolean} full if set to `true` all nodes are redrawn.\n         */\n        redraw : function (full) {\n            if(full) {\n                this._model.force_full_redraw = true;\n            }\n            //if(this._model.redraw_timeout) {\n            //\tclearTimeout(this._model.redraw_timeout);\n            //}\n            //this._model.redraw_timeout = setTimeout($.proxy(this._redraw, this),0);\n            this._redraw();\n        },\n        /**\n         * redraws a single node's children. Used internally.\n         * @private\n         * @name draw_children(node)\n         * @param {mixed} node the node whose children will be redrawn\n         */\n        draw_children : function (node) {\n            var obj = this.get_node(node),\n                i = false,\n                j = false,\n                k = false,\n                d = document;\n            if(!obj) { return false; }\n            if(obj.id === $.jstree.root) { return this.redraw(true); }\n            node = this.get_node(node, true);\n            if(!node || !node.length) { return false; } // TODO: quick toggle\n\n            node.children('.jstree-children').remove();\n            node = node[0];\n            if(obj.children.length && obj.state.loaded) {\n                k = d.createElement('UL');\n                k.setAttribute('role', 'group');\n                k.className = 'jstree-children';\n                for(i = 0, j = obj.children.length; i < j; i++) {\n                    k.appendChild(this.redraw_node(obj.children[i], true, true));\n                }\n                node.appendChild(k);\n            }\n        },\n        /**\n         * redraws a single node. Used internally.\n         * @private\n         * @name redraw_node(node, deep, is_callback, force_render)\n         * @param {mixed} node the node to redraw\n         * @param {Boolean} deep should child nodes be redrawn too\n         * @param {Boolean} is_callback is this a recursion call\n         * @param {Boolean} force_render should children of closed parents be drawn anyway\n         */\n        redraw_node : function (node, deep, is_callback, force_render) {\n            var obj = this.get_node(node),\n                par = false,\n                ind = false,\n                old = false,\n                i = false,\n                j = false,\n                k = false,\n                c = '',\n                d = document,\n                m = this._model.data,\n                f = false,\n                s = false,\n                tmp = null,\n                t = 0,\n                l = 0,\n                has_children = false,\n                last_sibling = false;\n            if(!obj) { return false; }\n            if(obj.id === $.jstree.root) {  return this.redraw(true); }\n            deep = deep || obj.children.length === 0;\n            node = !document.querySelector ? document.getElementById(obj.id) : this.element[0].querySelector('#' + (\"0123456789\".indexOf(obj.id[0]) !== -1 ? '\\\\3' + obj.id[0] + ' ' + obj.id.substr(1).replace($.jstree.idregex,'\\\\$&') : obj.id.replace($.jstree.idregex,'\\\\$&')) ); //, this.element);\n            if(!node) {\n                deep = true;\n                //node = d.createElement('LI');\n                if(!is_callback) {\n                    par = obj.parent !== $.jstree.root ? $('#' + obj.parent.replace($.jstree.idregex,'\\\\$&'), this.element)[0] : null;\n                    if(par !== null && (!par || !m[obj.parent].state.opened)) {\n                        return false;\n                    }\n                    ind = $.inArray(obj.id, par === null ? m[$.jstree.root].children : m[obj.parent].children);\n                }\n            }\n            else {\n                node = $(node);\n                if(!is_callback) {\n                    par = node.parent().parent()[0];\n                    if(par === this.element[0]) {\n                        par = null;\n                    }\n                    ind = node.index();\n                }\n                // m[obj.id].data = node.data(); // use only node's data, no need to touch jquery storage\n                if(!deep && obj.children.length && !node.children('.jstree-children').length) {\n                    deep = true;\n                }\n                if(!deep) {\n                    old = node.children('.jstree-children')[0];\n                }\n                f = node.children('.jstree-anchor')[0] === document.activeElement;\n                node.remove();\n                //node = d.createElement('LI');\n                //node = node[0];\n            }\n            node = this._data.core.node.cloneNode(true);\n            // node is DOM, deep is boolean\n\n            c = 'jstree-node ';\n            for(i in obj.li_attr) {\n                if(obj.li_attr.hasOwnProperty(i)) {\n                    if(i === 'id') { continue; }\n                    if(i !== 'class') {\n                        node.setAttribute(i, obj.li_attr[i]);\n                    }\n                    else {\n                        c += obj.li_attr[i];\n                    }\n                }\n            }\n            if(!obj.a_attr.id) {\n                obj.a_attr.id = obj.id + '_anchor';\n            }\n            node.childNodes[1].setAttribute('aria-selected', !!obj.state.selected);\n            node.childNodes[1].setAttribute('aria-level', obj.parents.length);\n            if(this.settings.core.compute_elements_positions) {\n                node.childNodes[1].setAttribute('aria-setsize', m[obj.parent].children.length);\n                node.childNodes[1].setAttribute('aria-posinset', m[obj.parent].children.indexOf(obj.id) + 1);\n            }\n            if(obj.state.disabled) {\n                node.childNodes[1].setAttribute('aria-disabled', true);\n            }\n\n            for(i = 0, j = obj.children.length; i < j; i++) {\n                if(!m[obj.children[i]].state.hidden) {\n                    has_children = true;\n                    break;\n                }\n            }\n            if(obj.parent !== null && m[obj.parent] && !obj.state.hidden) {\n                i = $.inArray(obj.id, m[obj.parent].children);\n                last_sibling = obj.id;\n                if(i !== -1) {\n                    i++;\n                    for(j = m[obj.parent].children.length; i < j; i++) {\n                        if(!m[m[obj.parent].children[i]].state.hidden) {\n                            last_sibling = m[obj.parent].children[i];\n                        }\n                        if(last_sibling !== obj.id) {\n                            break;\n                        }\n                    }\n                }\n            }\n\n            if(obj.state.hidden) {\n                c += ' jstree-hidden';\n            }\n            if (obj.state.loading) {\n                c += ' jstree-loading';\n            }\n            if(obj.state.loaded && !has_children) {\n                c += ' jstree-leaf';\n            }\n            else {\n                c += obj.state.opened && obj.state.loaded ? ' jstree-open' : ' jstree-closed';\n                node.childNodes[1].setAttribute('aria-expanded', (obj.state.opened && obj.state.loaded) );\n            }\n            if(last_sibling === obj.id) {\n                c += ' jstree-last';\n            }\n            node.id = obj.id;\n            node.className = c;\n            c = ( obj.state.selected ? ' jstree-clicked' : '') + ( obj.state.disabled ? ' jstree-disabled' : '');\n            for(j in obj.a_attr) {\n                if(obj.a_attr.hasOwnProperty(j)) {\n                    if(j === 'href' && obj.a_attr[j] === '#') { continue; }\n                    if(j !== 'class') {\n                        node.childNodes[1].setAttribute(j, obj.a_attr[j]);\n                    }\n                    else {\n                        c += ' ' + obj.a_attr[j];\n                    }\n                }\n            }\n            if(c.length) {\n                node.childNodes[1].className = 'jstree-anchor ' + c;\n            }\n            if((obj.icon && obj.icon !== true) || obj.icon === false) {\n                if(obj.icon === false) {\n                    node.childNodes[1].childNodes[0].className += ' jstree-themeicon-hidden';\n                }\n                else if(obj.icon.indexOf('/') === -1 && obj.icon.indexOf('.') === -1) {\n                    node.childNodes[1].childNodes[0].className += ' ' + obj.icon + ' jstree-themeicon-custom';\n                }\n                else {\n                    node.childNodes[1].childNodes[0].style.backgroundImage = 'url(\"'+obj.icon+'\")';\n                    node.childNodes[1].childNodes[0].style.backgroundPosition = 'center center';\n                    node.childNodes[1].childNodes[0].style.backgroundSize = 'auto';\n                    node.childNodes[1].childNodes[0].className += ' jstree-themeicon-custom';\n                }\n            }\n\n            if(this.settings.core.force_text) {\n                node.childNodes[1].appendChild(d.createTextNode(obj.text));\n            }\n            else {\n                node.childNodes[1].innerHTML += obj.text;\n            }\n\n\n            if(deep && obj.children.length && (obj.state.opened || force_render) && obj.state.loaded) {\n                k = d.createElement('UL');\n                k.setAttribute('role', 'group');\n                k.className = 'jstree-children';\n                for(i = 0, j = obj.children.length; i < j; i++) {\n                    k.appendChild(this.redraw_node(obj.children[i], deep, true));\n                }\n                node.appendChild(k);\n            }\n            if(old) {\n                node.appendChild(old);\n            }\n            if(!is_callback) {\n                // append back using par / ind\n                if(!par) {\n                    par = this.element[0];\n                }\n                for(i = 0, j = par.childNodes.length; i < j; i++) {\n                    if(par.childNodes[i] && par.childNodes[i].className && par.childNodes[i].className.indexOf('jstree-children') !== -1) {\n                        tmp = par.childNodes[i];\n                        break;\n                    }\n                }\n                if(!tmp) {\n                    tmp = d.createElement('UL');\n                    tmp.setAttribute('role', 'group');\n                    tmp.className = 'jstree-children';\n                    par.appendChild(tmp);\n                }\n                par = tmp;\n\n                if(ind < par.childNodes.length) {\n                    par.insertBefore(node, par.childNodes[ind]);\n                }\n                else {\n                    par.appendChild(node);\n                }\n                if(f) {\n                    t = this.element[0].scrollTop;\n                    l = this.element[0].scrollLeft;\n                    node.childNodes[1].focus();\n                    this.element[0].scrollTop = t;\n                    this.element[0].scrollLeft = l;\n                }\n            }\n            if(obj.state.opened && !obj.state.loaded) {\n                obj.state.opened = false;\n                setTimeout(function () {\n                    this.open_node(obj.id, false, 0);\n                }.bind(this), 0);\n            }\n            return node;\n        },\n        /**\n         * opens a node, revealing its children. If the node is not loaded it will be loaded and opened once ready.\n         * @name open_node(obj [, callback, animation])\n         * @param {mixed} obj the node to open\n         * @param {Function} callback a function to execute once the node is opened\n         * @param {Number} animation the animation duration in milliseconds when opening the node (overrides the `core.animation` setting). Use `false` for no animation.\n         * @trigger open_node.jstree, after_open.jstree, before_open.jstree\n         */\n        open_node : function (obj, callback, animation) {\n            var t1, t2, d, t;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.open_node(obj[t1], callback, animation);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            animation = animation === undefined ? this.settings.core.animation : animation;\n            if(!this.is_closed(obj)) {\n                if(callback) {\n                    callback.call(this, obj, false);\n                }\n                return false;\n            }\n            if(!this.is_loaded(obj)) {\n                if(this.is_loading(obj)) {\n                    return setTimeout(function () {\n                        this.open_node(obj, callback, animation);\n                    }.bind(this), 500);\n                }\n                this.load_node(obj, function (o, ok) {\n                    return ok ? this.open_node(o, callback, animation) : (callback ? callback.call(this, o, false) : false);\n                });\n            }\n            else {\n                d = this.get_node(obj, true);\n                t = this;\n                if(d.length) {\n                    if(animation && d.children(\".jstree-children\").length) {\n                        d.children(\".jstree-children\").stop(true, true);\n                    }\n                    if(obj.children.length && !this._firstChild(d.children('.jstree-children')[0])) {\n                        this.draw_children(obj);\n                        //d = this.get_node(obj, true);\n                    }\n                    if(!animation) {\n                        this.trigger('before_open', { \"node\" : obj });\n                        d[0].className = d[0].className.replace('jstree-closed', 'jstree-open');\n                        d[0].childNodes[1].setAttribute(\"aria-expanded\", true);\n                    }\n                    else {\n                        this.trigger('before_open', { \"node\" : obj });\n                        d\n                            .children(\".jstree-children\").css(\"display\",\"none\").end()\n                            .removeClass(\"jstree-closed\").addClass(\"jstree-open\")\n                            .children('.jstree-anchor').attr(\"aria-expanded\", true).end()\n                            .children(\".jstree-children\").stop(true, true)\n                            .slideDown(animation, function () {\n                                this.style.display = \"\";\n                                if (t.element) {\n                                    t.trigger(\"after_open\", { \"node\" : obj });\n                                }\n                            });\n                    }\n                }\n                obj.state.opened = true;\n                if(callback) {\n                    callback.call(this, obj, true);\n                }\n                if(!d.length) {\n                    /**\n                     * triggered when a node is about to be opened (if the node is supposed to be in the DOM, it will be, but it won't be visible yet)\n                     * @event\n                     * @name before_open.jstree\n                     * @param {Object} node the opened node\n                     */\n                    this.trigger('before_open', { \"node\" : obj });\n                }\n                /**\n                 * triggered when a node is opened (if there is an animation it will not be completed yet)\n                 * @event\n                 * @name open_node.jstree\n                 * @param {Object} node the opened node\n                 */\n                this.trigger('open_node', { \"node\" : obj });\n                if(!animation || !d.length) {\n                    /**\n                     * triggered when a node is opened and the animation is complete\n                     * @event\n                     * @name after_open.jstree\n                     * @param {Object} node the opened node\n                     */\n                    this.trigger(\"after_open\", { \"node\" : obj });\n                }\n                return true;\n            }\n        },\n        /**\n         * opens every parent of a node (node should be loaded)\n         * @name _open_to(obj)\n         * @param {mixed} obj the node to reveal\n         * @private\n         */\n        _open_to : function (obj) {\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            var i, j, p = obj.parents;\n            for(i = 0, j = p.length; i < j; i+=1) {\n                if(i !== $.jstree.root) {\n                    this.open_node(p[i], false, 0);\n                }\n            }\n            return $('#' + obj.id.replace($.jstree.idregex,'\\\\$&'), this.element);\n        },\n        /**\n         * closes a node, hiding its children\n         * @name close_node(obj [, animation])\n         * @param {mixed} obj the node to close\n         * @param {Number} animation the animation duration in milliseconds when closing the node (overrides the `core.animation` setting). Use `false` for no animation.\n         * @trigger close_node.jstree, after_close.jstree\n         */\n        close_node : function (obj, animation) {\n            var t1, t2, t, d;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.close_node(obj[t1], animation);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            if(this.is_closed(obj)) {\n                return false;\n            }\n            animation = animation === undefined ? this.settings.core.animation : animation;\n            t = this;\n            d = this.get_node(obj, true);\n\n            obj.state.opened = false;\n            /**\n             * triggered when a node is closed (if there is an animation it will not be complete yet)\n             * @event\n             * @name close_node.jstree\n             * @param {Object} node the closed node\n             */\n            this.trigger('close_node',{ \"node\" : obj });\n            if(!d.length) {\n                /**\n                 * triggered when a node is closed and the animation is complete\n                 * @event\n                 * @name after_close.jstree\n                 * @param {Object} node the closed node\n                 */\n                this.trigger(\"after_close\", { \"node\" : obj });\n            }\n            else {\n                if(!animation) {\n                    d[0].className = d[0].className.replace('jstree-open', 'jstree-closed');\n                    d.children('.jstree-anchor').attr(\"aria-expanded\", false);\n                    d.children('.jstree-children').remove();\n                    this.trigger(\"after_close\", { \"node\" : obj });\n                }\n                else {\n                    d\n                        .children(\".jstree-children\").attr(\"style\",\"display:block !important\").end()\n                        .removeClass(\"jstree-open\").addClass(\"jstree-closed\")\n                        .children('.jstree-anchor').attr(\"aria-expanded\", false).end()\n                        .children(\".jstree-children\").stop(true, true).slideUp(animation, function () {\n                        this.style.display = \"\";\n                        d.children('.jstree-children').remove();\n                        if (t.element) {\n                            t.trigger(\"after_close\", { \"node\" : obj });\n                        }\n                    });\n                }\n            }\n        },\n        /**\n         * toggles a node - closing it if it is open, opening it if it is closed\n         * @name toggle_node(obj)\n         * @param {mixed} obj the node to toggle\n         */\n        toggle_node : function (obj) {\n            var t1, t2;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.toggle_node(obj[t1]);\n                }\n                return true;\n            }\n            if(this.is_closed(obj)) {\n                return this.open_node(obj);\n            }\n            if(this.is_open(obj)) {\n                return this.close_node(obj);\n            }\n        },\n        /**\n         * opens all nodes within a node (or the tree), revealing their children. If the node is not loaded it will be loaded and opened once ready.\n         * @name open_all([obj, animation, original_obj])\n         * @param {mixed} obj the node to open recursively, omit to open all nodes in the tree\n         * @param {Number} animation the animation duration in milliseconds when opening the nodes, the default is no animation\n         * @param {jQuery} reference to the node that started the process (internal use)\n         * @trigger open_all.jstree\n         */\n        open_all : function (obj, animation, original_obj) {\n            if(!obj) { obj = $.jstree.root; }\n            obj = this.get_node(obj);\n            if(!obj) { return false; }\n            var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true), i, j, _this;\n            if(!dom.length) {\n                for(i = 0, j = obj.children_d.length; i < j; i++) {\n                    if(this.is_closed(this._model.data[obj.children_d[i]])) {\n                        this._model.data[obj.children_d[i]].state.opened = true;\n                    }\n                }\n                return this.trigger('open_all', { \"node\" : obj });\n            }\n            original_obj = original_obj || dom;\n            _this = this;\n            dom = this.is_closed(obj) ? dom.find('.jstree-closed').addBack() : dom.find('.jstree-closed');\n            dom.each(function () {\n                _this.open_node(\n                    this,\n                    function(node, status) { if(status && this.is_parent(node)) { this.open_all(node, animation, original_obj); } },\n                    animation || 0\n                );\n            });\n            if(original_obj.find('.jstree-closed').length === 0) {\n                /**\n                 * triggered when an `open_all` call completes\n                 * @event\n                 * @name open_all.jstree\n                 * @param {Object} node the opened node\n                 */\n                this.trigger('open_all', { \"node\" : this.get_node(original_obj) });\n            }\n        },\n        /**\n         * closes all nodes within a node (or the tree), revealing their children\n         * @name close_all([obj, animation])\n         * @param {mixed} obj the node to close recursively, omit to close all nodes in the tree\n         * @param {Number} animation the animation duration in milliseconds when closing the nodes, the default is no animation\n         * @trigger close_all.jstree\n         */\n        close_all : function (obj, animation) {\n            if(!obj) { obj = $.jstree.root; }\n            obj = this.get_node(obj);\n            if(!obj) { return false; }\n            var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true),\n                _this = this, i, j;\n            if(dom.length) {\n                dom = this.is_open(obj) ? dom.find('.jstree-open').addBack() : dom.find('.jstree-open');\n                $(dom.get().reverse()).each(function () { _this.close_node(this, animation || 0); });\n            }\n            for(i = 0, j = obj.children_d.length; i < j; i++) {\n                this._model.data[obj.children_d[i]].state.opened = false;\n            }\n            /**\n             * triggered when an `close_all` call completes\n             * @event\n             * @name close_all.jstree\n             * @param {Object} node the closed node\n             */\n            this.trigger('close_all', { \"node\" : obj });\n        },\n        /**\n         * checks if a node is disabled (not selectable)\n         * @name is_disabled(obj)\n         * @param  {mixed} obj\n         * @return {Boolean}\n         */\n        is_disabled : function (obj) {\n            obj = this.get_node(obj);\n            return obj && obj.state && obj.state.disabled;\n        },\n        /**\n         * enables a node - so that it can be selected\n         * @name enable_node(obj)\n         * @param {mixed} obj the node to enable\n         * @trigger enable_node.jstree\n         */\n        enable_node : function (obj) {\n            var t1, t2;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.enable_node(obj[t1]);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            obj.state.disabled = false;\n            this.get_node(obj,true).children('.jstree-anchor').removeClass('jstree-disabled').attr('aria-disabled', false);\n            /**\n             * triggered when an node is enabled\n             * @event\n             * @name enable_node.jstree\n             * @param {Object} node the enabled node\n             */\n            this.trigger('enable_node', { 'node' : obj });\n        },\n        /**\n         * disables a node - so that it can not be selected\n         * @name disable_node(obj)\n         * @param {mixed} obj the node to disable\n         * @trigger disable_node.jstree\n         */\n        disable_node : function (obj) {\n            var t1, t2;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.disable_node(obj[t1]);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            obj.state.disabled = true;\n            this.get_node(obj,true).children('.jstree-anchor').addClass('jstree-disabled').attr('aria-disabled', true);\n            /**\n             * triggered when an node is disabled\n             * @event\n             * @name disable_node.jstree\n             * @param {Object} node the disabled node\n             */\n            this.trigger('disable_node', { 'node' : obj });\n        },\n        /**\n         * determines if a node is hidden\n         * @name is_hidden(obj)\n         * @param {mixed} obj the node\n         */\n        is_hidden : function (obj) {\n            obj = this.get_node(obj);\n            return obj.state.hidden === true;\n        },\n        /**\n         * hides a node - it is still in the structure but will not be visible\n         * @name hide_node(obj)\n         * @param {mixed} obj the node to hide\n         * @param {Boolean} skip_redraw internal parameter controlling if redraw is called\n         * @trigger hide_node.jstree\n         */\n        hide_node : function (obj, skip_redraw) {\n            var t1, t2;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.hide_node(obj[t1], true);\n                }\n                if (!skip_redraw) {\n                    this.redraw();\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            if(!obj.state.hidden) {\n                obj.state.hidden = true;\n                this._node_changed(obj.parent);\n                if(!skip_redraw) {\n                    this.redraw();\n                }\n                /**\n                 * triggered when an node is hidden\n                 * @event\n                 * @name hide_node.jstree\n                 * @param {Object} node the hidden node\n                 */\n                this.trigger('hide_node', { 'node' : obj });\n            }\n        },\n        /**\n         * shows a node\n         * @name show_node(obj)\n         * @param {mixed} obj the node to show\n         * @param {Boolean} skip_redraw internal parameter controlling if redraw is called\n         * @trigger show_node.jstree\n         */\n        show_node : function (obj, skip_redraw) {\n            var t1, t2;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.show_node(obj[t1], true);\n                }\n                if (!skip_redraw) {\n                    this.redraw();\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            if(obj.state.hidden) {\n                obj.state.hidden = false;\n                this._node_changed(obj.parent);\n                if(!skip_redraw) {\n                    this.redraw();\n                }\n                /**\n                 * triggered when an node is shown\n                 * @event\n                 * @name show_node.jstree\n                 * @param {Object} node the shown node\n                 */\n                this.trigger('show_node', { 'node' : obj });\n            }\n        },\n        /**\n         * hides all nodes\n         * @name hide_all()\n         * @trigger hide_all.jstree\n         */\n        hide_all : function (skip_redraw) {\n            var i, m = this._model.data, ids = [];\n            for(i in m) {\n                if(m.hasOwnProperty(i) && i !== $.jstree.root && !m[i].state.hidden) {\n                    m[i].state.hidden = true;\n                    ids.push(i);\n                }\n            }\n            this._model.force_full_redraw = true;\n            if(!skip_redraw) {\n                this.redraw();\n            }\n            /**\n             * triggered when all nodes are hidden\n             * @event\n             * @name hide_all.jstree\n             * @param {Array} nodes the IDs of all hidden nodes\n             */\n            this.trigger('hide_all', { 'nodes' : ids });\n            return ids;\n        },\n        /**\n         * shows all nodes\n         * @name show_all()\n         * @trigger show_all.jstree\n         */\n        show_all : function (skip_redraw) {\n            var i, m = this._model.data, ids = [];\n            for(i in m) {\n                if(m.hasOwnProperty(i) && i !== $.jstree.root && m[i].state.hidden) {\n                    m[i].state.hidden = false;\n                    ids.push(i);\n                }\n            }\n            this._model.force_full_redraw = true;\n            if(!skip_redraw) {\n                this.redraw();\n            }\n            /**\n             * triggered when all nodes are shown\n             * @event\n             * @name show_all.jstree\n             * @param {Array} nodes the IDs of all shown nodes\n             */\n            this.trigger('show_all', { 'nodes' : ids });\n            return ids;\n        },\n        /**\n         * called when a node is selected by the user. Used internally.\n         * @private\n         * @name activate_node(obj, e)\n         * @param {mixed} obj the node\n         * @param {Object} e the related event\n         * @trigger activate_node.jstree, changed.jstree\n         */\n        activate_node : function (obj, e) {\n            if(this.is_disabled(obj)) {\n                return false;\n            }\n            if(!e || typeof e !== 'object') {\n                e = {};\n            }\n\n            // ensure last_clicked is still in the DOM, make it fresh (maybe it was moved?) and make sure it is still selected, if not - make last_clicked the last selected node\n            this._data.core.last_clicked = this._data.core.last_clicked && this._data.core.last_clicked.id !== undefined ? this.get_node(this._data.core.last_clicked.id) : null;\n            if(this._data.core.last_clicked && !this._data.core.last_clicked.state.selected) { this._data.core.last_clicked = null; }\n            if(!this._data.core.last_clicked && this._data.core.selected.length) { this._data.core.last_clicked = this.get_node(this._data.core.selected[this._data.core.selected.length - 1]); }\n\n            if(!this.settings.core.multiple || (!e.metaKey && !e.ctrlKey && !e.shiftKey) || (e.shiftKey && (!this._data.core.last_clicked || !this.get_parent(obj) || this.get_parent(obj) !== this._data.core.last_clicked.parent ) )) {\n                if(!this.settings.core.multiple && (e.metaKey || e.ctrlKey || e.shiftKey) && this.is_selected(obj)) {\n                    this.deselect_node(obj, false, e);\n                }\n                else {\n                    this.deselect_all(true);\n                    this.select_node(obj, false, false, e);\n                    this._data.core.last_clicked = this.get_node(obj);\n                }\n            }\n            else {\n                if(e.shiftKey) {\n                    var o = this.get_node(obj).id,\n                        l = this._data.core.last_clicked.id,\n                        p = this.get_node(this._data.core.last_clicked.parent).children,\n                        c = false,\n                        i, j;\n                    for(i = 0, j = p.length; i < j; i += 1) {\n                        // separate IFs work whem o and l are the same\n                        if(p[i] === o) {\n                            c = !c;\n                        }\n                        if(p[i] === l) {\n                            c = !c;\n                        }\n                        if(!this.is_disabled(p[i]) && (c || p[i] === o || p[i] === l)) {\n                            if (!this.is_hidden(p[i])) {\n                                this.select_node(p[i], true, false, e);\n                            }\n                        }\n                        else {\n                            this.deselect_node(p[i], true, e);\n                        }\n                    }\n                    this.trigger('changed', { 'action' : 'select_node', 'node' : this.get_node(obj), 'selected' : this._data.core.selected, 'event' : e });\n                }\n                else {\n                    if(!this.is_selected(obj)) {\n                        this.select_node(obj, false, false, e);\n                    }\n                    else {\n                        this.deselect_node(obj, false, e);\n                    }\n                }\n            }\n            /**\n             * triggered when an node is clicked or intercated with by the user\n             * @event\n             * @name activate_node.jstree\n             * @param {Object} node\n             * @param {Object} event the ooriginal event (if any) which triggered the call (may be an empty object)\n             */\n            this.trigger('activate_node', { 'node' : this.get_node(obj), 'event' : e });\n        },\n        /**\n         * applies the hover state on a node, called when a node is hovered by the user. Used internally.\n         * @private\n         * @name hover_node(obj)\n         * @param {mixed} obj\n         * @trigger hover_node.jstree\n         */\n        hover_node : function (obj) {\n            obj = this.get_node(obj, true);\n            if(!obj || !obj.length || obj.children('.jstree-hovered').length) {\n                return false;\n            }\n            var o = this.element.find('.jstree-hovered'), t = this.element;\n            if(o && o.length) { this.dehover_node(o); }\n\n            obj.children('.jstree-anchor').addClass('jstree-hovered');\n            /**\n             * triggered when an node is hovered\n             * @event\n             * @name hover_node.jstree\n             * @param {Object} node\n             */\n            this.trigger('hover_node', { 'node' : this.get_node(obj) });\n            setTimeout(function () { t.attr('aria-activedescendant', obj[0].id); }, 0);\n        },\n        /**\n         * removes the hover state from a nodecalled when a node is no longer hovered by the user. Used internally.\n         * @private\n         * @name dehover_node(obj)\n         * @param {mixed} obj\n         * @trigger dehover_node.jstree\n         */\n        dehover_node : function (obj) {\n            obj = this.get_node(obj, true);\n            if(!obj || !obj.length || !obj.children('.jstree-hovered').length) {\n                return false;\n            }\n            obj.children('.jstree-anchor').removeClass('jstree-hovered');\n            /**\n             * triggered when an node is no longer hovered\n             * @event\n             * @name dehover_node.jstree\n             * @param {Object} node\n             */\n            this.trigger('dehover_node', { 'node' : this.get_node(obj) });\n        },\n        /**\n         * select a node\n         * @name select_node(obj [, supress_event, prevent_open])\n         * @param {mixed} obj an array can be used to select multiple nodes\n         * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered\n         * @param {Boolean} prevent_open if set to `true` parents of the selected node won't be opened\n         * @trigger select_node.jstree, changed.jstree\n         */\n        select_node : function (obj, supress_event, prevent_open, e) {\n            var dom, t1, t2, th;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.select_node(obj[t1], supress_event, prevent_open, e);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            dom = this.get_node(obj, true);\n            if(!obj.state.selected) {\n                obj.state.selected = true;\n                this._data.core.selected.push(obj.id);\n                if(!prevent_open) {\n                    dom = this._open_to(obj);\n                }\n                if(dom && dom.length) {\n                    dom.children('.jstree-anchor').addClass('jstree-clicked').attr('aria-selected', true);\n                }\n                /**\n                 * triggered when an node is selected\n                 * @event\n                 * @name select_node.jstree\n                 * @param {Object} node\n                 * @param {Array} selected the current selection\n                 * @param {Object} event the event (if any) that triggered this select_node\n                 */\n                this.trigger('select_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });\n                if(!supress_event) {\n                    /**\n                     * triggered when selection changes\n                     * @event\n                     * @name changed.jstree\n                     * @param {Object} node\n                     * @param {Object} action the action that caused the selection to change\n                     * @param {Array} selected the current selection\n                     * @param {Object} event the event (if any) that triggered this changed event\n                     */\n                    this.trigger('changed', { 'action' : 'select_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });\n                }\n            }\n        },\n        /**\n         * deselect a node\n         * @name deselect_node(obj [, supress_event])\n         * @param {mixed} obj an array can be used to deselect multiple nodes\n         * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered\n         * @trigger deselect_node.jstree, changed.jstree\n         */\n        deselect_node : function (obj, supress_event, e) {\n            var t1, t2, dom;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.deselect_node(obj[t1], supress_event, e);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            dom = this.get_node(obj, true);\n            if(obj.state.selected) {\n                obj.state.selected = false;\n                this._data.core.selected = $.vakata.array_remove_item(this._data.core.selected, obj.id);\n                if(dom.length) {\n                    dom.children('.jstree-anchor').removeClass('jstree-clicked').attr('aria-selected', false);\n                }\n                /**\n                 * triggered when an node is deselected\n                 * @event\n                 * @name deselect_node.jstree\n                 * @param {Object} node\n                 * @param {Array} selected the current selection\n                 * @param {Object} event the event (if any) that triggered this deselect_node\n                 */\n                this.trigger('deselect_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });\n                if(!supress_event) {\n                    this.trigger('changed', { 'action' : 'deselect_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });\n                }\n            }\n        },\n        /**\n         * select all nodes in the tree\n         * @name select_all([supress_event])\n         * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered\n         * @trigger select_all.jstree, changed.jstree\n         */\n        select_all : function (supress_event) {\n            var tmp = this._data.core.selected.concat([]), i, j;\n            this._data.core.selected = this._model.data[$.jstree.root].children_d.concat();\n            for(i = 0, j = this._data.core.selected.length; i < j; i++) {\n                if(this._model.data[this._data.core.selected[i]]) {\n                    this._model.data[this._data.core.selected[i]].state.selected = true;\n                }\n            }\n            this.redraw(true);\n            /**\n             * triggered when all nodes are selected\n             * @event\n             * @name select_all.jstree\n             * @param {Array} selected the current selection\n             */\n            this.trigger('select_all', { 'selected' : this._data.core.selected });\n            if(!supress_event) {\n                this.trigger('changed', { 'action' : 'select_all', 'selected' : this._data.core.selected, 'old_selection' : tmp });\n            }\n        },\n        /**\n         * deselect all selected nodes\n         * @name deselect_all([supress_event])\n         * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered\n         * @trigger deselect_all.jstree, changed.jstree\n         */\n        deselect_all : function (supress_event) {\n            var tmp = this._data.core.selected.concat([]), i, j;\n            for(i = 0, j = this._data.core.selected.length; i < j; i++) {\n                if(this._model.data[this._data.core.selected[i]]) {\n                    this._model.data[this._data.core.selected[i]].state.selected = false;\n                }\n            }\n            this._data.core.selected = [];\n            this.element.find('.jstree-clicked').removeClass('jstree-clicked').attr('aria-selected', false);\n            /**\n             * triggered when all nodes are deselected\n             * @event\n             * @name deselect_all.jstree\n             * @param {Object} node the previous selection\n             * @param {Array} selected the current selection\n             */\n            this.trigger('deselect_all', { 'selected' : this._data.core.selected, 'node' : tmp });\n            if(!supress_event) {\n                this.trigger('changed', { 'action' : 'deselect_all', 'selected' : this._data.core.selected, 'old_selection' : tmp });\n            }\n        },\n        /**\n         * checks if a node is selected\n         * @name is_selected(obj)\n         * @param  {mixed}  obj\n         * @return {Boolean}\n         */\n        is_selected : function (obj) {\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            return obj.state.selected;\n        },\n        /**\n         * get an array of all selected nodes\n         * @name get_selected([full])\n         * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned\n         * @return {Array}\n         */\n        get_selected : function (full) {\n            return full ? $.map(this._data.core.selected, function (i) { return this.get_node(i); }.bind(this)) : this._data.core.selected.slice();\n        },\n        /**\n         * get an array of all top level selected nodes (ignoring children of selected nodes)\n         * @name get_top_selected([full])\n         * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned\n         * @return {Array}\n         */\n        get_top_selected : function (full) {\n            var tmp = this.get_selected(true),\n                obj = {}, i, j, k, l;\n            for(i = 0, j = tmp.length; i < j; i++) {\n                obj[tmp[i].id] = tmp[i];\n            }\n            for(i = 0, j = tmp.length; i < j; i++) {\n                for(k = 0, l = tmp[i].children_d.length; k < l; k++) {\n                    if(obj[tmp[i].children_d[k]]) {\n                        delete obj[tmp[i].children_d[k]];\n                    }\n                }\n            }\n            tmp = [];\n            for(i in obj) {\n                if(obj.hasOwnProperty(i)) {\n                    tmp.push(i);\n                }\n            }\n            return full ? $.map(tmp, function (i) { return this.get_node(i); }.bind(this)) : tmp;\n        },\n        /**\n         * get an array of all bottom level selected nodes (ignoring selected parents)\n         * @name get_bottom_selected([full])\n         * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned\n         * @return {Array}\n         */\n        get_bottom_selected : function (full) {\n            var tmp = this.get_selected(true),\n                obj = [], i, j;\n            for(i = 0, j = tmp.length; i < j; i++) {\n                if(!tmp[i].children.length) {\n                    obj.push(tmp[i].id);\n                }\n            }\n            return full ? $.map(obj, function (i) { return this.get_node(i); }.bind(this)) : obj;\n        },\n        /**\n         * gets the current state of the tree so that it can be restored later with `set_state(state)`. Used internally.\n         * @name get_state()\n         * @private\n         * @return {Object}\n         */\n        get_state : function () {\n            var state\t= {\n                'core' : {\n                    'open' : [],\n                    'loaded' : [],\n                    'scroll' : {\n                        'left' : this.element.scrollLeft(),\n                        'top' : this.element.scrollTop()\n                    },\n                    /*!\n\t\t\t\t\t'themes' : {\n\t\t\t\t\t\t'name' : this.get_theme(),\n\t\t\t\t\t\t'icons' : this._data.core.themes.icons,\n\t\t\t\t\t\t'dots' : this._data.core.themes.dots\n\t\t\t\t\t},\n\t\t\t\t\t*/\n                    'selected' : []\n                }\n            }, i;\n            for(i in this._model.data) {\n                if(this._model.data.hasOwnProperty(i)) {\n                    if(i !== $.jstree.root) {\n                        if(this._model.data[i].state.loaded && this.settings.core.loaded_state) {\n                            state.core.loaded.push(i);\n                        }\n                        if(this._model.data[i].state.opened) {\n                            state.core.open.push(i);\n                        }\n                        if(this._model.data[i].state.selected) {\n                            state.core.selected.push(i);\n                        }\n                    }\n                }\n            }\n            return state;\n        },\n        /**\n         * sets the state of the tree. Used internally.\n         * @name set_state(state [, callback])\n         * @private\n         * @param {Object} state the state to restore. Keep in mind this object is passed by reference and jstree will modify it.\n         * @param {Function} callback an optional function to execute once the state is restored.\n         * @trigger set_state.jstree\n         */\n        set_state : function (state, callback) {\n            if(state) {\n                if(state.core && state.core.selected && state.core.initial_selection === undefined) {\n                    state.core.initial_selection = this._data.core.selected.concat([]).sort().join(',');\n                }\n                if(state.core) {\n                    var res, n, t, _this, i;\n                    if(state.core.loaded) {\n                        if(!this.settings.core.loaded_state || !$.vakata.is_array(state.core.loaded) || !state.core.loaded.length) {\n                            delete state.core.loaded;\n                            this.set_state(state, callback);\n                        }\n                        else {\n                            this._load_nodes(state.core.loaded, function (nodes) {\n                                delete state.core.loaded;\n                                this.set_state(state, callback);\n                            });\n                        }\n                        return false;\n                    }\n                    if(state.core.open) {\n                        if(!$.vakata.is_array(state.core.open) || !state.core.open.length) {\n                            delete state.core.open;\n                            this.set_state(state, callback);\n                        }\n                        else {\n                            this._load_nodes(state.core.open, function (nodes) {\n                                this.open_node(nodes, false, 0);\n                                delete state.core.open;\n                                this.set_state(state, callback);\n                            });\n                        }\n                        return false;\n                    }\n                    if(state.core.scroll) {\n                        if(state.core.scroll && state.core.scroll.left !== undefined) {\n                            this.element.scrollLeft(state.core.scroll.left);\n                        }\n                        if(state.core.scroll && state.core.scroll.top !== undefined) {\n                            this.element.scrollTop(state.core.scroll.top);\n                        }\n                        delete state.core.scroll;\n                        this.set_state(state, callback);\n                        return false;\n                    }\n                    if(state.core.selected) {\n                        _this = this;\n                        if (state.core.initial_selection === undefined ||\n                            state.core.initial_selection === this._data.core.selected.concat([]).sort().join(',')\n                        ) {\n                            this.deselect_all();\n                            $.each(state.core.selected, function (i, v) {\n                                _this.select_node(v, false, true);\n                            });\n                        }\n                        delete state.core.initial_selection;\n                        delete state.core.selected;\n                        this.set_state(state, callback);\n                        return false;\n                    }\n                    for(i in state) {\n                        if(state.hasOwnProperty(i) && i !== \"core\" && $.inArray(i, this.settings.plugins) === -1) {\n                            delete state[i];\n                        }\n                    }\n                    if($.isEmptyObject(state.core)) {\n                        delete state.core;\n                        this.set_state(state, callback);\n                        return false;\n                    }\n                }\n                if($.isEmptyObject(state)) {\n                    state = null;\n                    if(callback) { callback.call(this); }\n                    /**\n                     * triggered when a `set_state` call completes\n                     * @event\n                     * @name set_state.jstree\n                     */\n                    this.trigger('set_state');\n                    return false;\n                }\n                return true;\n            }\n            return false;\n        },\n        /**\n         * refreshes the tree - all nodes are reloaded with calls to `load_node`.\n         * @name refresh()\n         * @param {Boolean} skip_loading an option to skip showing the loading indicator\n         * @param {Mixed} forget_state if set to `true` state will not be reapplied, if set to a function (receiving the current state as argument) the result of that function will be used as state\n         * @trigger refresh.jstree\n         */\n        refresh : function (skip_loading, forget_state) {\n            this._data.core.state = forget_state === true ? {} : this.get_state();\n            if(forget_state && $.vakata.is_function(forget_state)) { this._data.core.state = forget_state.call(this, this._data.core.state); }\n            this._cnt = 0;\n            this._model.data = {};\n            this._model.data[$.jstree.root] = {\n                id : $.jstree.root,\n                parent : null,\n                parents : [],\n                children : [],\n                children_d : [],\n                state : { loaded : false }\n            };\n            this._data.core.selected = [];\n            this._data.core.last_clicked = null;\n            this._data.core.focused = null;\n\n            var c = this.get_container_ul()[0].className;\n            if(!skip_loading) {\n                this.element.html(\"<\"+\"ul class='\"+c+\"' role='group'><\"+\"li class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='none' id='j\"+this._id+\"_loading'><i class='jstree-icon jstree-ocl'></i><\"+\"a class='jstree-anchor' role='treeitem' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>\" + this.get_string(\"Loading ...\") + \"</a></li></ul>\");\n                this.element.attr('aria-activedescendant','j'+this._id+'_loading');\n            }\n            this.load_node($.jstree.root, function (o, s) {\n                if(s) {\n                    this.get_container_ul()[0].className = c;\n                    if(this._firstChild(this.get_container_ul()[0])) {\n                        this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);\n                    }\n                    this.set_state($.extend(true, {}, this._data.core.state), function () {\n                        /**\n                         * triggered when a `refresh` call completes\n                         * @event\n                         * @name refresh.jstree\n                         */\n                        this.trigger('refresh');\n                    });\n                }\n                this._data.core.state = null;\n            });\n        },\n        /**\n         * refreshes a node in the tree (reload its children) all opened nodes inside that node are reloaded with calls to `load_node`.\n         * @name refresh_node(obj)\n         * @param  {mixed} obj the node\n         * @trigger refresh_node.jstree\n         */\n        refresh_node : function (obj) {\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n            var opened = [], to_load = [], s = this._data.core.selected.concat([]);\n            to_load.push(obj.id);\n            if(obj.state.opened === true) { opened.push(obj.id); }\n            this.get_node(obj, true).find('.jstree-open').each(function() { to_load.push(this.id); opened.push(this.id); });\n            this._load_nodes(to_load, function (nodes) {\n                this.open_node(opened, false, 0);\n                this.select_node(s);\n                /**\n                 * triggered when a node is refreshed\n                 * @event\n                 * @name refresh_node.jstree\n                 * @param {Object} node - the refreshed node\n                 * @param {Array} nodes - an array of the IDs of the nodes that were reloaded\n                 */\n                this.trigger('refresh_node', { 'node' : obj, 'nodes' : nodes });\n            }.bind(this), false, true);\n        },\n        /**\n         * set (change) the ID of a node\n         * @name set_id(obj, id)\n         * @param  {mixed} obj the node\n         * @param  {String} id the new ID\n         * @return {Boolean}\n         * @trigger set_id.jstree\n         */\n        set_id : function (obj, id) {\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n            var i, j, m = this._model.data, old = obj.id;\n            id = id.toString();\n            // update parents (replace current ID with new one in children and children_d)\n            m[obj.parent].children[$.inArray(obj.id, m[obj.parent].children)] = id;\n            for(i = 0, j = obj.parents.length; i < j; i++) {\n                m[obj.parents[i]].children_d[$.inArray(obj.id, m[obj.parents[i]].children_d)] = id;\n            }\n            // update children (replace current ID with new one in parent and parents)\n            for(i = 0, j = obj.children.length; i < j; i++) {\n                m[obj.children[i]].parent = id;\n            }\n            for(i = 0, j = obj.children_d.length; i < j; i++) {\n                m[obj.children_d[i]].parents[$.inArray(obj.id, m[obj.children_d[i]].parents)] = id;\n            }\n            i = $.inArray(obj.id, this._data.core.selected);\n            if(i !== -1) { this._data.core.selected[i] = id; }\n            // update model and obj itself (obj.id, this._model.data[KEY])\n            i = this.get_node(obj.id, true);\n            if(i) {\n                i.attr('id', id); //.children('.jstree-anchor').attr('id', id + '_anchor').end().attr('aria-labelledby', id + '_anchor');\n                if(this.element.attr('aria-activedescendant') === obj.id) {\n                    this.element.attr('aria-activedescendant', id);\n                }\n            }\n            delete m[obj.id];\n            obj.id = id;\n            obj.li_attr.id = id;\n            m[id] = obj;\n            /**\n             * triggered when a node id value is changed\n             * @event\n             * @name set_id.jstree\n             * @param {Object} node\n             * @param {String} old the old id\n             */\n            this.trigger('set_id',{ \"node\" : obj, \"new\" : obj.id, \"old\" : old });\n            return true;\n        },\n        /**\n         * get the text value of a node\n         * @name get_text(obj)\n         * @param  {mixed} obj the node\n         * @return {String}\n         */\n        get_text : function (obj) {\n            obj = this.get_node(obj);\n            return (!obj || obj.id === $.jstree.root) ? false : obj.text;\n        },\n        /**\n         * set the text value of a node. Used internally, please use `rename_node(obj, val)`.\n         * @private\n         * @name set_text(obj, val)\n         * @param  {mixed} obj the node, you can pass an array to set the text on multiple nodes\n         * @param  {String} val the new text value\n         * @return {Boolean}\n         * @trigger set_text.jstree\n         */\n        set_text : function (obj, val) {\n            var t1, t2;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.set_text(obj[t1], val);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n            obj.text = val;\n            if(this.get_node(obj, true).length) {\n                this.redraw_node(obj.id);\n            }\n            /**\n             * triggered when a node text value is changed\n             * @event\n             * @name set_text.jstree\n             * @param {Object} obj\n             * @param {String} text the new value\n             */\n            this.trigger('set_text',{ \"obj\" : obj, \"text\" : val });\n            return true;\n        },\n        /**\n         * gets a JSON representation of a node (or the whole tree)\n         * @name get_json([obj, options])\n         * @param  {mixed} obj\n         * @param  {Object} options\n         * @param  {Boolean} options.no_state do not return state information\n         * @param  {Boolean} options.no_id do not return ID\n         * @param  {Boolean} options.no_children do not include children\n         * @param  {Boolean} options.no_data do not include node data\n         * @param  {Boolean} options.no_li_attr do not include LI attributes\n         * @param  {Boolean} options.no_a_attr do not include A attributes\n         * @param  {Boolean} options.flat return flat JSON instead of nested\n         * @return {Object}\n         */\n        get_json : function (obj, options, flat) {\n            obj = this.get_node(obj || $.jstree.root);\n            if(!obj) { return false; }\n            if(options && options.flat && !flat) { flat = []; }\n            var tmp = {\n                'id' : obj.id,\n                'text' : obj.text,\n                'icon' : this.get_icon(obj),\n                'li_attr' : $.extend(true, {}, obj.li_attr),\n                'a_attr' : $.extend(true, {}, obj.a_attr),\n                'state' : {},\n                'data' : options && options.no_data ? false : $.extend(true, $.vakata.is_array(obj.data)?[]:{}, obj.data)\n                //( this.get_node(obj, true).length ? this.get_node(obj, true).data() : obj.data ),\n            }, i, j;\n            if(options && options.flat) {\n                tmp.parent = obj.parent;\n            }\n            else {\n                tmp.children = [];\n            }\n            if(!options || !options.no_state) {\n                for(i in obj.state) {\n                    if(obj.state.hasOwnProperty(i)) {\n                        tmp.state[i] = obj.state[i];\n                    }\n                }\n            } else {\n                delete tmp.state;\n            }\n            if(options && options.no_li_attr) {\n                delete tmp.li_attr;\n            }\n            if(options && options.no_a_attr) {\n                delete tmp.a_attr;\n            }\n            if(options && options.no_id) {\n                delete tmp.id;\n                if(tmp.li_attr && tmp.li_attr.id) {\n                    delete tmp.li_attr.id;\n                }\n                if(tmp.a_attr && tmp.a_attr.id) {\n                    delete tmp.a_attr.id;\n                }\n            }\n            if(options && options.flat && obj.id !== $.jstree.root) {\n                flat.push(tmp);\n            }\n            if(!options || !options.no_children) {\n                for(i = 0, j = obj.children.length; i < j; i++) {\n                    if(options && options.flat) {\n                        this.get_json(obj.children[i], options, flat);\n                    }\n                    else {\n                        tmp.children.push(this.get_json(obj.children[i], options));\n                    }\n                }\n            }\n            return options && options.flat ? flat : (obj.id === $.jstree.root ? tmp.children : tmp);\n        },\n        /**\n         * create a new node (do not confuse with load_node)\n         * @name create_node([par, node, pos, callback, is_loaded])\n         * @param  {mixed}   par       the parent node (to create a root node use either \"#\" (string) or `null`)\n         * @param  {mixed}   node      the data for the new node (a valid JSON object, or a simple string with the name)\n         * @param  {mixed}   pos       the index at which to insert the node, \"first\" and \"last\" are also supported, default is \"last\"\n         * @param  {Function} callback a function to be called once the node is created\n         * @param  {Boolean} is_loaded internal argument indicating if the parent node was succesfully loaded\n         * @return {String}            the ID of the newly create node\n         * @trigger model.jstree, create_node.jstree\n         */\n        create_node : function (par, node, pos, callback, is_loaded) {\n            if(par === null) { par = $.jstree.root; }\n            par = this.get_node(par);\n            if(!par) { return false; }\n            pos = pos === undefined ? \"last\" : pos;\n            if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {\n                return this.load_node(par, function () { this.create_node(par, node, pos, callback, true); });\n            }\n            if(!node) { node = { \"text\" : this.get_string('New node') }; }\n            if(typeof node === \"string\") {\n                node = { \"text\" : node };\n            } else {\n                node = $.extend(true, {}, node);\n            }\n            if(node.text === undefined) { node.text = this.get_string('New node'); }\n            var tmp, dpc, i, j;\n\n            if(par.id === $.jstree.root) {\n                if(pos === \"before\") { pos = \"first\"; }\n                if(pos === \"after\") { pos = \"last\"; }\n            }\n            switch(pos) {\n                case \"before\":\n                    tmp = this.get_node(par.parent);\n                    pos = $.inArray(par.id, tmp.children);\n                    par = tmp;\n                    break;\n                case \"after\" :\n                    tmp = this.get_node(par.parent);\n                    pos = $.inArray(par.id, tmp.children) + 1;\n                    par = tmp;\n                    break;\n                case \"inside\":\n                case \"first\":\n                    pos = 0;\n                    break;\n                case \"last\":\n                    pos = par.children.length;\n                    break;\n                default:\n                    if(!pos) { pos = 0; }\n                    break;\n            }\n            if(pos > par.children.length) { pos = par.children.length; }\n            if(!node.id) { node.id = true; }\n            if(!this.check(\"create_node\", node, par, pos)) {\n                this.settings.core.error.call(this, this._data.core.last_error);\n                return false;\n            }\n            if(node.id === true) { delete node.id; }\n            node = this._parse_model_from_json(node, par.id, par.parents.concat());\n            if(!node) { return false; }\n            tmp = this.get_node(node);\n            dpc = [];\n            dpc.push(node);\n            dpc = dpc.concat(tmp.children_d);\n            this.trigger('model', { \"nodes\" : dpc, \"parent\" : par.id });\n\n            par.children_d = par.children_d.concat(dpc);\n            for(i = 0, j = par.parents.length; i < j; i++) {\n                this._model.data[par.parents[i]].children_d = this._model.data[par.parents[i]].children_d.concat(dpc);\n            }\n            node = tmp;\n            tmp = [];\n            for(i = 0, j = par.children.length; i < j; i++) {\n                tmp[i >= pos ? i+1 : i] = par.children[i];\n            }\n            tmp[pos] = node.id;\n            par.children = tmp;\n\n            this.redraw_node(par, true);\n            /**\n             * triggered when a node is created\n             * @event\n             * @name create_node.jstree\n             * @param {Object} node\n             * @param {String} parent the parent's ID\n             * @param {Number} position the position of the new node among the parent's children\n             */\n            this.trigger('create_node', { \"node\" : this.get_node(node), \"parent\" : par.id, \"position\" : pos });\n            if(callback) { callback.call(this, this.get_node(node)); }\n            return node.id;\n        },\n        /**\n         * set the text value of a node\n         * @name rename_node(obj, val)\n         * @param  {mixed} obj the node, you can pass an array to rename multiple nodes to the same name\n         * @param  {String} val the new text value\n         * @return {Boolean}\n         * @trigger rename_node.jstree\n         */\n        rename_node : function (obj, val) {\n            var t1, t2, old;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.rename_node(obj[t1], val);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n            old = obj.text;\n            if(!this.check(\"rename_node\", obj, this.get_parent(obj), val)) {\n                this.settings.core.error.call(this, this._data.core.last_error);\n                return false;\n            }\n            this.set_text(obj, val); // .apply(this, Array.prototype.slice.call(arguments))\n            /**\n             * triggered when a node is renamed\n             * @event\n             * @name rename_node.jstree\n             * @param {Object} node\n             * @param {String} text the new value\n             * @param {String} old the old value\n             */\n            this.trigger('rename_node', { \"node\" : obj, \"text\" : val, \"old\" : old });\n            return true;\n        },\n        /**\n         * remove a node\n         * @name delete_node(obj)\n         * @param  {mixed} obj the node, you can pass an array to delete multiple nodes\n         * @return {Boolean}\n         * @trigger delete_node.jstree, changed.jstree\n         */\n        delete_node : function (obj) {\n            var t1, t2, par, pos, tmp, i, j, k, l, c, top, lft;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.delete_node(obj[t1]);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n            par = this.get_node(obj.parent);\n            pos = $.inArray(obj.id, par.children);\n            c = false;\n            if(!this.check(\"delete_node\", obj, par, pos)) {\n                this.settings.core.error.call(this, this._data.core.last_error);\n                return false;\n            }\n            if(pos !== -1) {\n                par.children = $.vakata.array_remove(par.children, pos);\n            }\n            tmp = obj.children_d.concat([]);\n            tmp.push(obj.id);\n            for(i = 0, j = obj.parents.length; i < j; i++) {\n                this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) {\n                    return $.inArray(v, tmp) === -1;\n                });\n            }\n            for(k = 0, l = tmp.length; k < l; k++) {\n                if(this._model.data[tmp[k]].state.selected) {\n                    c = true;\n                    break;\n                }\n            }\n            if (c) {\n                this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) {\n                    return $.inArray(v, tmp) === -1;\n                });\n            }\n            /**\n             * triggered when a node is deleted\n             * @event\n             * @name delete_node.jstree\n             * @param {Object} node\n             * @param {String} parent the parent's ID\n             */\n            this.trigger('delete_node', { \"node\" : obj, \"parent\" : par.id });\n            if(c) {\n                this.trigger('changed', { 'action' : 'delete_node', 'node' : obj, 'selected' : this._data.core.selected, 'parent' : par.id });\n            }\n            for(k = 0, l = tmp.length; k < l; k++) {\n                delete this._model.data[tmp[k]];\n            }\n            if($.inArray(this._data.core.focused, tmp) !== -1) {\n                this._data.core.focused = null;\n                top = this.element[0].scrollTop;\n                lft = this.element[0].scrollLeft;\n                if(par.id === $.jstree.root) {\n                    if (this._model.data[$.jstree.root].children[0]) {\n                        this.get_node(this._model.data[$.jstree.root].children[0], true).children('.jstree-anchor').triger('focus');\n                    }\n                }\n                else {\n                    this.get_node(par, true).children('.jstree-anchor').trigger('focus');\n                }\n                this.element[0].scrollTop  = top;\n                this.element[0].scrollLeft = lft;\n            }\n            this.redraw_node(par, true);\n            return true;\n        },\n        /**\n         * check if an operation is premitted on the tree. Used internally.\n         * @private\n         * @name check(chk, obj, par, pos)\n         * @param  {String} chk the operation to check, can be \"create_node\", \"rename_node\", \"delete_node\", \"copy_node\" or \"move_node\"\n         * @param  {mixed} obj the node\n         * @param  {mixed} par the parent\n         * @param  {mixed} pos the position to insert at, or if \"rename_node\" - the new name\n         * @param  {mixed} more some various additional information, for example if a \"move_node\" operations is triggered by DND this will be the hovered node\n         * @return {Boolean}\n         */\n        check : function (chk, obj, par, pos, more) {\n            obj = obj && obj.id ? obj : this.get_node(obj);\n            par = par && par.id ? par : this.get_node(par);\n            var tmp = chk.match(/^move_node|copy_node|create_node$/i) ? par : obj,\n                chc = this.settings.core.check_callback;\n            if(chk === \"move_node\" || chk === \"copy_node\") {\n                if((!more || !more.is_multi) && (chk === \"move_node\" && $.inArray(obj.id, par.children) === pos)) {\n                    this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_08', 'reason' : 'Moving node to its current position', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                    return false;\n                }\n                if((!more || !more.is_multi) && (obj.id === par.id || (chk === \"move_node\" && $.inArray(obj.id, par.children) === pos) || $.inArray(par.id, obj.children_d) !== -1)) {\n                    this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_01', 'reason' : 'Moving parent inside child', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                    return false;\n                }\n            }\n            if(tmp && tmp.data) { tmp = tmp.data; }\n            if(tmp && tmp.functions && (tmp.functions[chk] === false || tmp.functions[chk] === true)) {\n                if(tmp.functions[chk] === false) {\n                    this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_02', 'reason' : 'Node data prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                }\n                return tmp.functions[chk];\n            }\n            if(chc === false || ($.vakata.is_function(chc) && chc.call(this, chk, obj, par, pos, more) === false) || (chc && chc[chk] === false)) {\n                this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_03', 'reason' : 'User config for core.check_callback prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                return false;\n            }\n            return true;\n        },\n        /**\n         * get the last error\n         * @name last_error()\n         * @return {Object}\n         */\n        last_error : function () {\n            return this._data.core.last_error;\n        },\n        /**\n         * move a node to a new parent\n         * @name move_node(obj, par [, pos, callback, is_loaded])\n         * @param  {mixed} obj the node to move, pass an array to move multiple nodes\n         * @param  {mixed} par the new parent\n         * @param  {mixed} pos the position to insert at (besides integer values, \"first\" and \"last\" are supported, as well as \"before\" and \"after\"), defaults to integer `0`\n         * @param  {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position\n         * @param  {Boolean} is_loaded internal parameter indicating if the parent node has been loaded\n         * @param  {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn\n         * @param  {Boolean} instance internal parameter indicating if the node comes from another instance\n         * @trigger move_node.jstree\n         */\n        move_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) {\n            var t1, t2, old_par, old_pos, new_par, old_ins, is_multi, dpc, tmp, i, j, k, l, p;\n\n            par = this.get_node(par);\n            pos = pos === undefined ? 0 : pos;\n            if(!par) { return false; }\n            if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {\n                return this.load_node(par, function () { this.move_node(obj, par, pos, callback, true, false, origin); });\n            }\n\n            if($.vakata.is_array(obj)) {\n                if(obj.length === 1) {\n                    obj = obj[0];\n                }\n                else {\n                    //obj = obj.slice();\n                    for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                        if((tmp = this.move_node(obj[t1], par, pos, callback, is_loaded, false, origin))) {\n                            par = tmp;\n                            pos = \"after\";\n                        }\n                    }\n                    this.redraw();\n                    return true;\n                }\n            }\n            obj = obj && obj.id ? obj : this.get_node(obj);\n\n            if(!obj || obj.id === $.jstree.root) { return false; }\n\n            old_par = (obj.parent || $.jstree.root).toString();\n            new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent);\n            old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id));\n            is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id);\n            old_pos = old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1;\n            if(old_ins && old_ins._id) {\n                obj = old_ins._model.data[obj.id];\n            }\n\n            if(is_multi) {\n                if((tmp = this.copy_node(obj, par, pos, callback, is_loaded, false, origin))) {\n                    if(old_ins) { old_ins.delete_node(obj); }\n                    return tmp;\n                }\n                return false;\n            }\n            //var m = this._model.data;\n            if(par.id === $.jstree.root) {\n                if(pos === \"before\") { pos = \"first\"; }\n                if(pos === \"after\") { pos = \"last\"; }\n            }\n            switch(pos) {\n                case \"before\":\n                    pos = $.inArray(par.id, new_par.children);\n                    break;\n                case \"after\" :\n                    pos = $.inArray(par.id, new_par.children) + 1;\n                    break;\n                case \"inside\":\n                case \"first\":\n                    pos = 0;\n                    break;\n                case \"last\":\n                    pos = new_par.children.length;\n                    break;\n                default:\n                    if(!pos) { pos = 0; }\n                    break;\n            }\n            if(pos > new_par.children.length) { pos = new_par.children.length; }\n            if(!this.check(\"move_node\", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) {\n                this.settings.core.error.call(this, this._data.core.last_error);\n                return false;\n            }\n            if(obj.parent === new_par.id) {\n                dpc = new_par.children.concat();\n                tmp = $.inArray(obj.id, dpc);\n                if(tmp !== -1) {\n                    dpc = $.vakata.array_remove(dpc, tmp);\n                    if(pos > tmp) { pos--; }\n                }\n                tmp = [];\n                for(i = 0, j = dpc.length; i < j; i++) {\n                    tmp[i >= pos ? i+1 : i] = dpc[i];\n                }\n                tmp[pos] = obj.id;\n                new_par.children = tmp;\n                this._node_changed(new_par.id);\n                this.redraw(new_par.id === $.jstree.root);\n            }\n            else {\n                // clean old parent and up\n                tmp = obj.children_d.concat();\n                tmp.push(obj.id);\n                for(i = 0, j = obj.parents.length; i < j; i++) {\n                    dpc = [];\n                    p = old_ins._model.data[obj.parents[i]].children_d;\n                    for(k = 0, l = p.length; k < l; k++) {\n                        if($.inArray(p[k], tmp) === -1) {\n                            dpc.push(p[k]);\n                        }\n                    }\n                    old_ins._model.data[obj.parents[i]].children_d = dpc;\n                }\n                old_ins._model.data[old_par].children = $.vakata.array_remove_item(old_ins._model.data[old_par].children, obj.id);\n\n                // insert into new parent and up\n                for(i = 0, j = new_par.parents.length; i < j; i++) {\n                    this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(tmp);\n                }\n                dpc = [];\n                for(i = 0, j = new_par.children.length; i < j; i++) {\n                    dpc[i >= pos ? i+1 : i] = new_par.children[i];\n                }\n                dpc[pos] = obj.id;\n                new_par.children = dpc;\n                new_par.children_d.push(obj.id);\n                new_par.children_d = new_par.children_d.concat(obj.children_d);\n\n                // update object\n                obj.parent = new_par.id;\n                tmp = new_par.parents.concat();\n                tmp.unshift(new_par.id);\n                p = obj.parents.length;\n                obj.parents = tmp;\n\n                // update object children\n                tmp = tmp.concat();\n                for(i = 0, j = obj.children_d.length; i < j; i++) {\n                    this._model.data[obj.children_d[i]].parents = this._model.data[obj.children_d[i]].parents.slice(0,p*-1);\n                    Array.prototype.push.apply(this._model.data[obj.children_d[i]].parents, tmp);\n                }\n\n                if(old_par === $.jstree.root || new_par.id === $.jstree.root) {\n                    this._model.force_full_redraw = true;\n                }\n                if(!this._model.force_full_redraw) {\n                    this._node_changed(old_par);\n                    this._node_changed(new_par.id);\n                }\n                if(!skip_redraw) {\n                    this.redraw();\n                }\n            }\n            if(callback) { callback.call(this, obj, new_par, pos); }\n            /**\n             * triggered when a node is moved\n             * @event\n             * @name move_node.jstree\n             * @param {Object} node\n             * @param {String} parent the parent's ID\n             * @param {Number} position the position of the node among the parent's children\n             * @param {String} old_parent the old parent of the node\n             * @param {Number} old_position the old position of the node\n             * @param {Boolean} is_multi do the node and new parent belong to different instances\n             * @param {jsTree} old_instance the instance the node came from\n             * @param {jsTree} new_instance the instance of the new parent\n             */\n            this.trigger('move_node', { \"node\" : obj, \"parent\" : new_par.id, \"position\" : pos, \"old_parent\" : old_par, \"old_position\" : old_pos, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this });\n            return obj.id;\n        },\n        /**\n         * copy a node to a new parent\n         * @name copy_node(obj, par [, pos, callback, is_loaded])\n         * @param  {mixed} obj the node to copy, pass an array to copy multiple nodes\n         * @param  {mixed} par the new parent\n         * @param  {mixed} pos the position to insert at (besides integer values, \"first\" and \"last\" are supported, as well as \"before\" and \"after\"), defaults to integer `0`\n         * @param  {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position\n         * @param  {Boolean} is_loaded internal parameter indicating if the parent node has been loaded\n         * @param  {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn\n         * @param  {Boolean} instance internal parameter indicating if the node comes from another instance\n         * @trigger model.jstree copy_node.jstree\n         */\n        copy_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) {\n            var t1, t2, dpc, tmp, i, j, node, old_par, new_par, old_ins, is_multi;\n\n            par = this.get_node(par);\n            pos = pos === undefined ? 0 : pos;\n            if(!par) { return false; }\n            if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {\n                return this.load_node(par, function () { this.copy_node(obj, par, pos, callback, true, false, origin); });\n            }\n\n            if($.vakata.is_array(obj)) {\n                if(obj.length === 1) {\n                    obj = obj[0];\n                }\n                else {\n                    //obj = obj.slice();\n                    for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                        if((tmp = this.copy_node(obj[t1], par, pos, callback, is_loaded, true, origin))) {\n                            par = tmp;\n                            pos = \"after\";\n                        }\n                    }\n                    this.redraw();\n                    return true;\n                }\n            }\n            obj = obj && obj.id ? obj : this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n\n            old_par = (obj.parent || $.jstree.root).toString();\n            new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent);\n            old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id));\n            is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id);\n\n            if(old_ins && old_ins._id) {\n                obj = old_ins._model.data[obj.id];\n            }\n\n            if(par.id === $.jstree.root) {\n                if(pos === \"before\") { pos = \"first\"; }\n                if(pos === \"after\") { pos = \"last\"; }\n            }\n            switch(pos) {\n                case \"before\":\n                    pos = $.inArray(par.id, new_par.children);\n                    break;\n                case \"after\" :\n                    pos = $.inArray(par.id, new_par.children) + 1;\n                    break;\n                case \"inside\":\n                case \"first\":\n                    pos = 0;\n                    break;\n                case \"last\":\n                    pos = new_par.children.length;\n                    break;\n                default:\n                    if(!pos) { pos = 0; }\n                    break;\n            }\n            if(pos > new_par.children.length) { pos = new_par.children.length; }\n            if(!this.check(\"copy_node\", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) {\n                this.settings.core.error.call(this, this._data.core.last_error);\n                return false;\n            }\n            node = old_ins ? old_ins.get_json(obj, { no_id : true, no_data : true, no_state : true }) : obj;\n            if(!node) { return false; }\n            if(node.id === true) { delete node.id; }\n            node = this._parse_model_from_json(node, new_par.id, new_par.parents.concat());\n            if(!node) { return false; }\n            tmp = this.get_node(node);\n            if(obj && obj.state && obj.state.loaded === false) { tmp.state.loaded = false; }\n            dpc = [];\n            dpc.push(node);\n            dpc = dpc.concat(tmp.children_d);\n            this.trigger('model', { \"nodes\" : dpc, \"parent\" : new_par.id });\n\n            // insert into new parent and up\n            for(i = 0, j = new_par.parents.length; i < j; i++) {\n                this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(dpc);\n            }\n            dpc = [];\n            for(i = 0, j = new_par.children.length; i < j; i++) {\n                dpc[i >= pos ? i+1 : i] = new_par.children[i];\n            }\n            dpc[pos] = tmp.id;\n            new_par.children = dpc;\n            new_par.children_d.push(tmp.id);\n            new_par.children_d = new_par.children_d.concat(tmp.children_d);\n\n            if(new_par.id === $.jstree.root) {\n                this._model.force_full_redraw = true;\n            }\n            if(!this._model.force_full_redraw) {\n                this._node_changed(new_par.id);\n            }\n            if(!skip_redraw) {\n                this.redraw(new_par.id === $.jstree.root);\n            }\n            if(callback) { callback.call(this, tmp, new_par, pos); }\n            /**\n             * triggered when a node is copied\n             * @event\n             * @name copy_node.jstree\n             * @param {Object} node the copied node\n             * @param {Object} original the original node\n             * @param {String} parent the parent's ID\n             * @param {Number} position the position of the node among the parent's children\n             * @param {String} old_parent the old parent of the node\n             * @param {Number} old_position the position of the original node\n             * @param {Boolean} is_multi do the node and new parent belong to different instances\n             * @param {jsTree} old_instance the instance the node came from\n             * @param {jsTree} new_instance the instance of the new parent\n             */\n            this.trigger('copy_node', { \"node\" : tmp, \"original\" : obj, \"parent\" : new_par.id, \"position\" : pos, \"old_parent\" : old_par, \"old_position\" : old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1,'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this });\n            return tmp.id;\n        },\n        /**\n         * cut a node (a later call to `paste(obj)` would move the node)\n         * @name cut(obj)\n         * @param  {mixed} obj multiple objects can be passed using an array\n         * @trigger cut.jstree\n         */\n        cut : function (obj) {\n            if(!obj) { obj = this._data.core.selected.concat(); }\n            if(!$.vakata.is_array(obj)) { obj = [obj]; }\n            if(!obj.length) { return false; }\n            var tmp = [], o, t1, t2;\n            for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                o = this.get_node(obj[t1]);\n                if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); }\n            }\n            if(!tmp.length) { return false; }\n            ccp_node = tmp;\n            ccp_inst = this;\n            ccp_mode = 'move_node';\n            /**\n             * triggered when nodes are added to the buffer for moving\n             * @event\n             * @name cut.jstree\n             * @param {Array} node\n             */\n            this.trigger('cut', { \"node\" : obj });\n        },\n        /**\n         * copy a node (a later call to `paste(obj)` would copy the node)\n         * @name copy(obj)\n         * @param  {mixed} obj multiple objects can be passed using an array\n         * @trigger copy.jstree\n         */\n        copy : function (obj) {\n            if(!obj) { obj = this._data.core.selected.concat(); }\n            if(!$.vakata.is_array(obj)) { obj = [obj]; }\n            if(!obj.length) { return false; }\n            var tmp = [], o, t1, t2;\n            for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                o = this.get_node(obj[t1]);\n                if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); }\n            }\n            if(!tmp.length) { return false; }\n            ccp_node = tmp;\n            ccp_inst = this;\n            ccp_mode = 'copy_node';\n            /**\n             * triggered when nodes are added to the buffer for copying\n             * @event\n             * @name copy.jstree\n             * @param {Array} node\n             */\n            this.trigger('copy', { \"node\" : obj });\n        },\n        /**\n         * get the current buffer (any nodes that are waiting for a paste operation)\n         * @name get_buffer()\n         * @return {Object} an object consisting of `mode` (\"copy_node\" or \"move_node\"), `node` (an array of objects) and `inst` (the instance)\n         */\n        get_buffer : function () {\n            return { 'mode' : ccp_mode, 'node' : ccp_node, 'inst' : ccp_inst };\n        },\n        /**\n         * check if there is something in the buffer to paste\n         * @name can_paste()\n         * @return {Boolean}\n         */\n        can_paste : function () {\n            return ccp_mode !== false && ccp_node !== false; // && ccp_inst._model.data[ccp_node];\n        },\n        /**\n         * copy or move the previously cut or copied nodes to a new parent\n         * @name paste(obj [, pos])\n         * @param  {mixed} obj the new parent\n         * @param  {mixed} pos the position to insert at (besides integer, \"first\" and \"last\" are supported), defaults to integer `0`\n         * @trigger paste.jstree\n         */\n        paste : function (obj, pos) {\n            obj = this.get_node(obj);\n            if(!obj || !ccp_mode || !ccp_mode.match(/^(copy_node|move_node)$/) || !ccp_node) { return false; }\n            if(this[ccp_mode](ccp_node, obj, pos, false, false, false, ccp_inst)) {\n                /**\n                 * triggered when paste is invoked\n                 * @event\n                 * @name paste.jstree\n                 * @param {String} parent the ID of the receiving node\n                 * @param {Array} node the nodes in the buffer\n                 * @param {String} mode the performed operation - \"copy_node\" or \"move_node\"\n                 */\n                this.trigger('paste', { \"parent\" : obj.id, \"node\" : ccp_node, \"mode\" : ccp_mode });\n            }\n            ccp_node = false;\n            ccp_mode = false;\n            ccp_inst = false;\n        },\n        /**\n         * clear the buffer of previously copied or cut nodes\n         * @name clear_buffer()\n         * @trigger clear_buffer.jstree\n         */\n        clear_buffer : function () {\n            ccp_node = false;\n            ccp_mode = false;\n            ccp_inst = false;\n            /**\n             * triggered when the copy / cut buffer is cleared\n             * @event\n             * @name clear_buffer.jstree\n             */\n            this.trigger('clear_buffer');\n        },\n        /**\n         * put a node in edit mode (input field to rename the node)\n         * @name edit(obj [, default_text, callback])\n         * @param  {mixed} obj\n         * @param  {String} default_text the text to populate the input with (if omitted or set to a non-string value the node's text value is used)\n         * @param  {Function} callback a function to be called once the text box is blurred, it is called in the instance's scope and receives the node, a status parameter (true if the rename is successful, false otherwise), a boolean indicating if the user cancelled the edit and the original unescaped value provided by the user. You can also access the node's title using .text\n         */\n        edit : function (obj, default_text, callback) {\n            var rtl, w, a, s, t, h1, h2, fn, tmp, cancel = false;\n            obj = this.get_node(obj);\n            if(!obj) { return false; }\n            if(!this.check(\"edit\", obj, this.get_parent(obj))) {\n                this.settings.core.error.call(this, this._data.core.last_error);\n                return false;\n            }\n            tmp = obj;\n            default_text = typeof default_text === 'string' ? default_text : obj.text;\n            this.set_text(obj, \"\");\n            obj = this._open_to(obj);\n            tmp.text = default_text;\n\n            rtl = this._data.core.rtl;\n            w  = this.element.width();\n            this._data.core.focused = tmp.id;\n            a  = obj.children('.jstree-anchor').trigger('focus');\n            s  = $('<span></span>');\n            /*!\n\t\t\toi = obj.children(\"i:visible\"),\n\t\t\tai = a.children(\"i:visible\"),\n\t\t\tw1 = oi.width() * oi.length,\n\t\t\tw2 = ai.width() * ai.length,\n\t\t\t*/\n            t  = default_text;\n            h1 = $(\"<\"+\"div></div>\", { css : { \"position\" : \"absolute\", \"top\" : \"-200px\", \"left\" : (rtl ? \"0px\" : \"-1000px\"), \"visibility\" : \"hidden\" } }).appendTo(document.body);\n            h2 = $(\"<\"+\"input />\", {\n                \"value\" : t,\n                \"class\" : \"jstree-rename-input\",\n                // \"size\" : t.length,\n                \"css\" : {\n                    \"padding\" : \"0\",\n                    \"border\" : \"1px solid silver\",\n                    \"box-sizing\" : \"border-box\",\n                    \"display\" : \"inline-block\",\n                    \"height\" : (this._data.core.li_height) + \"px\",\n                    \"lineHeight\" : (this._data.core.li_height) + \"px\",\n                    \"width\" : \"150px\" // will be set a bit further down\n                },\n                \"blur\" : function (e) {\n                    e.stopImmediatePropagation();\n                    e.preventDefault();\n                    var i = s.children(\".jstree-rename-input\"),\n                        v = i.val(),\n                        f = this.settings.core.force_text,\n                        nv;\n                    if(v === \"\") { v = t; }\n                    h1.remove();\n                    s.replaceWith(a);\n                    s.remove();\n                    t = f ? t : $('<div></div>').append($.parseHTML(t)).html();\n                    obj = this.get_node(obj);\n                    this.set_text(obj, t);\n                    nv = !!this.rename_node(obj, f ? $('<div></div>').text(v).text() : $('<div></div>').append($.parseHTML(v)).html());\n                    if(!nv) {\n                        this.set_text(obj, t); // move this up? and fix #483\n                    }\n                    this._data.core.focused = tmp.id;\n                    setTimeout(function () {\n                        var node = this.get_node(tmp.id, true);\n                        if(node.length) {\n                            this._data.core.focused = tmp.id;\n                            node.children('.jstree-anchor').trigger('focus');\n                        }\n                    }.bind(this), 0);\n                    if(callback) {\n                        callback.call(this, tmp, nv, cancel, v);\n                    }\n                    h2 = null;\n                }.bind(this),\n                \"keydown\" : function (e) {\n                    var key = e.which;\n                    if(key === 27) {\n                        cancel = true;\n                        this.value = t;\n                    }\n                    if(key === 27 || key === 13 || key === 37 || key === 38 || key === 39 || key === 40 || key === 32) {\n                        e.stopImmediatePropagation();\n                    }\n                    if(key === 27 || key === 13) {\n                        e.preventDefault();\n                        this.blur();\n                    }\n                },\n                \"click\" : function (e) { e.stopImmediatePropagation(); },\n                \"mousedown\" : function (e) { e.stopImmediatePropagation(); },\n                \"keyup\" : function (e) {\n                    h2.width(Math.min(h1.text(\"pW\" + this.value).width(),w));\n                },\n                \"keypress\" : function(e) {\n                    if(e.which === 13) { return false; }\n                }\n            });\n            fn = {\n                fontFamily\t\t: a.css('fontFamily')\t\t|| '',\n                fontSize\t\t: a.css('fontSize')\t\t\t|| '',\n                fontWeight\t\t: a.css('fontWeight')\t\t|| '',\n                fontStyle\t\t: a.css('fontStyle')\t\t|| '',\n                fontStretch\t\t: a.css('fontStretch')\t\t|| '',\n                fontVariant\t\t: a.css('fontVariant')\t\t|| '',\n                letterSpacing\t: a.css('letterSpacing')\t|| '',\n                wordSpacing\t\t: a.css('wordSpacing')\t\t|| ''\n            };\n            s.attr('class', a.attr('class')).append(a.contents().clone()).append(h2);\n            a.replaceWith(s);\n            h1.css(fn);\n            h2.css(fn).width(Math.min(h1.text(\"pW\" + h2[0].value).width(),w))[0].select();\n            $(document).one('mousedown.jstree touchstart.jstree dnd_start.vakata', function (e) {\n                if (h2 && e.target !== h2) {\n                    $(h2).trigger('blur');\n                }\n            });\n        },\n\n\n        /**\n         * changes the theme\n         * @name set_theme(theme_name [, theme_url])\n         * @param {String} theme_name the name of the new theme to apply\n         * @param {mixed} theme_url  the location of the CSS file for this theme. Omit or set to `false` if you manually included the file. Set to `true` to autoload from the `core.themes.dir` directory.\n         * @trigger set_theme.jstree\n         */\n        set_theme : function (theme_name, theme_url) {\n            if(!theme_name) { return false; }\n            if(theme_url === true) {\n                var dir = this.settings.core.themes.dir;\n                if(!dir) { dir = $.jstree.path + '/themes'; }\n                theme_url = dir + '/' + theme_name + '/style.css';\n            }\n            if(theme_url && $.inArray(theme_url, themes_loaded) === -1) {\n                $('head').append('<'+'link rel=\"stylesheet\" href=\"' + theme_url + '\" type=\"text/css\" />');\n                themes_loaded.push(theme_url);\n            }\n            if(this._data.core.themes.name) {\n                this.element.removeClass('jstree-' + this._data.core.themes.name);\n            }\n            this._data.core.themes.name = theme_name;\n            this.element.addClass('jstree-' + theme_name);\n            this.element[this.settings.core.themes.responsive ? 'addClass' : 'removeClass' ]('jstree-' + theme_name + '-responsive');\n            /**\n             * triggered when a theme is set\n             * @event\n             * @name set_theme.jstree\n             * @param {String} theme the new theme\n             */\n            this.trigger('set_theme', { 'theme' : theme_name });\n        },\n        /**\n         * gets the name of the currently applied theme name\n         * @name get_theme()\n         * @return {String}\n         */\n        get_theme : function () { return this._data.core.themes.name; },\n        /**\n         * changes the theme variant (if the theme has variants)\n         * @name set_theme_variant(variant_name)\n         * @param {String|Boolean} variant_name the variant to apply (if `false` is used the current variant is removed)\n         */\n        set_theme_variant : function (variant_name) {\n            if(this._data.core.themes.variant) {\n                this.element.removeClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant);\n            }\n            this._data.core.themes.variant = variant_name;\n            if(variant_name) {\n                this.element.addClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant);\n            }\n        },\n        /**\n         * gets the name of the currently applied theme variant\n         * @name get_theme()\n         * @return {String}\n         */\n        get_theme_variant : function () { return this._data.core.themes.variant; },\n        /**\n         * shows a striped background on the container (if the theme supports it)\n         * @name show_stripes()\n         */\n        show_stripes : function () {\n            this._data.core.themes.stripes = true;\n            this.get_container_ul().addClass(\"jstree-striped\");\n            /**\n             * triggered when stripes are shown\n             * @event\n             * @name show_stripes.jstree\n             */\n            this.trigger('show_stripes');\n        },\n        /**\n         * hides the striped background on the container\n         * @name hide_stripes()\n         */\n        hide_stripes : function () {\n            this._data.core.themes.stripes = false;\n            this.get_container_ul().removeClass(\"jstree-striped\");\n            /**\n             * triggered when stripes are hidden\n             * @event\n             * @name hide_stripes.jstree\n             */\n            this.trigger('hide_stripes');\n        },\n        /**\n         * toggles the striped background on the container\n         * @name toggle_stripes()\n         */\n        toggle_stripes : function () { if(this._data.core.themes.stripes) { this.hide_stripes(); } else { this.show_stripes(); } },\n        /**\n         * shows the connecting dots (if the theme supports it)\n         * @name show_dots()\n         */\n        show_dots : function () {\n            this._data.core.themes.dots = true;\n            this.get_container_ul().removeClass(\"jstree-no-dots\");\n            /**\n             * triggered when dots are shown\n             * @event\n             * @name show_dots.jstree\n             */\n            this.trigger('show_dots');\n        },\n        /**\n         * hides the connecting dots\n         * @name hide_dots()\n         */\n        hide_dots : function () {\n            this._data.core.themes.dots = false;\n            this.get_container_ul().addClass(\"jstree-no-dots\");\n            /**\n             * triggered when dots are hidden\n             * @event\n             * @name hide_dots.jstree\n             */\n            this.trigger('hide_dots');\n        },\n        /**\n         * toggles the connecting dots\n         * @name toggle_dots()\n         */\n        toggle_dots : function () { if(this._data.core.themes.dots) { this.hide_dots(); } else { this.show_dots(); } },\n        /**\n         * show the node icons\n         * @name show_icons()\n         */\n        show_icons : function () {\n            this._data.core.themes.icons = true;\n            this.get_container_ul().removeClass(\"jstree-no-icons\");\n            /**\n             * triggered when icons are shown\n             * @event\n             * @name show_icons.jstree\n             */\n            this.trigger('show_icons');\n        },\n        /**\n         * hide the node icons\n         * @name hide_icons()\n         */\n        hide_icons : function () {\n            this._data.core.themes.icons = false;\n            this.get_container_ul().addClass(\"jstree-no-icons\");\n            /**\n             * triggered when icons are hidden\n             * @event\n             * @name hide_icons.jstree\n             */\n            this.trigger('hide_icons');\n        },\n        /**\n         * toggle the node icons\n         * @name toggle_icons()\n         */\n        toggle_icons : function () { if(this._data.core.themes.icons) { this.hide_icons(); } else { this.show_icons(); } },\n        /**\n         * show the node ellipsis\n         * @name show_icons()\n         */\n        show_ellipsis : function () {\n            this._data.core.themes.ellipsis = true;\n            this.get_container_ul().addClass(\"jstree-ellipsis\");\n            /**\n             * triggered when ellisis is shown\n             * @event\n             * @name show_ellipsis.jstree\n             */\n            this.trigger('show_ellipsis');\n        },\n        /**\n         * hide the node ellipsis\n         * @name hide_ellipsis()\n         */\n        hide_ellipsis : function () {\n            this._data.core.themes.ellipsis = false;\n            this.get_container_ul().removeClass(\"jstree-ellipsis\");\n            /**\n             * triggered when ellisis is hidden\n             * @event\n             * @name hide_ellipsis.jstree\n             */\n            this.trigger('hide_ellipsis');\n        },\n        /**\n         * toggle the node ellipsis\n         * @name toggle_icons()\n         */\n        toggle_ellipsis : function () { if(this._data.core.themes.ellipsis) { this.hide_ellipsis(); } else { this.show_ellipsis(); } },\n        /**\n         * set the node icon for a node\n         * @name set_icon(obj, icon)\n         * @param {mixed} obj\n         * @param {String} icon the new icon - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class\n         */\n        set_icon : function (obj, icon) {\n            var t1, t2, dom, old;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.set_icon(obj[t1], icon);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n            old = obj.icon;\n            obj.icon = icon === true || icon === null || icon === undefined || icon === '' ? true : icon;\n            dom = this.get_node(obj, true).children(\".jstree-anchor\").children(\".jstree-themeicon\");\n            if(icon === false) {\n                dom.removeClass('jstree-themeicon-custom ' + old).css(\"background\",\"\").removeAttr(\"rel\");\n                this.hide_icon(obj);\n            }\n            else if(icon === true || icon === null || icon === undefined || icon === '') {\n                dom.removeClass('jstree-themeicon-custom ' + old).css(\"background\",\"\").removeAttr(\"rel\");\n                if(old === false) { this.show_icon(obj); }\n            }\n            else if(icon.indexOf(\"/\") === -1 && icon.indexOf(\".\") === -1) {\n                dom.removeClass(old).css(\"background\",\"\");\n                dom.addClass(icon + ' jstree-themeicon-custom').attr(\"rel\",icon);\n                if(old === false) { this.show_icon(obj); }\n            }\n            else {\n                dom.removeClass(old).css(\"background\",\"\");\n                dom.addClass('jstree-themeicon-custom').css(\"background\", \"url('\" + icon + \"') center center no-repeat\").attr(\"rel\",icon);\n                if(old === false) { this.show_icon(obj); }\n            }\n            return true;\n        },\n        /**\n         * get the node icon for a node\n         * @name get_icon(obj)\n         * @param {mixed} obj\n         * @return {String}\n         */\n        get_icon : function (obj) {\n            obj = this.get_node(obj);\n            return (!obj || obj.id === $.jstree.root) ? false : obj.icon;\n        },\n        /**\n         * hide the icon on an individual node\n         * @name hide_icon(obj)\n         * @param {mixed} obj\n         */\n        hide_icon : function (obj) {\n            var t1, t2;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.hide_icon(obj[t1]);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj === $.jstree.root) { return false; }\n            obj.icon = false;\n            this.get_node(obj, true).children(\".jstree-anchor\").children(\".jstree-themeicon\").addClass('jstree-themeicon-hidden');\n            return true;\n        },\n        /**\n         * show the icon on an individual node\n         * @name show_icon(obj)\n         * @param {mixed} obj\n         */\n        show_icon : function (obj) {\n            var t1, t2, dom;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.show_icon(obj[t1]);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj === $.jstree.root) { return false; }\n            dom = this.get_node(obj, true);\n            obj.icon = dom.length ? dom.children(\".jstree-anchor\").children(\".jstree-themeicon\").attr('rel') : true;\n            if(!obj.icon) { obj.icon = true; }\n            dom.children(\".jstree-anchor\").children(\".jstree-themeicon\").removeClass('jstree-themeicon-hidden');\n            return true;\n        }\n    };\n\n    // helpers\n    $.vakata = {};\n    // collect attributes\n    $.vakata.attributes = function(node, with_values) {\n        node = $(node)[0];\n        var attr = with_values ? {} : [];\n        if(node && node.attributes) {\n            $.each(node.attributes, function (i, v) {\n                if($.inArray(v.name.toLowerCase(),['style','contenteditable','hasfocus','tabindex']) !== -1) { return; }\n                if(v.value !== null && $.vakata.trim(v.value) !== '') {\n                    if(with_values) { attr[v.name] = v.value; }\n                    else { attr.push(v.name); }\n                }\n            });\n        }\n        return attr;\n    };\n    $.vakata.array_unique = function(array) {\n        var a = [], i, j, l, o = {};\n        for(i = 0, l = array.length; i < l; i++) {\n            if(o[array[i]] === undefined) {\n                a.push(array[i]);\n                o[array[i]] = true;\n            }\n        }\n        return a;\n    };\n    // remove item from array\n    $.vakata.array_remove = function(array, from) {\n        array.splice(from, 1);\n        return array;\n        //var rest = array.slice((to || from) + 1 || array.length);\n        //array.length = from < 0 ? array.length + from : from;\n        //array.push.apply(array, rest);\n        //return array;\n    };\n    // remove item from array\n    $.vakata.array_remove_item = function(array, item) {\n        var tmp = $.inArray(item, array);\n        return tmp !== -1 ? $.vakata.array_remove(array, tmp) : array;\n    };\n    $.vakata.array_filter = function(c,a,b,d,e) {\n        if (c.filter) {\n            return c.filter(a, b);\n        }\n        d=[];\n        for (e in c) {\n            if (~~e+''===e+'' && e>=0 && a.call(b,c[e],+e,c)) {\n                d.push(c[e]);\n            }\n        }\n        return d;\n    };\n    $.vakata.trim = function (text) {\n        return String.prototype.trim ?\n            String.prototype.trim.call(text.toString()) :\n            text.toString().replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n    };\n    $.vakata.is_function = function(obj) {\n        return typeof obj === \"function\" && typeof obj.nodeType !== \"number\";\n    };\n    $.vakata.is_array = Array.isArray || function (obj) {\n        return Object.prototype.toString.call(obj) === \"[object Array]\";\n    };\n\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind#polyfill\n    if (!Function.prototype.bind) {\n        Function.prototype.bind = function () {\n            var thatFunc = this, thatArg = arguments[0];\n            var args = Array.prototype.slice.call(arguments, 1);\n            if (typeof thatFunc !== 'function') {\n                // closest thing possible to the ECMAScript 5\n                // internal IsCallable function\n                throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');\n            }\n            return function(){\n                var funcArgs = args.concat(Array.prototype.slice.call(arguments));\n                return thatFunc.apply(thatArg, funcArgs);\n            };\n        };\n    }\n\n\n    /**\n     * ### Changed plugin\n     *\n     * This plugin adds more information to the `changed.jstree` event. The new data is contained in the `changed` event data property, and contains a lists of `selected` and `deselected` nodes.\n     */\n\n    $.jstree.plugins.changed = function (options, parent) {\n        var last = [];\n        this.trigger = function (ev, data) {\n            var i, j;\n            if(!data) {\n                data = {};\n            }\n            if(ev.replace('.jstree','') === 'changed') {\n                data.changed = { selected : [], deselected : [] };\n                var tmp = {};\n                for(i = 0, j = last.length; i < j; i++) {\n                    tmp[last[i]] = 1;\n                }\n                for(i = 0, j = data.selected.length; i < j; i++) {\n                    if(!tmp[data.selected[i]]) {\n                        data.changed.selected.push(data.selected[i]);\n                    }\n                    else {\n                        tmp[data.selected[i]] = 2;\n                    }\n                }\n                for(i = 0, j = last.length; i < j; i++) {\n                    if(tmp[last[i]] === 1) {\n                        data.changed.deselected.push(last[i]);\n                    }\n                }\n                last = data.selected.slice();\n            }\n            /**\n             * triggered when selection changes (the \"changed\" plugin enhances the original event with more data)\n             * @event\n             * @name changed.jstree\n             * @param {Object} node\n             * @param {Object} action the action that caused the selection to change\n             * @param {Array} selected the current selection\n             * @param {Object} changed an object containing two properties `selected` and `deselected` - both arrays of node IDs, which were selected or deselected since the last changed event\n             * @param {Object} event the event (if any) that triggered this changed event\n             * @plugin changed\n             */\n            parent.trigger.call(this, ev, data);\n        };\n        this.refresh = function (skip_loading, forget_state) {\n            last = [];\n            return parent.refresh.apply(this, arguments);\n        };\n    };\n\n    /**\n     * ### Checkbox plugin\n     *\n     * This plugin renders checkbox icons in front of each node, making multiple selection much easier.\n     * It also supports tri-state behavior, meaning that if a node has a few of its children checked it will be rendered as undetermined, and state will be propagated up.\n     */\n\n    var _i = document.createElement('I');\n    _i.className = 'jstree-icon jstree-checkbox';\n    _i.setAttribute('role', 'presentation');\n    /**\n     * stores all defaults for the checkbox plugin\n     * @name $.jstree.defaults.checkbox\n     * @plugin checkbox\n     */\n    $.jstree.defaults.checkbox = {\n        /**\n         * a boolean indicating if checkboxes should be visible (can be changed at a later time using `show_checkboxes()` and `hide_checkboxes`). Defaults to `true`.\n         * @name $.jstree.defaults.checkbox.visible\n         * @plugin checkbox\n         */\n        visible\t\t\t\t: true,\n        /**\n         * a boolean indicating if checkboxes should cascade down and have an undetermined state. Defaults to `true`.\n         * @name $.jstree.defaults.checkbox.three_state\n         * @plugin checkbox\n         */\n        three_state\t\t\t: true,\n        /**\n         * a boolean indicating if clicking anywhere on the node should act as clicking on the checkbox. Defaults to `true`.\n         * @name $.jstree.defaults.checkbox.whole_node\n         * @plugin checkbox\n         */\n        whole_node\t\t\t: true,\n        /**\n         * a boolean indicating if the selected style of a node should be kept, or removed. Defaults to `true`.\n         * @name $.jstree.defaults.checkbox.keep_selected_style\n         * @plugin checkbox\n         */\n        keep_selected_style\t: true,\n        /**\n         * This setting controls how cascading and undetermined nodes are applied.\n         * If 'up' is in the string - cascading up is enabled, if 'down' is in the string - cascading down is enabled, if 'undetermined' is in the string - undetermined nodes will be used.\n         * If `three_state` is set to `true` this setting is automatically set to 'up+down+undetermined'. Defaults to ''.\n         * @name $.jstree.defaults.checkbox.cascade\n         * @plugin checkbox\n         */\n        cascade\t\t\t\t: '',\n        /**\n         * This setting controls if checkbox are bound to the general tree selection or to an internal array maintained by the checkbox plugin. Defaults to `true`, only set to `false` if you know exactly what you are doing.\n         * @name $.jstree.defaults.checkbox.tie_selection\n         * @plugin checkbox\n         */\n        tie_selection\t\t: true,\n\n        /**\n         * This setting controls if cascading down affects disabled checkboxes\n         * @name $.jstree.defaults.checkbox.cascade_to_disabled\n         * @plugin checkbox\n         */\n        cascade_to_disabled : true,\n\n        /**\n         * This setting controls if cascading down affects hidden checkboxes\n         * @name $.jstree.defaults.checkbox.cascade_to_hidden\n         * @plugin checkbox\n         */\n        cascade_to_hidden : true\n    };\n    $.jstree.plugins.checkbox = function (options, parent) {\n        this.bind = function () {\n            parent.bind.call(this);\n            this._data.checkbox.uto = false;\n            this._data.checkbox.selected = [];\n            if(this.settings.checkbox.three_state) {\n                this.settings.checkbox.cascade = 'up+down+undetermined';\n            }\n            this.element\n                .on(\"init.jstree\", function () {\n                    this._data.checkbox.visible = this.settings.checkbox.visible;\n                    if(!this.settings.checkbox.keep_selected_style) {\n                        this.element.addClass('jstree-checkbox-no-clicked');\n                    }\n                    if(this.settings.checkbox.tie_selection) {\n                        this.element.addClass('jstree-checkbox-selection');\n                    }\n                }.bind(this))\n                .on(\"loading.jstree\", function () {\n                    this[ this._data.checkbox.visible ? 'show_checkboxes' : 'hide_checkboxes' ]();\n                }.bind(this));\n            if(this.settings.checkbox.cascade.indexOf('undetermined') !== -1) {\n                this.element\n                    .on('changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree', function () {\n                        // only if undetermined is in setting\n                        if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); }\n                        this._data.checkbox.uto = setTimeout(this._undetermined.bind(this), 50);\n                    }.bind(this));\n            }\n            if(!this.settings.checkbox.tie_selection) {\n                this.element\n                    .on('model.jstree', function (e, data) {\n                        var m = this._model.data,\n                            p = m[data.parent],\n                            dpc = data.nodes,\n                            i, j;\n                        for(i = 0, j = dpc.length; i < j; i++) {\n                            m[dpc[i]].state.checked = m[dpc[i]].state.checked || (m[dpc[i]].original && m[dpc[i]].original.state && m[dpc[i]].original.state.checked);\n                            if(m[dpc[i]].state.checked) {\n                                this._data.checkbox.selected.push(dpc[i]);\n                            }\n                        }\n                    }.bind(this));\n            }\n            if(this.settings.checkbox.cascade.indexOf('up') !== -1 || this.settings.checkbox.cascade.indexOf('down') !== -1) {\n                this.element\n                    .on('model.jstree', function (e, data) {\n                        var m = this._model.data,\n                            p = m[data.parent],\n                            dpc = data.nodes,\n                            chd = [],\n                            c, i, j, k, l, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection;\n\n                        if(s.indexOf('down') !== -1) {\n                            // apply down\n                            if(p.state[ t ? 'selected' : 'checked' ]) {\n                                for(i = 0, j = dpc.length; i < j; i++) {\n                                    m[dpc[i]].state[ t ? 'selected' : 'checked' ] = true;\n                                }\n\n                                this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(dpc);\n                            }\n                            else {\n                                for(i = 0, j = dpc.length; i < j; i++) {\n                                    if(m[dpc[i]].state[ t ? 'selected' : 'checked' ]) {\n                                        for(k = 0, l = m[dpc[i]].children_d.length; k < l; k++) {\n                                            m[m[dpc[i]].children_d[k]].state[ t ? 'selected' : 'checked' ] = true;\n                                        }\n                                        this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(m[dpc[i]].children_d);\n                                    }\n                                }\n                            }\n                        }\n\n                        if(s.indexOf('up') !== -1) {\n                            // apply up\n                            for(i = 0, j = p.children_d.length; i < j; i++) {\n                                if(!m[p.children_d[i]].children.length) {\n                                    chd.push(m[p.children_d[i]].parent);\n                                }\n                            }\n                            chd = $.vakata.array_unique(chd);\n                            for(k = 0, l = chd.length; k < l; k++) {\n                                p = m[chd[k]];\n                                while(p && p.id !== $.jstree.root) {\n                                    c = 0;\n                                    for(i = 0, j = p.children.length; i < j; i++) {\n                                        c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];\n                                    }\n                                    if(c === j) {\n                                        p.state[ t ? 'selected' : 'checked' ] = true;\n                                        this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);\n                                        tmp = this.get_node(p, true);\n                                        if(tmp && tmp.length) {\n                                            tmp.attr('aria-selected', true).children('.jstree-anchor').addClass( t ? 'jstree-clicked' : 'jstree-checked');\n                                        }\n                                    }\n                                    else {\n                                        break;\n                                    }\n                                    p = this.get_node(p.parent);\n                                }\n                            }\n                        }\n\n                        this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected);\n                    }.bind(this))\n                    .on(this.settings.checkbox.tie_selection ? 'select_node.jstree' : 'check_node.jstree', function (e, data) {\n                        var self = this,\n                            obj = data.node,\n                            m = this._model.data,\n                            par = this.get_node(obj.parent),\n                            i, j, c, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection,\n                            sel = {}, cur = this._data[ t ? 'core' : 'checkbox' ].selected;\n\n                        for (i = 0, j = cur.length; i < j; i++) {\n                            sel[cur[i]] = true;\n                        }\n\n                        // apply down\n                        if(s.indexOf('down') !== -1) {\n                            //this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected.concat(obj.children_d));\n                            var selectedIds = this._cascade_new_checked_state(obj.id, true);\n                            var temp = obj.children_d.concat(obj.id);\n                            for (i = 0, j = temp.length; i < j; i++) {\n                                if (selectedIds.indexOf(temp[i]) > -1) {\n                                    sel[temp[i]] = true;\n                                }\n                                else {\n                                    delete sel[temp[i]];\n                                }\n                            }\n                        }\n\n                        // apply up\n                        if(s.indexOf('up') !== -1) {\n                            while(par && par.id !== $.jstree.root) {\n                                c = 0;\n                                for(i = 0, j = par.children.length; i < j; i++) {\n                                    c += m[par.children[i]].state[ t ? 'selected' : 'checked' ];\n                                }\n                                if(c === j) {\n                                    par.state[ t ? 'selected' : 'checked' ] = true;\n                                    sel[par.id] = true;\n                                    //this._data[ t ? 'core' : 'checkbox' ].selected.push(par.id);\n                                    tmp = this.get_node(par, true);\n                                    if(tmp && tmp.length) {\n                                        tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');\n                                    }\n                                }\n                                else {\n                                    break;\n                                }\n                                par = this.get_node(par.parent);\n                            }\n                        }\n\n                        cur = [];\n                        for (i in sel) {\n                            if (sel.hasOwnProperty(i)) {\n                                cur.push(i);\n                            }\n                        }\n                        this._data[ t ? 'core' : 'checkbox' ].selected = cur;\n                    }.bind(this))\n                    .on(this.settings.checkbox.tie_selection ? 'deselect_all.jstree' : 'uncheck_all.jstree', function (e, data) {\n                        var obj = this.get_node($.jstree.root),\n                            m = this._model.data,\n                            i, j, tmp;\n                        for(i = 0, j = obj.children_d.length; i < j; i++) {\n                            tmp = m[obj.children_d[i]];\n                            if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {\n                                tmp.original.state.undetermined = false;\n                            }\n                        }\n                    }.bind(this))\n                    .on(this.settings.checkbox.tie_selection ? 'deselect_node.jstree' : 'uncheck_node.jstree', function (e, data) {\n                        var self = this,\n                            obj = data.node,\n                            dom = this.get_node(obj, true),\n                            i, j, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection,\n                            cur = this._data[ t ? 'core' : 'checkbox' ].selected, sel = {},\n                            stillSelectedIds = [],\n                            allIds = obj.children_d.concat(obj.id);\n\n                        // apply down\n                        if(s.indexOf('down') !== -1) {\n                            var selectedIds = this._cascade_new_checked_state(obj.id, false);\n\n                            cur = $.vakata.array_filter(cur, function(id) {\n                                return allIds.indexOf(id) === -1 || selectedIds.indexOf(id) > -1;\n                            });\n                        }\n\n                        // only apply up if cascade up is enabled and if this node is not selected\n                        // (if all child nodes are disabled and cascade_to_disabled === false then this node will till be selected).\n                        if(s.indexOf('up') !== -1 && cur.indexOf(obj.id) === -1) {\n                            for(i = 0, j = obj.parents.length; i < j; i++) {\n                                tmp = this._model.data[obj.parents[i]];\n                                tmp.state[ t ? 'selected' : 'checked' ] = false;\n                                if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {\n                                    tmp.original.state.undetermined = false;\n                                }\n                                tmp = this.get_node(obj.parents[i], true);\n                                if(tmp && tmp.length) {\n                                    tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');\n                                }\n                            }\n\n                            cur = $.vakata.array_filter(cur, function(id) {\n                                return obj.parents.indexOf(id) === -1;\n                            });\n                        }\n\n                        this._data[ t ? 'core' : 'checkbox' ].selected = cur;\n                    }.bind(this));\n            }\n            if(this.settings.checkbox.cascade.indexOf('up') !== -1) {\n                this.element\n                    .on('delete_node.jstree', function (e, data) {\n                        // apply up (whole handler)\n                        var p = this.get_node(data.parent),\n                            m = this._model.data,\n                            i, j, c, tmp, t = this.settings.checkbox.tie_selection;\n                        while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) {\n                            c = 0;\n                            for(i = 0, j = p.children.length; i < j; i++) {\n                                c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];\n                            }\n                            if(j > 0 && c === j) {\n                                p.state[ t ? 'selected' : 'checked' ] = true;\n                                this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);\n                                tmp = this.get_node(p, true);\n                                if(tmp && tmp.length) {\n                                    tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');\n                                }\n                            }\n                            else {\n                                break;\n                            }\n                            p = this.get_node(p.parent);\n                        }\n                    }.bind(this))\n                    .on('move_node.jstree', function (e, data) {\n                        // apply up (whole handler)\n                        var is_multi = data.is_multi,\n                            old_par = data.old_parent,\n                            new_par = this.get_node(data.parent),\n                            m = this._model.data,\n                            p, c, i, j, tmp, t = this.settings.checkbox.tie_selection;\n                        if(!is_multi) {\n                            p = this.get_node(old_par);\n                            while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) {\n                                c = 0;\n                                for(i = 0, j = p.children.length; i < j; i++) {\n                                    c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];\n                                }\n                                if(j > 0 && c === j) {\n                                    p.state[ t ? 'selected' : 'checked' ] = true;\n                                    this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);\n                                    tmp = this.get_node(p, true);\n                                    if(tmp && tmp.length) {\n                                        tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');\n                                    }\n                                }\n                                else {\n                                    break;\n                                }\n                                p = this.get_node(p.parent);\n                            }\n                        }\n                        p = new_par;\n                        while(p && p.id !== $.jstree.root) {\n                            c = 0;\n                            for(i = 0, j = p.children.length; i < j; i++) {\n                                c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];\n                            }\n                            if(c === j) {\n                                if(!p.state[ t ? 'selected' : 'checked' ]) {\n                                    p.state[ t ? 'selected' : 'checked' ] = true;\n                                    this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);\n                                    tmp = this.get_node(p, true);\n                                    if(tmp && tmp.length) {\n                                        tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');\n                                    }\n                                }\n                            }\n                            else {\n                                if(p.state[ t ? 'selected' : 'checked' ]) {\n                                    p.state[ t ? 'selected' : 'checked' ] = false;\n                                    this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_remove_item(this._data[ t ? 'core' : 'checkbox' ].selected, p.id);\n                                    tmp = this.get_node(p, true);\n                                    if(tmp && tmp.length) {\n                                        tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');\n                                    }\n                                }\n                                else {\n                                    break;\n                                }\n                            }\n                            p = this.get_node(p.parent);\n                        }\n                    }.bind(this));\n            }\n        };\n        /**\n         * get an array of all nodes whose state is \"undetermined\"\n         * @name get_undetermined([full])\n         * @param  {boolean} full: if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned\n         * @return {Array}\n         * @plugin checkbox\n         */\n        this.get_undetermined = function (full) {\n            if (this.settings.checkbox.cascade.indexOf('undetermined') === -1) {\n                return [];\n            }\n            var i, j, k, l, o = {}, m = this._model.data, t = this.settings.checkbox.tie_selection, s = this._data[ t ? 'core' : 'checkbox' ].selected, p = [], tt = this, r = [];\n            for(i = 0, j = s.length; i < j; i++) {\n                if(m[s[i]] && m[s[i]].parents) {\n                    for(k = 0, l = m[s[i]].parents.length; k < l; k++) {\n                        if(o[m[s[i]].parents[k]] !== undefined) {\n                            break;\n                        }\n                        if(m[s[i]].parents[k] !== $.jstree.root) {\n                            o[m[s[i]].parents[k]] = true;\n                            p.push(m[s[i]].parents[k]);\n                        }\n                    }\n                }\n            }\n            // attempt for server side undetermined state\n            this.element.find('.jstree-closed').not(':has(.jstree-children)')\n                .each(function () {\n                    var tmp = tt.get_node(this), tmp2;\n\n                    if(!tmp) { return; }\n\n                    if(!tmp.state.loaded) {\n                        if(tmp.original && tmp.original.state && tmp.original.state.undetermined && tmp.original.state.undetermined === true) {\n                            if(o[tmp.id] === undefined && tmp.id !== $.jstree.root) {\n                                o[tmp.id] = true;\n                                p.push(tmp.id);\n                            }\n                            for(k = 0, l = tmp.parents.length; k < l; k++) {\n                                if(o[tmp.parents[k]] === undefined && tmp.parents[k] !== $.jstree.root) {\n                                    o[tmp.parents[k]] = true;\n                                    p.push(tmp.parents[k]);\n                                }\n                            }\n                        }\n                    }\n                    else {\n                        for(i = 0, j = tmp.children_d.length; i < j; i++) {\n                            tmp2 = m[tmp.children_d[i]];\n                            if(!tmp2.state.loaded && tmp2.original && tmp2.original.state && tmp2.original.state.undetermined && tmp2.original.state.undetermined === true) {\n                                if(o[tmp2.id] === undefined && tmp2.id !== $.jstree.root) {\n                                    o[tmp2.id] = true;\n                                    p.push(tmp2.id);\n                                }\n                                for(k = 0, l = tmp2.parents.length; k < l; k++) {\n                                    if(o[tmp2.parents[k]] === undefined && tmp2.parents[k] !== $.jstree.root) {\n                                        o[tmp2.parents[k]] = true;\n                                        p.push(tmp2.parents[k]);\n                                    }\n                                }\n                            }\n                        }\n                    }\n                });\n            for (i = 0, j = p.length; i < j; i++) {\n                if(!m[p[i]].state[ t ? 'selected' : 'checked' ]) {\n                    r.push(full ? m[p[i]] : p[i]);\n                }\n            }\n            return r;\n        };\n        /**\n         * set the undetermined state where and if necessary. Used internally.\n         * @private\n         * @name _undetermined()\n         * @plugin checkbox\n         */\n        this._undetermined = function () {\n            if(this.element === null) { return; }\n            var p = this.get_undetermined(false), i, j, s;\n\n            this.element.find('.jstree-undetermined').removeClass('jstree-undetermined');\n            for (i = 0, j = p.length; i < j; i++) {\n                s = this.get_node(p[i], true);\n                if(s && s.length) {\n                    s.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-undetermined');\n                }\n            }\n        };\n        this.redraw_node = function(obj, deep, is_callback, force_render) {\n            obj = parent.redraw_node.apply(this, arguments);\n            if(obj) {\n                var i, j, tmp = null, icon = null;\n                for(i = 0, j = obj.childNodes.length; i < j; i++) {\n                    if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf(\"jstree-anchor\") !== -1) {\n                        tmp = obj.childNodes[i];\n                        break;\n                    }\n                }\n                if(tmp) {\n                    if(!this.settings.checkbox.tie_selection && this._model.data[obj.id].state.checked) { tmp.className += ' jstree-checked'; }\n                    icon = _i.cloneNode(false);\n                    if(this._model.data[obj.id].state.checkbox_disabled) { icon.className += ' jstree-checkbox-disabled'; }\n                    tmp.insertBefore(icon, tmp.childNodes[0]);\n                }\n            }\n            if(!is_callback && this.settings.checkbox.cascade.indexOf('undetermined') !== -1) {\n                if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); }\n                this._data.checkbox.uto = setTimeout(this._undetermined.bind(this), 50);\n            }\n            return obj;\n        };\n        /**\n         * show the node checkbox icons\n         * @name show_checkboxes()\n         * @plugin checkbox\n         */\n        this.show_checkboxes = function () { this._data.core.themes.checkboxes = true; this.get_container_ul().removeClass(\"jstree-no-checkboxes\"); };\n        /**\n         * hide the node checkbox icons\n         * @name hide_checkboxes()\n         * @plugin checkbox\n         */\n        this.hide_checkboxes = function () { this._data.core.themes.checkboxes = false; this.get_container_ul().addClass(\"jstree-no-checkboxes\"); };\n        /**\n         * toggle the node icons\n         * @name toggle_checkboxes()\n         * @plugin checkbox\n         */\n        this.toggle_checkboxes = function () { if(this._data.core.themes.checkboxes) { this.hide_checkboxes(); } else { this.show_checkboxes(); } };\n        /**\n         * checks if a node is in an undetermined state\n         * @name is_undetermined(obj)\n         * @param  {mixed} obj\n         * @return {Boolean}\n         */\n        this.is_undetermined = function (obj) {\n            obj = this.get_node(obj);\n            var s = this.settings.checkbox.cascade, i, j, t = this.settings.checkbox.tie_selection, d = this._data[ t ? 'core' : 'checkbox' ].selected, m = this._model.data;\n            if(!obj || obj.state[ t ? 'selected' : 'checked' ] === true || s.indexOf('undetermined') === -1 || (s.indexOf('down') === -1 && s.indexOf('up') === -1)) {\n                return false;\n            }\n            if(!obj.state.loaded && obj.original.state.undetermined === true) {\n                return true;\n            }\n            for(i = 0, j = obj.children_d.length; i < j; i++) {\n                if($.inArray(obj.children_d[i], d) !== -1 || (!m[obj.children_d[i]].state.loaded && m[obj.children_d[i]].original.state.undetermined)) {\n                    return true;\n                }\n            }\n            return false;\n        };\n        /**\n         * disable a node's checkbox\n         * @name disable_checkbox(obj)\n         * @param {mixed} obj an array can be used too\n         * @trigger disable_checkbox.jstree\n         * @plugin checkbox\n         */\n        this.disable_checkbox = function (obj) {\n            var t1, t2, dom;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.disable_checkbox(obj[t1]);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            dom = this.get_node(obj, true);\n            if(!obj.state.checkbox_disabled) {\n                obj.state.checkbox_disabled = true;\n                if(dom && dom.length) {\n                    dom.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-checkbox-disabled');\n                }\n                /**\n                 * triggered when an node's checkbox is disabled\n                 * @event\n                 * @name disable_checkbox.jstree\n                 * @param {Object} node\n                 * @plugin checkbox\n                 */\n                this.trigger('disable_checkbox', { 'node' : obj });\n            }\n        };\n        /**\n         * enable a node's checkbox\n         * @name enable_checkbox(obj)\n         * @param {mixed} obj an array can be used too\n         * @trigger enable_checkbox.jstree\n         * @plugin checkbox\n         */\n        this.enable_checkbox = function (obj) {\n            var t1, t2, dom;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.enable_checkbox(obj[t1]);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            dom = this.get_node(obj, true);\n            if(obj.state.checkbox_disabled) {\n                obj.state.checkbox_disabled = false;\n                if(dom && dom.length) {\n                    dom.children('.jstree-anchor').children('.jstree-checkbox').removeClass('jstree-checkbox-disabled');\n                }\n                /**\n                 * triggered when an node's checkbox is enabled\n                 * @event\n                 * @name enable_checkbox.jstree\n                 * @param {Object} node\n                 * @plugin checkbox\n                 */\n                this.trigger('enable_checkbox', { 'node' : obj });\n            }\n        };\n\n        this.activate_node = function (obj, e) {\n            if($(e.target).hasClass('jstree-checkbox-disabled')) {\n                return false;\n            }\n            if(this.settings.checkbox.tie_selection && (this.settings.checkbox.whole_node || $(e.target).hasClass('jstree-checkbox'))) {\n                e.ctrlKey = true;\n            }\n            if(this.settings.checkbox.tie_selection || (!this.settings.checkbox.whole_node && !$(e.target).hasClass('jstree-checkbox'))) {\n                return parent.activate_node.call(this, obj, e);\n            }\n            if(this.is_disabled(obj)) {\n                return false;\n            }\n            if(this.is_checked(obj)) {\n                this.uncheck_node(obj, e);\n            }\n            else {\n                this.check_node(obj, e);\n            }\n            this.trigger('activate_node', { 'node' : this.get_node(obj) });\n        };\n\n        /**\n         * Cascades checked state to a node and all its descendants. This function does NOT affect hidden and disabled nodes (or their descendants).\n         * However if these unaffected nodes are already selected their ids will be included in the returned array.\n         * @private\n         * @name _cascade_new_checked_state(id, checkedState)\n         * @param {string} id the node ID\n         * @param {bool} checkedState should the nodes be checked or not\n         * @returns {Array} Array of all node id's (in this tree branch) that are checked.\n         */\n        this._cascade_new_checked_state = function (id, checkedState) {\n            var self = this;\n            var t = this.settings.checkbox.tie_selection;\n            var node = this._model.data[id];\n            var selectedNodeIds = [];\n            var selectedChildrenIds = [], i, j, selectedChildIds;\n\n            if (\n                (this.settings.checkbox.cascade_to_disabled || !node.state.disabled) &&\n                (this.settings.checkbox.cascade_to_hidden || !node.state.hidden)\n            ) {\n                //First try and check/uncheck the children\n                if (node.children) {\n                    for (i = 0, j = node.children.length; i < j; i++) {\n                        var childId = node.children[i];\n                        selectedChildIds = self._cascade_new_checked_state(childId, checkedState);\n                        selectedNodeIds = selectedNodeIds.concat(selectedChildIds);\n                        if (selectedChildIds.indexOf(childId) > -1) {\n                            selectedChildrenIds.push(childId);\n                        }\n                    }\n                }\n\n                var dom = self.get_node(node, true);\n\n                //A node's state is undetermined if some but not all of it's children are checked/selected .\n                var undetermined = selectedChildrenIds.length > 0 && selectedChildrenIds.length < node.children.length;\n\n                if(node.original && node.original.state && node.original.state.undetermined) {\n                    node.original.state.undetermined = undetermined;\n                }\n\n                //If a node is undetermined then remove selected class\n                if (undetermined) {\n                    node.state[ t ? 'selected' : 'checked' ] = false;\n                    dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');\n                }\n                    //Otherwise, if the checkedState === true (i.e. the node is being checked now) and all of the node's children are checked (if it has any children),\n                //check the node and style it correctly.\n                else if (checkedState && selectedChildrenIds.length === node.children.length) {\n                    node.state[ t ? 'selected' : 'checked' ] = checkedState;\n                    selectedNodeIds.push(node.id);\n\n                    dom.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');\n                }\n                else {\n                    node.state[ t ? 'selected' : 'checked' ] = false;\n                    dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');\n                }\n            }\n            else {\n                selectedChildIds = this.get_checked_descendants(id);\n\n                if (node.state[ t ? 'selected' : 'checked' ]) {\n                    selectedChildIds.push(node.id);\n                }\n\n                selectedNodeIds = selectedNodeIds.concat(selectedChildIds);\n            }\n\n            return selectedNodeIds;\n        };\n\n        /**\n         * Gets ids of nodes selected in branch (of tree) specified by id (does not include the node specified by id)\n         * @name get_checked_descendants(obj)\n         * @param {string} id the node ID\n         * @return {Array} array of IDs\n         * @plugin checkbox\n         */\n        this.get_checked_descendants = function (id) {\n            var self = this;\n            var t = self.settings.checkbox.tie_selection;\n            var node = self._model.data[id];\n\n            return $.vakata.array_filter(node.children_d, function(_id) {\n                return self._model.data[_id].state[ t ? 'selected' : 'checked' ];\n            });\n        };\n\n        /**\n         * check a node (only if tie_selection in checkbox settings is false, otherwise select_node will be called internally)\n         * @name check_node(obj)\n         * @param {mixed} obj an array can be used to check multiple nodes\n         * @trigger check_node.jstree\n         * @plugin checkbox\n         */\n        this.check_node = function (obj, e) {\n            if(this.settings.checkbox.tie_selection) { return this.select_node(obj, false, true, e); }\n            var dom, t1, t2, th;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.check_node(obj[t1], e);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            dom = this.get_node(obj, true);\n            if(!obj.state.checked) {\n                obj.state.checked = true;\n                this._data.checkbox.selected.push(obj.id);\n                if(dom && dom.length) {\n                    dom.children('.jstree-anchor').addClass('jstree-checked');\n                }\n                /**\n                 * triggered when an node is checked (only if tie_selection in checkbox settings is false)\n                 * @event\n                 * @name check_node.jstree\n                 * @param {Object} node\n                 * @param {Array} selected the current selection\n                 * @param {Object} event the event (if any) that triggered this check_node\n                 * @plugin checkbox\n                 */\n                this.trigger('check_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e });\n            }\n        };\n        /**\n         * uncheck a node (only if tie_selection in checkbox settings is false, otherwise deselect_node will be called internally)\n         * @name uncheck_node(obj)\n         * @param {mixed} obj an array can be used to uncheck multiple nodes\n         * @trigger uncheck_node.jstree\n         * @plugin checkbox\n         */\n        this.uncheck_node = function (obj, e) {\n            if(this.settings.checkbox.tie_selection) { return this.deselect_node(obj, false, e); }\n            var t1, t2, dom;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.uncheck_node(obj[t1], e);\n                }\n                return true;\n            }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) {\n                return false;\n            }\n            dom = this.get_node(obj, true);\n            if(obj.state.checked) {\n                obj.state.checked = false;\n                this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, obj.id);\n                if(dom.length) {\n                    dom.children('.jstree-anchor').removeClass('jstree-checked');\n                }\n                /**\n                 * triggered when an node is unchecked (only if tie_selection in checkbox settings is false)\n                 * @event\n                 * @name uncheck_node.jstree\n                 * @param {Object} node\n                 * @param {Array} selected the current selection\n                 * @param {Object} event the event (if any) that triggered this uncheck_node\n                 * @plugin checkbox\n                 */\n                this.trigger('uncheck_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e });\n            }\n        };\n\n        /**\n         * checks all nodes in the tree (only if tie_selection in checkbox settings is false, otherwise select_all will be called internally)\n         * @name check_all()\n         * @trigger check_all.jstree, changed.jstree\n         * @plugin checkbox\n         */\n        this.check_all = function () {\n            if(this.settings.checkbox.tie_selection) { return this.select_all(); }\n            var tmp = this._data.checkbox.selected.concat([]), i, j;\n            this._data.checkbox.selected = this._model.data[$.jstree.root].children_d.concat();\n            for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) {\n                if(this._model.data[this._data.checkbox.selected[i]]) {\n                    this._model.data[this._data.checkbox.selected[i]].state.checked = true;\n                }\n            }\n            this.redraw(true);\n            /**\n             * triggered when all nodes are checked (only if tie_selection in checkbox settings is false)\n             * @event\n             * @name check_all.jstree\n             * @param {Array} selected the current selection\n             * @plugin checkbox\n             */\n            this.trigger('check_all', { 'selected' : this._data.checkbox.selected });\n        };\n        /**\n         * uncheck all checked nodes (only if tie_selection in checkbox settings is false, otherwise deselect_all will be called internally)\n         * @name uncheck_all()\n         * @trigger uncheck_all.jstree\n         * @plugin checkbox\n         */\n        this.uncheck_all = function () {\n            if(this.settings.checkbox.tie_selection) { return this.deselect_all(); }\n            var tmp = this._data.checkbox.selected.concat([]), i, j;\n            for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) {\n                if(this._model.data[this._data.checkbox.selected[i]]) {\n                    this._model.data[this._data.checkbox.selected[i]].state.checked = false;\n                }\n            }\n            this._data.checkbox.selected = [];\n            this.element.find('.jstree-checked').removeClass('jstree-checked');\n            /**\n             * triggered when all nodes are unchecked (only if tie_selection in checkbox settings is false)\n             * @event\n             * @name uncheck_all.jstree\n             * @param {Object} node the previous selection\n             * @param {Array} selected the current selection\n             * @plugin checkbox\n             */\n            this.trigger('uncheck_all', { 'selected' : this._data.checkbox.selected, 'node' : tmp });\n        };\n        /**\n         * checks if a node is checked (if tie_selection is on in the settings this function will return the same as is_selected)\n         * @name is_checked(obj)\n         * @param  {mixed}  obj\n         * @return {Boolean}\n         * @plugin checkbox\n         */\n        this.is_checked = function (obj) {\n            if(this.settings.checkbox.tie_selection) { return this.is_selected(obj); }\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n            return obj.state.checked;\n        };\n        /**\n         * get an array of all checked nodes (if tie_selection is on in the settings this function will return the same as get_selected)\n         * @name get_checked([full])\n         * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned\n         * @return {Array}\n         * @plugin checkbox\n         */\n        this.get_checked = function (full) {\n            if(this.settings.checkbox.tie_selection) { return this.get_selected(full); }\n            return full ? $.map(this._data.checkbox.selected, function (i) { return this.get_node(i); }.bind(this)) : this._data.checkbox.selected.slice();\n        };\n        /**\n         * get an array of all top level checked nodes (ignoring children of checked nodes) (if tie_selection is on in the settings this function will return the same as get_top_selected)\n         * @name get_top_checked([full])\n         * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned\n         * @return {Array}\n         * @plugin checkbox\n         */\n        this.get_top_checked = function (full) {\n            if(this.settings.checkbox.tie_selection) { return this.get_top_selected(full); }\n            var tmp = this.get_checked(true),\n                obj = {}, i, j, k, l;\n            for(i = 0, j = tmp.length; i < j; i++) {\n                obj[tmp[i].id] = tmp[i];\n            }\n            for(i = 0, j = tmp.length; i < j; i++) {\n                for(k = 0, l = tmp[i].children_d.length; k < l; k++) {\n                    if(obj[tmp[i].children_d[k]]) {\n                        delete obj[tmp[i].children_d[k]];\n                    }\n                }\n            }\n            tmp = [];\n            for(i in obj) {\n                if(obj.hasOwnProperty(i)) {\n                    tmp.push(i);\n                }\n            }\n            return full ? $.map(tmp, function (i) { return this.get_node(i); }.bind(this)) : tmp;\n        };\n        /**\n         * get an array of all bottom level checked nodes (ignoring selected parents) (if tie_selection is on in the settings this function will return the same as get_bottom_selected)\n         * @name get_bottom_checked([full])\n         * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned\n         * @return {Array}\n         * @plugin checkbox\n         */\n        this.get_bottom_checked = function (full) {\n            if(this.settings.checkbox.tie_selection) { return this.get_bottom_selected(full); }\n            var tmp = this.get_checked(true),\n                obj = [], i, j;\n            for(i = 0, j = tmp.length; i < j; i++) {\n                if(!tmp[i].children.length) {\n                    obj.push(tmp[i].id);\n                }\n            }\n            return full ? $.map(obj, function (i) { return this.get_node(i); }.bind(this)) : obj;\n        };\n        this.load_node = function (obj, callback) {\n            var k, l, i, j, c, tmp;\n            if(!$.vakata.is_array(obj) && !this.settings.checkbox.tie_selection) {\n                tmp = this.get_node(obj);\n                if(tmp && tmp.state.loaded) {\n                    for(k = 0, l = tmp.children_d.length; k < l; k++) {\n                        if(this._model.data[tmp.children_d[k]].state.checked) {\n                            c = true;\n                            this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, tmp.children_d[k]);\n                        }\n                    }\n                }\n            }\n            return parent.load_node.apply(this, arguments);\n        };\n        this.get_state = function () {\n            var state = parent.get_state.apply(this, arguments);\n            if(this.settings.checkbox.tie_selection) { return state; }\n            state.checkbox = this._data.checkbox.selected.slice();\n            return state;\n        };\n        this.set_state = function (state, callback) {\n            var res = parent.set_state.apply(this, arguments);\n            if(res && state.checkbox) {\n                if(!this.settings.checkbox.tie_selection) {\n                    this.uncheck_all();\n                    var _this = this;\n                    $.each(state.checkbox, function (i, v) {\n                        _this.check_node(v);\n                    });\n                }\n                delete state.checkbox;\n                this.set_state(state, callback);\n                return false;\n            }\n            return res;\n        };\n        this.refresh = function (skip_loading, forget_state) {\n            if(this.settings.checkbox.tie_selection) {\n                this._data.checkbox.selected = [];\n            }\n            return parent.refresh.apply(this, arguments);\n        };\n    };\n\n    // include the checkbox plugin by default\n    // $.jstree.defaults.plugins.push(\"checkbox\");\n\n\n    /**\n     * ### Conditionalselect plugin\n     *\n     * This plugin allows defining a callback to allow or deny node selection by user input (activate node method).\n     */\n\n    /**\n     * a callback (function) which is invoked in the instance's scope and receives two arguments - the node and the event that triggered the `activate_node` call. Returning false prevents working with the node, returning true allows invoking activate_node. Defaults to returning `true`.\n     * @name $.jstree.defaults.checkbox.visible\n     * @plugin checkbox\n     */\n    $.jstree.defaults.conditionalselect = function () { return true; };\n    $.jstree.plugins.conditionalselect = function (options, parent) {\n        // own function\n        this.activate_node = function (obj, e) {\n            if(this.settings.conditionalselect.call(this, this.get_node(obj), e)) {\n                return parent.activate_node.call(this, obj, e);\n            }\n        };\n    };\n\n\n    /**\n     * ### Contextmenu plugin\n     *\n     * Shows a context menu when a node is right-clicked.\n     */\n\n    /**\n     * stores all defaults for the contextmenu plugin\n     * @name $.jstree.defaults.contextmenu\n     * @plugin contextmenu\n     */\n    $.jstree.defaults.contextmenu = {\n        /**\n         * a boolean indicating if the node should be selected when the context menu is invoked on it. Defaults to `true`.\n         * @name $.jstree.defaults.contextmenu.select_node\n         * @plugin contextmenu\n         */\n        select_node : true,\n        /**\n         * a boolean indicating if the menu should be shown aligned with the node. Defaults to `true`, otherwise the mouse coordinates are used.\n         * @name $.jstree.defaults.contextmenu.show_at_node\n         * @plugin contextmenu\n         */\n        show_at_node : true,\n        /**\n         * an object of actions, or a function that accepts a node and a callback function and calls the callback function with an object of actions available for that node (you can also return the items too).\n         *\n         * Each action consists of a key (a unique name) and a value which is an object with the following properties (only label and action are required). Once a menu item is activated the `action` function will be invoked with an object containing the following keys: item - the contextmenu item definition as seen below, reference - the DOM node that was used (the tree node), element - the contextmenu DOM element, position - an object with x/y properties indicating the position of the menu.\n         *\n         * * `separator_before` - a boolean indicating if there should be a separator before this item\n         * * `separator_after` - a boolean indicating if there should be a separator after this item\n         * * `_disabled` - a boolean indicating if this action should be disabled\n         * * `label` - a string - the name of the action (could be a function returning a string)\n         * * `title` - a string - an optional tooltip for the item\n         * * `action` - a function to be executed if this item is chosen, the function will receive\n         * * `icon` - a string, can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class\n         * * `shortcut` - keyCode which will trigger the action if the menu is open (for example `113` for rename, which equals F2)\n         * * `shortcut_label` - shortcut label (like for example `F2` for rename)\n         * * `submenu` - an object with the same structure as $.jstree.defaults.contextmenu.items which can be used to create a submenu - each key will be rendered as a separate option in a submenu that will appear once the current item is hovered\n         *\n         * @name $.jstree.defaults.contextmenu.items\n         * @plugin contextmenu\n         */\n        items : function (o, cb) { // Could be an object directly\n            return {\n                \"create\" : {\n                    \"separator_before\"\t: false,\n                    \"separator_after\"\t: true,\n                    \"_disabled\"\t\t\t: false, //(this.check(\"create_node\", data.reference, {}, \"last\")),\n                    \"label\"\t\t\t\t: \"Create\",\n                    \"action\"\t\t\t: function (data) {\n                        var inst = $.jstree.reference(data.reference),\n                            obj = inst.get_node(data.reference);\n                        inst.create_node(obj, {}, \"last\", function (new_node) {\n                            try {\n                                inst.edit(new_node);\n                            } catch (ex) {\n                                setTimeout(function () { inst.edit(new_node); },0);\n                            }\n                        });\n                    }\n                },\n                \"rename\" : {\n                    \"separator_before\"\t: false,\n                    \"separator_after\"\t: false,\n                    \"_disabled\"\t\t\t: false, //(this.check(\"rename_node\", data.reference, this.get_parent(data.reference), \"\")),\n                    \"label\"\t\t\t\t: \"Rename\",\n                    /*!\n\t\t\t\t\t\"shortcut\"\t\t\t: 113,\n\t\t\t\t\t\"shortcut_label\"\t: 'F2',\n\t\t\t\t\t\"icon\"\t\t\t\t: \"glyphicon glyphicon-leaf\",\n\t\t\t\t\t*/\n                    \"action\"\t\t\t: function (data) {\n                        var inst = $.jstree.reference(data.reference),\n                            obj = inst.get_node(data.reference);\n                        inst.edit(obj);\n                    }\n                },\n                \"remove\" : {\n                    \"separator_before\"\t: false,\n                    \"icon\"\t\t\t\t: false,\n                    \"separator_after\"\t: false,\n                    \"_disabled\"\t\t\t: false, //(this.check(\"delete_node\", data.reference, this.get_parent(data.reference), \"\")),\n                    \"label\"\t\t\t\t: \"Delete\",\n                    \"action\"\t\t\t: function (data) {\n                        var inst = $.jstree.reference(data.reference),\n                            obj = inst.get_node(data.reference);\n                        if(inst.is_selected(obj)) {\n                            inst.delete_node(inst.get_selected());\n                        }\n                        else {\n                            inst.delete_node(obj);\n                        }\n                    }\n                },\n                \"ccp\" : {\n                    \"separator_before\"\t: true,\n                    \"icon\"\t\t\t\t: false,\n                    \"separator_after\"\t: false,\n                    \"label\"\t\t\t\t: \"Edit\",\n                    \"action\"\t\t\t: false,\n                    \"submenu\" : {\n                        \"cut\" : {\n                            \"separator_before\"\t: false,\n                            \"separator_after\"\t: false,\n                            \"label\"\t\t\t\t: \"Cut\",\n                            \"action\"\t\t\t: function (data) {\n                                var inst = $.jstree.reference(data.reference),\n                                    obj = inst.get_node(data.reference);\n                                if(inst.is_selected(obj)) {\n                                    inst.cut(inst.get_top_selected());\n                                }\n                                else {\n                                    inst.cut(obj);\n                                }\n                            }\n                        },\n                        \"copy\" : {\n                            \"separator_before\"\t: false,\n                            \"icon\"\t\t\t\t: false,\n                            \"separator_after\"\t: false,\n                            \"label\"\t\t\t\t: \"Copy\",\n                            \"action\"\t\t\t: function (data) {\n                                var inst = $.jstree.reference(data.reference),\n                                    obj = inst.get_node(data.reference);\n                                if(inst.is_selected(obj)) {\n                                    inst.copy(inst.get_top_selected());\n                                }\n                                else {\n                                    inst.copy(obj);\n                                }\n                            }\n                        },\n                        \"paste\" : {\n                            \"separator_before\"\t: false,\n                            \"icon\"\t\t\t\t: false,\n                            \"_disabled\"\t\t\t: function (data) {\n                                return !$.jstree.reference(data.reference).can_paste();\n                            },\n                            \"separator_after\"\t: false,\n                            \"label\"\t\t\t\t: \"Paste\",\n                            \"action\"\t\t\t: function (data) {\n                                var inst = $.jstree.reference(data.reference),\n                                    obj = inst.get_node(data.reference);\n                                inst.paste(obj);\n                            }\n                        }\n                    }\n                }\n            };\n        }\n    };\n\n    $.jstree.plugins.contextmenu = function (options, parent) {\n        this.bind = function () {\n            parent.bind.call(this);\n\n            var last_ts = 0, cto = null, ex, ey;\n            this.element\n                .on(\"init.jstree loading.jstree ready.jstree\", function () {\n                    this.get_container_ul().addClass('jstree-contextmenu');\n                }.bind(this))\n                .on(\"contextmenu.jstree\", \".jstree-anchor\", function (e, data) {\n                    if (e.target.tagName.toLowerCase() === 'input') {\n                        return;\n                    }\n                    e.preventDefault();\n                    last_ts = e.ctrlKey ? +new Date() : 0;\n                    if(data || cto) {\n                        last_ts = (+new Date()) + 10000;\n                    }\n                    if(cto) {\n                        clearTimeout(cto);\n                    }\n                    if(!this.is_loading(e.currentTarget)) {\n                        this.show_contextmenu(e.currentTarget, e.pageX, e.pageY, e);\n                    }\n                }.bind(this))\n                .on(\"click.jstree\", \".jstree-anchor\", function (e) {\n                    if(this._data.contextmenu.visible && (!last_ts || (+new Date()) - last_ts > 250)) { // work around safari & macOS ctrl+click\n                        $.vakata.context.hide();\n                    }\n                    last_ts = 0;\n                }.bind(this))\n                .on(\"touchstart.jstree\", \".jstree-anchor\", function (e) {\n                    if(!e.originalEvent || !e.originalEvent.changedTouches || !e.originalEvent.changedTouches[0]) {\n                        return;\n                    }\n                    ex = e.originalEvent.changedTouches[0].clientX;\n                    ey = e.originalEvent.changedTouches[0].clientY;\n                    cto = setTimeout(function () {\n                        $(e.currentTarget).trigger('contextmenu', true);\n                    }, 750);\n                })\n                .on('touchmove.vakata.jstree', function (e) {\n                    if(cto && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0] && (Math.abs(ex - e.originalEvent.changedTouches[0].clientX) > 10 || Math.abs(ey - e.originalEvent.changedTouches[0].clientY) > 10)) {\n                        clearTimeout(cto);\n                        $.vakata.context.hide();\n                    }\n                })\n                .on('touchend.vakata.jstree', function (e) {\n                    if(cto) {\n                        clearTimeout(cto);\n                    }\n                });\n\n            /*!\n\t\t\tif(!('oncontextmenu' in document.body) && ('ontouchstart' in document.body)) {\n\t\t\t\tvar el = null, tm = null;\n\t\t\t\tthis.element\n\t\t\t\t\t.on(\"touchstart\", \".jstree-anchor\", function (e) {\n\t\t\t\t\t\tel = e.currentTarget;\n\t\t\t\t\t\ttm = +new Date();\n\t\t\t\t\t\t$(document).one(\"touchend\", function (e) {\n\t\t\t\t\t\t\te.target = document.elementFromPoint(e.originalEvent.targetTouches[0].pageX - window.pageXOffset, e.originalEvent.targetTouches[0].pageY - window.pageYOffset);\n\t\t\t\t\t\t\te.currentTarget = e.target;\n\t\t\t\t\t\t\ttm = ((+(new Date())) - tm);\n\t\t\t\t\t\t\tif(e.target === el && tm > 600 && tm < 1000) {\n\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t$(el).trigger('contextmenu', e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tel = null;\n\t\t\t\t\t\t\ttm = null;\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t}\n\t\t\t*/\n            $(document).on(\"context_hide.vakata.jstree\", function (e, data) {\n                this._data.contextmenu.visible = false;\n                $(data.reference).removeClass('jstree-context');\n            }.bind(this));\n        };\n        this.teardown = function () {\n            if(this._data.contextmenu.visible) {\n                $.vakata.context.hide();\n            }\n            $(document).off(\"context_hide.vakata.jstree\");\n            parent.teardown.call(this);\n        };\n\n        /**\n         * prepare and show the context menu for a node\n         * @name show_contextmenu(obj [, x, y])\n         * @param {mixed} obj the node\n         * @param {Number} x the x-coordinate relative to the document to show the menu at\n         * @param {Number} y the y-coordinate relative to the document to show the menu at\n         * @param {Object} e the event if available that triggered the contextmenu\n         * @plugin contextmenu\n         * @trigger show_contextmenu.jstree\n         */\n        this.show_contextmenu = function (obj, x, y, e) {\n            obj = this.get_node(obj);\n            if(!obj || obj.id === $.jstree.root) { return false; }\n            var s = this.settings.contextmenu,\n                d = this.get_node(obj, true),\n                a = d.children(\".jstree-anchor\"),\n                o = false,\n                i = false;\n            if(s.show_at_node || x === undefined || y === undefined) {\n                o = a.offset();\n                x = o.left;\n                y = o.top + this._data.core.li_height;\n            }\n            if(this.settings.contextmenu.select_node && !this.is_selected(obj)) {\n                this.activate_node(obj, e);\n            }\n\n            i = s.items;\n            if($.vakata.is_function(i)) {\n                i = i.call(this, obj, function (i) {\n                    this._show_contextmenu(obj, x, y, i);\n                }.bind(this));\n            }\n            if($.isPlainObject(i)) {\n                this._show_contextmenu(obj, x, y, i);\n            }\n        };\n        /**\n         * show the prepared context menu for a node\n         * @name _show_contextmenu(obj, x, y, i)\n         * @param {mixed} obj the node\n         * @param {Number} x the x-coordinate relative to the document to show the menu at\n         * @param {Number} y the y-coordinate relative to the document to show the menu at\n         * @param {Number} i the object of items to show\n         * @plugin contextmenu\n         * @trigger show_contextmenu.jstree\n         * @private\n         */\n        this._show_contextmenu = function (obj, x, y, i) {\n            var d = this.get_node(obj, true),\n                a = d.children(\".jstree-anchor\");\n            $(document).one(\"context_show.vakata.jstree\", function (e, data) {\n                var cls = 'jstree-contextmenu jstree-' + this.get_theme() + '-contextmenu';\n                $(data.element).addClass(cls);\n                a.addClass('jstree-context');\n            }.bind(this));\n            this._data.contextmenu.visible = true;\n            $.vakata.context.show(a, { 'x' : x, 'y' : y }, i);\n            /**\n             * triggered when the contextmenu is shown for a node\n             * @event\n             * @name show_contextmenu.jstree\n             * @param {Object} node the node\n             * @param {Number} x the x-coordinate of the menu relative to the document\n             * @param {Number} y the y-coordinate of the menu relative to the document\n             * @plugin contextmenu\n             */\n            this.trigger('show_contextmenu', { \"node\" : obj, \"x\" : x, \"y\" : y });\n        };\n    };\n\n    // contextmenu helper\n    (function ($) {\n        var right_to_left = false,\n            vakata_context = {\n                element\t\t: false,\n                reference\t: false,\n                position_x\t: 0,\n                position_y\t: 0,\n                items\t\t: [],\n                html\t\t: \"\",\n                is_visible\t: false\n            };\n\n        $.vakata.context = {\n            settings : {\n                hide_onmouseleave\t: 0,\n                icons\t\t\t\t: true\n            },\n            _trigger : function (event_name) {\n                $(document).triggerHandler(\"context_\" + event_name + \".vakata\", {\n                    \"reference\"\t: vakata_context.reference,\n                    \"element\"\t: vakata_context.element,\n                    \"position\"\t: {\n                        \"x\" : vakata_context.position_x,\n                        \"y\" : vakata_context.position_y\n                    }\n                });\n            },\n            _execute : function (i) {\n                i = vakata_context.items[i];\n                return i && (!i._disabled || ($.vakata.is_function(i._disabled) && !i._disabled({ \"item\" : i, \"reference\" : vakata_context.reference, \"element\" : vakata_context.element }))) && i.action ? i.action.call(null, {\n                    \"item\"\t\t: i,\n                    \"reference\"\t: vakata_context.reference,\n                    \"element\"\t: vakata_context.element,\n                    \"position\"\t: {\n                        \"x\" : vakata_context.position_x,\n                        \"y\" : vakata_context.position_y\n                    }\n                }) : false;\n            },\n            _parse : function (o, is_callback) {\n                if(!o) { return false; }\n                if(!is_callback) {\n                    vakata_context.html\t\t= \"\";\n                    vakata_context.items\t= [];\n                }\n                var str = \"\",\n                    sep = false,\n                    tmp;\n\n                if(is_callback) { str += \"<\"+\"ul>\"; }\n                $.each(o, function (i, val) {\n                    if(!val) { return true; }\n                    vakata_context.items.push(val);\n                    if(!sep && val.separator_before) {\n                        str += \"<\"+\"li class='vakata-context-separator'><\"+\"a href='#' \" + ($.vakata.context.settings.icons ? '' : 'class=\"vakata-context-no-icons\"') + \">&#160;<\"+\"/a><\"+\"/li>\";\n                    }\n                    sep = false;\n                    str += \"<\"+\"li class='\" + (val._class || \"\") + (val._disabled === true || ($.vakata.is_function(val._disabled) && val._disabled({ \"item\" : val, \"reference\" : vakata_context.reference, \"element\" : vakata_context.element })) ? \" vakata-contextmenu-disabled \" : \"\") + \"' \"+(val.shortcut?\" data-shortcut='\"+val.shortcut+\"' \":'')+\">\";\n                    str += \"<\"+\"a href='#' rel='\" + (vakata_context.items.length - 1) + \"' \" + (val.title ? \"title='\" + val.title + \"'\" : \"\") + \">\";\n                    if($.vakata.context.settings.icons) {\n                        str += \"<\"+\"i \";\n                        if(val.icon) {\n                            if(val.icon.indexOf(\"/\") !== -1 || val.icon.indexOf(\".\") !== -1) { str += \" style='background:url(\\\"\" + val.icon + \"\\\") center center no-repeat' \"; }\n                            else { str += \" class='\" + val.icon + \"' \"; }\n                        }\n                        str += \"><\"+\"/i><\"+\"span class='vakata-contextmenu-sep'>&#160;<\"+\"/span>\";\n                    }\n                    str += ($.vakata.is_function(val.label) ? val.label({ \"item\" : i, \"reference\" : vakata_context.reference, \"element\" : vakata_context.element }) : val.label) + (val.shortcut?' <span class=\"vakata-contextmenu-shortcut vakata-contextmenu-shortcut-'+val.shortcut+'\">'+ (val.shortcut_label || '') +'</span>':'') + \"<\"+\"/a>\";\n                    if(val.submenu) {\n                        tmp = $.vakata.context._parse(val.submenu, true);\n                        if(tmp) { str += tmp; }\n                    }\n                    str += \"<\"+\"/li>\";\n                    if(val.separator_after) {\n                        str += \"<\"+\"li class='vakata-context-separator'><\"+\"a href='#' \" + ($.vakata.context.settings.icons ? '' : 'class=\"vakata-context-no-icons\"') + \">&#160;<\"+\"/a><\"+\"/li>\";\n                        sep = true;\n                    }\n                });\n                str  = str.replace(/<li class\\='vakata-context-separator'\\><\\/li\\>$/,\"\");\n                if(is_callback) { str += \"</ul>\"; }\n                /**\n                 * triggered on the document when the contextmenu is parsed (HTML is built)\n                 * @event\n                 * @plugin contextmenu\n                 * @name context_parse.vakata\n                 * @param {jQuery} reference the element that was right clicked\n                 * @param {jQuery} element the DOM element of the menu itself\n                 * @param {Object} position the x & y coordinates of the menu\n                 */\n                if(!is_callback) { vakata_context.html = str; $.vakata.context._trigger(\"parse\"); }\n                return str.length > 10 ? str : false;\n            },\n            _show_submenu : function (o) {\n                o = $(o);\n                if(!o.length || !o.children(\"ul\").length) { return; }\n                var e = o.children(\"ul\"),\n                    xl = o.offset().left,\n                    x = xl + o.outerWidth(),\n                    y = o.offset().top,\n                    w = e.width(),\n                    h = e.height(),\n                    dw = $(window).width() + $(window).scrollLeft(),\n                    dh = $(window).height() + $(window).scrollTop();\n                // \u043c\u043e\u0436\u0435 \u0434\u0430 \u0441\u0435 \u0441\u043f\u0435\u0441\u0442\u0438 \u0435 \u0435\u0434\u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 - \u0434\u0430\u043b\u0438 \u043d\u044f\u043c\u0430 \u043d\u044f\u043a\u043e\u0439 \u043e\u0442 \u043a\u043b\u0430\u0441\u043e\u0432\u0435\u0442\u0435 \u0432\u0435\u0447\u0435 \u043d\u0430\u0433\u043e\u0440\u0435\n                if(right_to_left) {\n                    o[x - (w + 10 + o.outerWidth()) < 0 ? \"addClass\" : \"removeClass\"](\"vakata-context-left\");\n                }\n                else {\n                    o[x + w > dw  && xl > dw - x ? \"addClass\" : \"removeClass\"](\"vakata-context-right\");\n                }\n                if(y + h + 10 > dh) {\n                    e.css(\"bottom\",\"-1px\");\n                }\n\n                //if does not fit - stick it to the side\n                if (o.hasClass('vakata-context-right')) {\n                    if (xl < w) {\n                        e.css(\"margin-right\", xl - w);\n                    }\n                } else {\n                    if (dw - x < w) {\n                        e.css(\"margin-left\", dw - x - w);\n                    }\n                }\n\n                e.show();\n            },\n            show : function (reference, position, data) {\n                var o, e, x, y, w, h, dw, dh, cond = true;\n                if(vakata_context.element && vakata_context.element.length) {\n                    vakata_context.element.width('');\n                }\n                switch(cond) {\n                    case (!position && !reference):\n                        return false;\n                    case (!!position && !!reference):\n                        vakata_context.reference\t= reference;\n                        vakata_context.position_x\t= position.x;\n                        vakata_context.position_y\t= position.y;\n                        break;\n                    case (!position && !!reference):\n                        vakata_context.reference\t= reference;\n                        o = reference.offset();\n                        vakata_context.position_x\t= o.left + reference.outerHeight();\n                        vakata_context.position_y\t= o.top;\n                        break;\n                    case (!!position && !reference):\n                        vakata_context.position_x\t= position.x;\n                        vakata_context.position_y\t= position.y;\n                        break;\n                }\n                if(!!reference && !data && $(reference).data('vakata_contextmenu')) {\n                    data = $(reference).data('vakata_contextmenu');\n                }\n                if($.vakata.context._parse(data)) {\n                    vakata_context.element.html(vakata_context.html);\n                }\n                if(vakata_context.items.length) {\n                    vakata_context.element.appendTo(document.body);\n                    e = vakata_context.element;\n                    x = vakata_context.position_x;\n                    y = vakata_context.position_y;\n                    w = e.width();\n                    h = e.height();\n                    dw = $(window).width() + $(window).scrollLeft();\n                    dh = $(window).height() + $(window).scrollTop();\n                    if(right_to_left) {\n                        x -= (e.outerWidth() - $(reference).outerWidth());\n                        if(x < $(window).scrollLeft() + 20) {\n                            x = $(window).scrollLeft() + 20;\n                        }\n                    }\n                    if(x + w + 20 > dw) {\n                        x = dw - (w + 20);\n                    }\n                    if(y + h + 20 > dh) {\n                        y = dh - (h + 20);\n                    }\n\n                    vakata_context.element\n                        .css({ \"left\" : x, \"top\" : y })\n                        .show()\n                        .find('a').first().trigger('focus').parent().addClass(\"vakata-context-hover\");\n                    vakata_context.is_visible = true;\n                    /**\n                     * triggered on the document when the contextmenu is shown\n                     * @event\n                     * @plugin contextmenu\n                     * @name context_show.vakata\n                     * @param {jQuery} reference the element that was right clicked\n                     * @param {jQuery} element the DOM element of the menu itself\n                     * @param {Object} position the x & y coordinates of the menu\n                     */\n                    $.vakata.context._trigger(\"show\");\n                }\n            },\n            hide : function () {\n                if(vakata_context.is_visible) {\n                    vakata_context.element.hide().find(\"ul\").hide().end().find(':focus').trigger('blur').end().detach();\n                    vakata_context.is_visible = false;\n                    /**\n                     * triggered on the document when the contextmenu is hidden\n                     * @event\n                     * @plugin contextmenu\n                     * @name context_hide.vakata\n                     * @param {jQuery} reference the element that was right clicked\n                     * @param {jQuery} element the DOM element of the menu itself\n                     * @param {Object} position the x & y coordinates of the menu\n                     */\n                    $.vakata.context._trigger(\"hide\");\n                }\n            }\n        };\n        $(function () {\n            right_to_left = $(document.body).css(\"direction\") === \"rtl\";\n            var to = false;\n\n            vakata_context.element = $(\"<ul class='vakata-context'></ul>\");\n            vakata_context.element\n                .on(\"mouseenter\", \"li\", function (e) {\n                    e.stopImmediatePropagation();\n\n                    if($.contains(this, e.relatedTarget)) {\n                        // \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u043e \u0437\u0430\u0440\u0430\u0434\u0438 delegate mouseleave \u043f\u043e-\u0434\u043e\u043b\u0443\n                        // $(this).find(\".vakata-context-hover\").removeClass(\"vakata-context-hover\");\n                        return;\n                    }\n\n                    if(to) { clearTimeout(to); }\n                    vakata_context.element.find(\".vakata-context-hover\").removeClass(\"vakata-context-hover\").end();\n\n                    $(this)\n                        .siblings().find(\"ul\").hide().end().end()\n                        .parentsUntil(\".vakata-context\", \"li\").addBack().addClass(\"vakata-context-hover\");\n                    $.vakata.context._show_submenu(this);\n                })\n                // \u0442\u0435\u0441\u0442\u043e\u0432\u043e - \u0434\u0430\u043b\u0438 \u043d\u0435 \u043d\u0430\u0442\u043e\u0432\u0430\u0440\u0432\u0430?\n                .on(\"mouseleave\", \"li\", function (e) {\n                    if($.contains(this, e.relatedTarget)) { return; }\n                    $(this).find(\".vakata-context-hover\").addBack().removeClass(\"vakata-context-hover\");\n                })\n                .on(\"mouseleave\", function (e) {\n                    $(this).find(\".vakata-context-hover\").removeClass(\"vakata-context-hover\");\n                    if($.vakata.context.settings.hide_onmouseleave) {\n                        to = setTimeout(\n                            (function (t) {\n                                return function () { $.vakata.context.hide(); };\n                            }(this)), $.vakata.context.settings.hide_onmouseleave);\n                    }\n                })\n                .on(\"click\", \"a\", function (e) {\n                    e.preventDefault();\n                    //})\n                    //.on(\"mouseup\", \"a\", function (e) {\n                    if(!$(this).trigger('blur').parent().hasClass(\"vakata-context-disabled\") && $.vakata.context._execute($(this).attr(\"rel\")) !== false) {\n                        $.vakata.context.hide();\n                    }\n                })\n                .on('keydown', 'a', function (e) {\n                    var o = null;\n                    switch(e.which) {\n                        case 13:\n                        case 32:\n                            e.type = \"click\";\n                            e.preventDefault();\n                            $(e.currentTarget).trigger(e);\n                            break;\n                        case 37:\n                            if(vakata_context.is_visible) {\n                                vakata_context.element.find(\".vakata-context-hover\").last().closest(\"li\").first().find(\"ul\").hide().find(\".vakata-context-hover\").removeClass(\"vakata-context-hover\").end().end().children('a').trigger('focus');\n                                e.stopImmediatePropagation();\n                                e.preventDefault();\n                            }\n                            break;\n                        case 38:\n                            if(vakata_context.is_visible) {\n                                o = vakata_context.element.find(\"ul:visible\").addBack().last().children(\".vakata-context-hover\").removeClass(\"vakata-context-hover\").prevAll(\"li:not(.vakata-context-separator)\").first();\n                                if(!o.length) { o = vakata_context.element.find(\"ul:visible\").addBack().last().children(\"li:not(.vakata-context-separator)\").last(); }\n                                o.addClass(\"vakata-context-hover\").children('a').trigger('focus');\n                                e.stopImmediatePropagation();\n                                e.preventDefault();\n                            }\n                            break;\n                        case 39:\n                            if(vakata_context.is_visible) {\n                                vakata_context.element.find(\".vakata-context-hover\").last().children(\"ul\").show().children(\"li:not(.vakata-context-separator)\").removeClass(\"vakata-context-hover\").first().addClass(\"vakata-context-hover\").children('a').trigger('focus');\n                                e.stopImmediatePropagation();\n                                e.preventDefault();\n                            }\n                            break;\n                        case 40:\n                            if(vakata_context.is_visible) {\n                                o = vakata_context.element.find(\"ul:visible\").addBack().last().children(\".vakata-context-hover\").removeClass(\"vakata-context-hover\").nextAll(\"li:not(.vakata-context-separator)\").first();\n                                if(!o.length) { o = vakata_context.element.find(\"ul:visible\").addBack().last().children(\"li:not(.vakata-context-separator)\").first(); }\n                                o.addClass(\"vakata-context-hover\").children('a').trigger('focus');\n                                e.stopImmediatePropagation();\n                                e.preventDefault();\n                            }\n                            break;\n                        case 27:\n                            $.vakata.context.hide();\n                            e.preventDefault();\n                            break;\n                        default:\n                            //console.log(e.which);\n                            break;\n                    }\n                })\n                .on('keydown', function (e) {\n                    e.preventDefault();\n                    var a = vakata_context.element.find('.vakata-contextmenu-shortcut-' + e.which).parent();\n                    if(a.parent().not('.vakata-context-disabled')) {\n                        a.trigger('click');\n                    }\n                });\n\n            $(document)\n                .on(\"mousedown.vakata.jstree\", function (e) {\n                    if(vakata_context.is_visible && vakata_context.element[0] !== e.target  && !$.contains(vakata_context.element[0], e.target)) {\n                        $.vakata.context.hide();\n                    }\n                })\n                .on(\"context_show.vakata.jstree\", function (e, data) {\n                    vakata_context.element.find(\"li:has(ul)\").children(\"a\").addClass(\"vakata-context-parent\");\n                    if(right_to_left) {\n                        vakata_context.element.addClass(\"vakata-context-rtl\").css(\"direction\", \"rtl\");\n                    }\n                    // also apply a RTL class?\n                    vakata_context.element.find(\"ul\").hide().end();\n                });\n        });\n    }($));\n    // $.jstree.defaults.plugins.push(\"contextmenu\");\n\n\n    /**\n     * ### Drag'n'drop plugin\n     *\n     * Enables dragging and dropping of nodes in the tree, resulting in a move or copy operations.\n     */\n\n    /**\n     * stores all defaults for the drag'n'drop plugin\n     * @name $.jstree.defaults.dnd\n     * @plugin dnd\n     */\n    $.jstree.defaults.dnd = {\n        /**\n         * a boolean indicating if a copy should be possible while dragging (by pressint the meta key or Ctrl). Defaults to `true`.\n         * @name $.jstree.defaults.dnd.copy\n         * @plugin dnd\n         */\n        copy : true,\n        /**\n         * a number indicating how long a node should remain hovered while dragging to be opened. Defaults to `500`.\n         * @name $.jstree.defaults.dnd.open_timeout\n         * @plugin dnd\n         */\n        open_timeout : 500,\n        /**\n         * a function invoked each time a node is about to be dragged, invoked in the tree's scope and receives the nodes about to be dragged as an argument (array) and the event that started the drag - return `false` to prevent dragging\n         * @name $.jstree.defaults.dnd.is_draggable\n         * @plugin dnd\n         */\n        is_draggable : true,\n        /**\n         * a boolean indicating if checks should constantly be made while the user is dragging the node (as opposed to checking only on drop), default is `true`\n         * @name $.jstree.defaults.dnd.check_while_dragging\n         * @plugin dnd\n         */\n        check_while_dragging : true,\n        /**\n         * a boolean indicating if nodes from this tree should only be copied with dnd (as opposed to moved), default is `false`\n         * @name $.jstree.defaults.dnd.always_copy\n         * @plugin dnd\n         */\n        always_copy : false,\n        /**\n         * when dropping a node \"inside\", this setting indicates the position the node should go to - it can be an integer or a string: \"first\" (same as 0) or \"last\", default is `0`\n         * @name $.jstree.defaults.dnd.inside_pos\n         * @plugin dnd\n         */\n        inside_pos : 0,\n        /**\n         * when starting the drag on a node that is selected this setting controls if all selected nodes are dragged or only the single node, default is `true`, which means all selected nodes are dragged when the drag is started on a selected node\n         * @name $.jstree.defaults.dnd.drag_selection\n         * @plugin dnd\n         */\n        drag_selection : true,\n        /**\n         * controls whether dnd works on touch devices. If left as boolean true dnd will work the same as in desktop browsers, which in some cases may impair scrolling. If set to boolean false dnd will not work on touch devices. There is a special third option - string \"selected\" which means only selected nodes can be dragged on touch devices.\n         * @name $.jstree.defaults.dnd.touch\n         * @plugin dnd\n         */\n        touch : true,\n        /**\n         * controls whether items can be dropped anywhere on the node, not just on the anchor, by default only the node anchor is a valid drop target. Works best with the wholerow plugin. If enabled on mobile depending on the interface it might be hard for the user to cancel the drop, since the whole tree container will be a valid drop target.\n         * @name $.jstree.defaults.dnd.large_drop_target\n         * @plugin dnd\n         */\n        large_drop_target : false,\n        /**\n         * controls whether a drag can be initiated from any part of the node and not just the text/icon part, works best with the wholerow plugin. Keep in mind it can cause problems with tree scrolling on mobile depending on the interface - in that case set the touch option to \"selected\".\n         * @name $.jstree.defaults.dnd.large_drag_target\n         * @plugin dnd\n         */\n        large_drag_target : false,\n        /**\n         * controls whether use HTML5 dnd api instead of classical. That will allow better integration of dnd events with other HTML5 controls.\n         * @reference http://caniuse.com/#feat=dragndrop\n         * @name $.jstree.defaults.dnd.use_html5\n         * @plugin dnd\n         */\n        use_html5: false\n    };\n    var drg, elm;\n    // TODO: now check works by checking for each node individually, how about max_children, unique, etc?\n    $.jstree.plugins.dnd = function (options, parent) {\n        this.init = function (el, options) {\n            parent.init.call(this, el, options);\n            this.settings.dnd.use_html5 = this.settings.dnd.use_html5 && ('draggable' in document.createElement('span'));\n        };\n        this.bind = function () {\n            parent.bind.call(this);\n\n            this.element\n                .on(this.settings.dnd.use_html5 ? 'dragstart.jstree' : 'mousedown.jstree touchstart.jstree', this.settings.dnd.large_drag_target ? '.jstree-node' : '.jstree-anchor', function (e) {\n                    if(this.settings.dnd.large_drag_target && $(e.target).closest('.jstree-node')[0] !== e.currentTarget) {\n                        return true;\n                    }\n                    if(e.type === \"touchstart\" && (!this.settings.dnd.touch || (this.settings.dnd.touch === 'selected' && !$(e.currentTarget).closest('.jstree-node').children('.jstree-anchor').hasClass('jstree-clicked')))) {\n                        return true;\n                    }\n                    var obj = this.get_node(e.target),\n                        mlt = this.is_selected(obj) && this.settings.dnd.drag_selection ? this.get_top_selected().length : 1,\n                        txt = (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget));\n                    if(this.settings.core.force_text) {\n                        txt = $.vakata.html.escape(txt);\n                    }\n                    if(obj && obj.id && obj.id !== $.jstree.root && (e.which === 1 || e.type === \"touchstart\" || e.type === \"dragstart\") &&\n                        (this.settings.dnd.is_draggable === true || ($.vakata.is_function(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, (mlt > 1 ? this.get_top_selected(true) : [obj]), e)))\n                    ) {\n                        drg = { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_top_selected() : [obj.id] };\n                        elm = e.currentTarget;\n                        if (this.settings.dnd.use_html5) {\n                            $.vakata.dnd._trigger('start', e, { 'helper': $(), 'element': elm, 'data': drg });\n                        } else {\n                            this.element.trigger('mousedown.jstree');\n                            return $.vakata.dnd.start(e, drg, '<div id=\"jstree-dnd\" class=\"jstree-' + this.get_theme() + ' jstree-' + this.get_theme() + '-' + this.get_theme_variant() + ' ' + ( this.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ) + '\"><i class=\"jstree-icon jstree-er\"></i>' + txt + '<ins class=\"jstree-copy\">+</ins></div>');\n                        }\n                    }\n                }.bind(this));\n            if (this.settings.dnd.use_html5) {\n                this.element\n                    .on('dragover.jstree', function (e) {\n                        e.preventDefault();\n                        $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });\n                        return false;\n                    })\n                    //.on('dragenter.jstree', this.settings.dnd.large_drop_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) {\n                    //\t\te.preventDefault();\n                    //\t\t$.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });\n                    //\t\treturn false;\n                    //\t}, this))\n                    .on('drop.jstree', function (e) {\n                        e.preventDefault();\n                        $.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg });\n                        return false;\n                    }.bind(this));\n            }\n        };\n        this.redraw_node = function(obj, deep, callback, force_render) {\n            obj = parent.redraw_node.apply(this, arguments);\n            if (obj && this.settings.dnd.use_html5) {\n                if (this.settings.dnd.large_drag_target) {\n                    obj.setAttribute('draggable', true);\n                } else {\n                    var i, j, tmp = null;\n                    for(i = 0, j = obj.childNodes.length; i < j; i++) {\n                        if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf(\"jstree-anchor\") !== -1) {\n                            tmp = obj.childNodes[i];\n                            break;\n                        }\n                    }\n                    if(tmp) {\n                        tmp.setAttribute('draggable', true);\n                    }\n                }\n            }\n            return obj;\n        };\n    };\n\n    $(function() {\n        // bind only once for all instances\n        var lastmv = false,\n            laster = false,\n            lastev = false,\n            opento = false,\n            marker = $('<div id=\"jstree-marker\">&#160;</div>').hide(); //.appendTo('body');\n\n        $(document)\n            .on('dragover.vakata.jstree', function (e) {\n                if (elm) {\n                    $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });\n                }\n            })\n            .on('drop.vakata.jstree', function (e) {\n                if (elm) {\n                    $.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg });\n                    elm = null;\n                    drg = null;\n                }\n            })\n            .on('dnd_start.vakata.jstree', function (e, data) {\n                lastmv = false;\n                lastev = false;\n                if(!data || !data.data || !data.data.jstree) { return; }\n                marker.appendTo(document.body); //.show();\n            })\n            .on('dnd_move.vakata.jstree', function (e, data) {\n                var isDifferentNode = data.event.target !== lastev.target;\n                if(opento) {\n                    if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {\n                        clearTimeout(opento);\n                    }\n                }\n                if(!data || !data.data || !data.data.jstree) { return; }\n\n                // if we are hovering the marker image do nothing (can happen on \"inside\" drags)\n                if(data.event.target.id && data.event.target.id === 'jstree-marker') {\n                    return;\n                }\n                lastev = data.event;\n\n                var ins = $.jstree.reference(data.event.target),\n                    ref = false,\n                    off = false,\n                    rel = false,\n                    tmp, l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm, is_copy, pn, c;\n                // if we are over an instance\n                if(ins && ins._data && ins._data.dnd) {\n                    marker.attr('class', 'jstree-' + ins.get_theme() + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ));\n                    is_copy = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)));\n                    data.helper\n                        .children().attr('class', 'jstree-' + ins.get_theme() + ' jstree-' + ins.get_theme() + '-' + ins.get_theme_variant() + ' ' + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ))\n                        .find('.jstree-copy').first()[ is_copy ? 'show' : 'hide' ]();\n\n                    // if are hovering the container itself add a new root node\n                    //console.log(data.event);\n                    if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) {\n                        ok = true;\n                        for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {\n                            ok = ok && ins.check( (data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)) ) ? \"copy_node\" : \"move_node\"), (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), $.jstree.root, 'last', { 'dnd' : true, 'ref' : ins.get_node($.jstree.root), 'pos' : 'i', 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) });\n                            if(!ok) { break; }\n                        }\n                        if(ok) {\n                            lastmv = { 'ins' : ins, 'par' : $.jstree.root, 'pos' : 'last' };\n                            marker.hide();\n                            data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');\n                            if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {\n                                data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';\n                            }\n                            return;\n                        }\n                    }\n                    else {\n                        // if we are hovering a tree node\n                        ref = ins.settings.dnd.large_drop_target ? $(data.event.target).closest('.jstree-node').children('.jstree-anchor') : $(data.event.target).closest('.jstree-anchor');\n                        if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) {\n                            off = ref.offset();\n                            rel = (data.event.pageY !== undefined ? data.event.pageY : data.event.originalEvent.pageY) - off.top;\n                            h = ref.outerHeight();\n                            if(rel < h / 3) {\n                                o = ['b', 'i', 'a'];\n                            }\n                            else if(rel > h - h / 3) {\n                                o = ['a', 'i', 'b'];\n                            }\n                            else {\n                                o = rel > h / 2 ? ['i', 'a', 'b'] : ['i', 'b', 'a'];\n                            }\n                            $.each(o, function (j, v) {\n                                switch(v) {\n                                    case 'b':\n                                        l = off.left - 6;\n                                        t = off.top;\n                                        p = ins.get_parent(ref);\n                                        i = ref.parent().index();\n                                        c = 'jstree-below';\n                                        break;\n                                    case 'i':\n                                        ip = ins.settings.dnd.inside_pos;\n                                        tm = ins.get_node(ref.parent());\n                                        l = off.left - 2;\n                                        t = off.top + h / 2 + 1;\n                                        p = tm.id;\n                                        i = ip === 'first' ? 0 : (ip === 'last' ? tm.children.length : Math.min(ip, tm.children.length));\n                                        c = 'jstree-inside';\n                                        break;\n                                    case 'a':\n                                        l = off.left - 6;\n                                        t = off.top + h;\n                                        p = ins.get_parent(ref);\n                                        i = ref.parent().index() + 1;\n                                        c = 'jstree-above';\n                                        break;\n                                }\n                                ok = true;\n                                for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {\n                                    op = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? \"copy_node\" : \"move_node\";\n                                    ps = i;\n                                    if(op === \"move_node\" && v === 'a' && (data.data.origin && data.data.origin === ins) && p === ins.get_parent(data.data.nodes[t1])) {\n                                        pr = ins.get_node(p);\n                                        if(ps > $.inArray(data.data.nodes[t1], pr.children)) {\n                                            ps -= 1;\n                                        }\n                                    }\n                                    ok = ok && ( (ins && ins.settings && ins.settings.dnd && ins.settings.dnd.check_while_dragging === false) || ins.check(op, (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), p, ps, { 'dnd' : true, 'ref' : ins.get_node(ref.parent()), 'pos' : v, 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) }) );\n                                    if(!ok) {\n                                        if(ins && ins.last_error) { laster = ins.last_error(); }\n                                        break;\n                                    }\n                                }\n                                if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) {\n                                    if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {\n                                        if (opento) { clearTimeout(opento); }\n                                        opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout);\n                                    }\n                                }\n                                if(ok) {\n                                    pn = ins.get_node(p, true);\n                                    if (!pn.hasClass('.jstree-dnd-parent')) {\n                                        $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');\n                                        pn.addClass('jstree-dnd-parent');\n                                    }\n                                    lastmv = { 'ins' : ins, 'par' : p, 'pos' : v === 'i' && ip === 'last' && i === 0 && !ins.is_loaded(tm) ? 'last' : i };\n                                    marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show();\n                                    marker.removeClass('jstree-above jstree-inside jstree-below').addClass(c);\n                                    data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');\n                                    if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {\n                                        data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';\n                                    }\n                                    laster = {};\n                                    o = true;\n                                    return false;\n                                }\n                            });\n                            if(o === true) { return; }\n                        }\n                    }\n                }\n                $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');\n                lastmv = false;\n                data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er');\n                if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {\n                    //data.event.originalEvent.dataTransfer.dropEffect = 'none';\n                }\n                marker.hide();\n            })\n            .on('dnd_scroll.vakata.jstree', function (e, data) {\n                if(!data || !data.data || !data.data.jstree) { return; }\n                marker.hide();\n                lastmv = false;\n                lastev = false;\n                data.helper.find('.jstree-icon').first().removeClass('jstree-ok').addClass('jstree-er');\n            })\n            .on('dnd_stop.vakata.jstree', function (e, data) {\n                $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');\n                if(opento) { clearTimeout(opento); }\n                if(!data || !data.data || !data.data.jstree) { return; }\n                marker.hide().detach();\n                var i, j, nodes = [];\n                if(lastmv) {\n                    for(i = 0, j = data.data.nodes.length; i < j; i++) {\n                        nodes[i] = data.data.origin ? data.data.origin.get_node(data.data.nodes[i]) : data.data.nodes[i];\n                    }\n                    lastmv.ins[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? 'copy_node' : 'move_node' ](nodes, lastmv.par, lastmv.pos, false, false, false, data.data.origin);\n                }\n                else {\n                    i = $(data.event.target).closest('.jstree');\n                    if(i.length && laster && laster.error && laster.error === 'check') {\n                        i = i.jstree(true);\n                        if(i) {\n                            i.settings.core.error.call(this, laster);\n                        }\n                    }\n                }\n                lastev = false;\n                lastmv = false;\n            })\n            .on('keyup.jstree keydown.jstree', function (e, data) {\n                data = $.vakata.dnd._get();\n                if(data && data.data && data.data.jstree) {\n                    if (e.type === \"keyup\" && e.which === 27) {\n                        if (opento) { clearTimeout(opento); }\n                        lastmv = false;\n                        laster = false;\n                        lastev = false;\n                        opento = false;\n                        marker.hide().detach();\n                        $.vakata.dnd._clean();\n                    } else {\n                        data.helper.find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (e.metaKey || e.ctrlKey))) ? 'show' : 'hide' ]();\n                        if(lastev) {\n                            lastev.metaKey = e.metaKey;\n                            lastev.ctrlKey = e.ctrlKey;\n                            $.vakata.dnd._trigger('move', lastev);\n                        }\n                    }\n                }\n            });\n    });\n\n    // helpers\n    (function ($) {\n        $.vakata.html = {\n            div : $('<div></div>'),\n            escape : function (str) {\n                return $.vakata.html.div.text(str).html();\n            },\n            strip : function (str) {\n                return $.vakata.html.div.empty().append($.parseHTML(str)).text();\n            }\n        };\n        // private variable\n        var vakata_dnd = {\n            element\t: false,\n            target\t: false,\n            is_down\t: false,\n            is_drag\t: false,\n            helper\t: false,\n            helper_w: 0,\n            data\t: false,\n            init_x\t: 0,\n            init_y\t: 0,\n            scroll_l: 0,\n            scroll_t: 0,\n            scroll_e: false,\n            scroll_i: false,\n            is_touch: false\n        };\n        $.vakata.dnd = {\n            settings : {\n                scroll_speed\t\t: 10,\n                scroll_proximity\t: 20,\n                helper_left\t\t\t: 5,\n                helper_top\t\t\t: 10,\n                threshold\t\t\t: 5,\n                threshold_touch\t\t: 10\n            },\n            _trigger : function (event_name, e, data) {\n                if (data === undefined) {\n                    data = $.vakata.dnd._get();\n                }\n                data.event = e;\n                $(document).triggerHandler(\"dnd_\" + event_name + \".vakata\", data);\n            },\n            _get : function () {\n                return {\n                    \"data\"\t\t: vakata_dnd.data,\n                    \"element\"\t: vakata_dnd.element,\n                    \"helper\"\t: vakata_dnd.helper\n                };\n            },\n            _clean : function () {\n                if(vakata_dnd.helper) { vakata_dnd.helper.remove(); }\n                if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }\n                vakata_dnd = {\n                    element\t: false,\n                    target\t: false,\n                    is_down\t: false,\n                    is_drag\t: false,\n                    helper\t: false,\n                    helper_w: 0,\n                    data\t: false,\n                    init_x\t: 0,\n                    init_y\t: 0,\n                    scroll_l: 0,\n                    scroll_t: 0,\n                    scroll_e: false,\n                    scroll_i: false,\n                    is_touch: false\n                };\n                elm = null;\n                $(document).off(\"mousemove.vakata.jstree touchmove.vakata.jstree\", $.vakata.dnd.drag);\n                $(document).off(\"mouseup.vakata.jstree touchend.vakata.jstree\", $.vakata.dnd.stop);\n            },\n            _scroll : function (init_only) {\n                if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) {\n                    if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }\n                    return false;\n                }\n                if(!vakata_dnd.scroll_i) {\n                    vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100);\n                    return false;\n                }\n                if(init_only === true) { return false; }\n\n                var i = vakata_dnd.scroll_e.scrollTop(),\n                    j = vakata_dnd.scroll_e.scrollLeft();\n                vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed);\n                vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed);\n                if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) {\n                    /**\n                     * triggered on the document when a drag causes an element to scroll\n                     * @event\n                     * @plugin dnd\n                     * @name dnd_scroll.vakata\n                     * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start\n                     * @param {DOM} element the DOM element being dragged\n                     * @param {jQuery} helper the helper shown next to the mouse\n                     * @param {jQuery} event the element that is scrolling\n                     */\n                    $.vakata.dnd._trigger(\"scroll\", vakata_dnd.scroll_e);\n                }\n            },\n            start : function (e, data, html) {\n                if(e.type === \"touchstart\" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {\n                    e.pageX = e.originalEvent.changedTouches[0].pageX;\n                    e.pageY = e.originalEvent.changedTouches[0].pageY;\n                    e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);\n                }\n                if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); }\n                try {\n                    e.currentTarget.unselectable = \"on\";\n                    e.currentTarget.onselectstart = function() { return false; };\n                    if(e.currentTarget.style) {\n                        e.currentTarget.style.touchAction = \"none\";\n                        e.currentTarget.style.msTouchAction = \"none\";\n                        e.currentTarget.style.MozUserSelect = \"none\";\n                    }\n                } catch(ignore) { }\n                vakata_dnd.init_x\t= e.pageX;\n                vakata_dnd.init_y\t= e.pageY;\n                vakata_dnd.data\t\t= data;\n                vakata_dnd.is_down\t= true;\n                vakata_dnd.element\t= e.currentTarget;\n                vakata_dnd.target\t= e.target;\n                vakata_dnd.is_touch\t= e.type === \"touchstart\";\n                if(html !== false) {\n                    vakata_dnd.helper = $(\"<div id='vakata-dnd'></div>\").html(html).css({\n                        \"display\"\t\t: \"block\",\n                        \"margin\"\t\t: \"0\",\n                        \"padding\"\t\t: \"0\",\n                        \"position\"\t\t: \"absolute\",\n                        \"top\"\t\t\t: \"-2000px\",\n                        \"lineHeight\"\t: \"16px\",\n                        \"zIndex\"\t\t: \"10000\"\n                    });\n                }\n                $(document).on(\"mousemove.vakata.jstree touchmove.vakata.jstree\", $.vakata.dnd.drag);\n                $(document).on(\"mouseup.vakata.jstree touchend.vakata.jstree\", $.vakata.dnd.stop);\n                return false;\n            },\n            drag : function (e) {\n                if(e.type === \"touchmove\" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {\n                    e.pageX = e.originalEvent.changedTouches[0].pageX;\n                    e.pageY = e.originalEvent.changedTouches[0].pageY;\n                    e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);\n                }\n                if(!vakata_dnd.is_down) { return; }\n                if(!vakata_dnd.is_drag) {\n                    if(\n                        Math.abs(e.pageX - vakata_dnd.init_x) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) ||\n                        Math.abs(e.pageY - vakata_dnd.init_y) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold)\n                    ) {\n                        if(vakata_dnd.helper) {\n                            vakata_dnd.helper.appendTo(document.body);\n                            vakata_dnd.helper_w = vakata_dnd.helper.outerWidth();\n                        }\n                        vakata_dnd.is_drag = true;\n                        $(vakata_dnd.target).one('click.vakata', false);\n                        /**\n                         * triggered on the document when a drag starts\n                         * @event\n                         * @plugin dnd\n                         * @name dnd_start.vakata\n                         * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start\n                         * @param {DOM} element the DOM element being dragged\n                         * @param {jQuery} helper the helper shown next to the mouse\n                         * @param {Object} event the event that caused the start (probably mousemove)\n                         */\n                        $.vakata.dnd._trigger(\"start\", e);\n                    }\n                    else { return; }\n                }\n\n                var d  = false, w  = false,\n                    dh = false, wh = false,\n                    dw = false, ww = false,\n                    dt = false, dl = false,\n                    ht = false, hl = false;\n\n                vakata_dnd.scroll_t = 0;\n                vakata_dnd.scroll_l = 0;\n                vakata_dnd.scroll_e = false;\n                $($(e.target).parentsUntil(\"body\").addBack().get().reverse())\n                    .filter(function () {\n                        return\t(/^auto|scroll$/).test($(this).css(\"overflow\")) &&\n                            (this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth);\n                    })\n                    .each(function () {\n                        var t = $(this), o = t.offset();\n                        if(this.scrollHeight > this.offsetHeight) {\n                            if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity)\t{ vakata_dnd.scroll_t = 1; }\n                            if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity)\t\t\t\t{ vakata_dnd.scroll_t = -1; }\n                        }\n                        if(this.scrollWidth > this.offsetWidth) {\n                            if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity)\t{ vakata_dnd.scroll_l = 1; }\n                            if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity)\t\t\t\t{ vakata_dnd.scroll_l = -1; }\n                        }\n                        if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {\n                            vakata_dnd.scroll_e = $(this);\n                            return false;\n                        }\n                    });\n\n                if(!vakata_dnd.scroll_e) {\n                    d  = $(document); w = $(window);\n                    dh = d.height(); wh = w.height();\n                    dw = d.width(); ww = w.width();\n                    dt = d.scrollTop(); dl = d.scrollLeft();\n                    if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity)\t\t{ vakata_dnd.scroll_t = -1;  }\n                    if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity)\t{ vakata_dnd.scroll_t = 1; }\n                    if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity)\t\t{ vakata_dnd.scroll_l = -1; }\n                    if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity)\t{ vakata_dnd.scroll_l = 1; }\n                    if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {\n                        vakata_dnd.scroll_e = d;\n                    }\n                }\n                if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); }\n\n                if(vakata_dnd.helper) {\n                    ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10);\n                    hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10);\n                    if(dh && ht + 25 > dh) { ht = dh - 50; }\n                    if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); }\n                    vakata_dnd.helper.css({\n                        left\t: hl + \"px\",\n                        top\t\t: ht + \"px\"\n                    });\n                }\n                /**\n                 * triggered on the document when a drag is in progress\n                 * @event\n                 * @plugin dnd\n                 * @name dnd_move.vakata\n                 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start\n                 * @param {DOM} element the DOM element being dragged\n                 * @param {jQuery} helper the helper shown next to the mouse\n                 * @param {Object} event the event that caused this to trigger (most likely mousemove)\n                 */\n                $.vakata.dnd._trigger(\"move\", e);\n                return false;\n            },\n            stop : function (e) {\n                if(e.type === \"touchend\" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {\n                    e.pageX = e.originalEvent.changedTouches[0].pageX;\n                    e.pageY = e.originalEvent.changedTouches[0].pageY;\n                    e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);\n                }\n                if(vakata_dnd.is_drag) {\n                    /**\n                     * triggered on the document when a drag stops (the dragged element is dropped)\n                     * @event\n                     * @plugin dnd\n                     * @name dnd_stop.vakata\n                     * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start\n                     * @param {DOM} element the DOM element being dragged\n                     * @param {jQuery} helper the helper shown next to the mouse\n                     * @param {Object} event the event that caused the stop\n                     */\n                    if (e.target !== vakata_dnd.target) {\n                        $(vakata_dnd.target).off('click.vakata');\n                    }\n                    $.vakata.dnd._trigger(\"stop\", e);\n                }\n                else {\n                    if(e.type === \"touchend\" && e.target === vakata_dnd.target) {\n                        var to = setTimeout(function () { $(e.target).trigger('click'); }, 100);\n                        $(e.target).one('click', function() { if(to) { clearTimeout(to); } });\n                    }\n                }\n                $.vakata.dnd._clean();\n                return false;\n            }\n        };\n    }($));\n\n    // include the dnd plugin by default\n    // $.jstree.defaults.plugins.push(\"dnd\");\n\n\n    /**\n     * ### Massload plugin\n     *\n     * Adds massload functionality to jsTree, so that multiple nodes can be loaded in a single request (only useful with lazy loading).\n     */\n\n    /**\n     * massload configuration\n     *\n     * It is possible to set this to a standard jQuery-like AJAX config.\n     * In addition to the standard jQuery ajax options here you can supply functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node IDs need to be loaded, the return value of those functions will be used.\n     *\n     * You can also set this to a function, that function will receive the node IDs being loaded as argument and a second param which is a function (callback) which should be called with the result.\n     *\n     * Both the AJAX and the function approach rely on the same return value - an object where the keys are the node IDs, and the value is the children of that node as an array.\n     *\n     *\t{\n     *\t\t\"id1\" : [{ \"text\" : \"Child of ID1\", \"id\" : \"c1\" }, { \"text\" : \"Another child of ID1\", \"id\" : \"c2\" }],\n     *\t\t\"id2\" : [{ \"text\" : \"Child of ID2\", \"id\" : \"c3\" }]\n     *\t}\n     *\n     * @name $.jstree.defaults.massload\n     * @plugin massload\n     */\n    $.jstree.defaults.massload = null;\n    $.jstree.plugins.massload = function (options, parent) {\n        this.init = function (el, options) {\n            this._data.massload = {};\n            parent.init.call(this, el, options);\n        };\n        this._load_nodes = function (nodes, callback, is_callback, force_reload) {\n            var s = this.settings.massload,\n                toLoad = [],\n                m = this._model.data,\n                i, j, dom;\n            if (!is_callback) {\n                for(i = 0, j = nodes.length; i < j; i++) {\n                    if(!m[nodes[i]] || ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || force_reload) ) {\n                        toLoad.push(nodes[i]);\n                        dom = this.get_node(nodes[i], true);\n                        if (dom && dom.length) {\n                            dom.addClass(\"jstree-loading\").attr('aria-busy',true);\n                        }\n                    }\n                }\n                this._data.massload = {};\n                if (toLoad.length) {\n                    if($.vakata.is_function(s)) {\n                        return s.call(this, toLoad, function (data) {\n                            var i, j;\n                            if(data) {\n                                for(i in data) {\n                                    if(data.hasOwnProperty(i)) {\n                                        this._data.massload[i] = data[i];\n                                    }\n                                }\n                            }\n                            for(i = 0, j = nodes.length; i < j; i++) {\n                                dom = this.get_node(nodes[i], true);\n                                if (dom && dom.length) {\n                                    dom.removeClass(\"jstree-loading\").attr('aria-busy',false);\n                                }\n                            }\n                            parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);\n                        }.bind(this));\n                    }\n                    if(typeof s === 'object' && s && s.url) {\n                        s = $.extend(true, {}, s);\n                        if($.vakata.is_function(s.url)) {\n                            s.url = s.url.call(this, toLoad);\n                        }\n                        if($.vakata.is_function(s.data)) {\n                            s.data = s.data.call(this, toLoad);\n                        }\n                        return $.ajax(s)\n                            .done(function (data,t,x) {\n                                var i, j;\n                                if(data) {\n                                    for(i in data) {\n                                        if(data.hasOwnProperty(i)) {\n                                            this._data.massload[i] = data[i];\n                                        }\n                                    }\n                                }\n                                for(i = 0, j = nodes.length; i < j; i++) {\n                                    dom = this.get_node(nodes[i], true);\n                                    if (dom && dom.length) {\n                                        dom.removeClass(\"jstree-loading\").attr('aria-busy',false);\n                                    }\n                                }\n                                parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);\n                            }.bind(this))\n                            .fail(function (f) {\n                                parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);\n                            }.bind(this));\n                    }\n                }\n            }\n            return parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);\n        };\n        this._load_node = function (obj, callback) {\n            var data = this._data.massload[obj.id],\n                rslt = null, dom;\n            if(data) {\n                rslt = this[typeof data === 'string' ? '_append_html_data' : '_append_json_data'](\n                    obj,\n                    typeof data === 'string' ? $($.parseHTML(data)).filter(function () { return this.nodeType !== 3; }) : data,\n                    function (status) { callback.call(this, status); }\n                );\n                dom = this.get_node(obj.id, true);\n                if (dom && dom.length) {\n                    dom.removeClass(\"jstree-loading\").attr('aria-busy',false);\n                }\n                delete this._data.massload[obj.id];\n                return rslt;\n            }\n            return parent._load_node.call(this, obj, callback);\n        };\n    };\n\n\n    /**\n     * ### Search plugin\n     *\n     * Adds search functionality to jsTree.\n     */\n\n    /**\n     * stores all defaults for the search plugin\n     * @name $.jstree.defaults.search\n     * @plugin search\n     */\n    $.jstree.defaults.search = {\n        /**\n         * a jQuery-like AJAX config, which jstree uses if a server should be queried for results.\n         *\n         * A `str` (which is the search string) parameter will be added with the request, an optional `inside` parameter will be added if the search is limited to a node id. The expected result is a JSON array with nodes that need to be opened so that matching nodes will be revealed.\n         * Leave this setting as `false` to not query the server. You can also set this to a function, which will be invoked in the instance's scope and receive 3 parameters - the search string, the callback to call with the array of nodes to load, and the optional node ID to limit the search to\n         * @name $.jstree.defaults.search.ajax\n         * @plugin search\n         */\n        ajax : false,\n        /**\n         * Indicates if the search should be fuzzy or not (should `chnd3` match `child node 3`). Default is `false`.\n         * @name $.jstree.defaults.search.fuzzy\n         * @plugin search\n         */\n        fuzzy : false,\n        /**\n         * Indicates if the search should be case sensitive. Default is `false`.\n         * @name $.jstree.defaults.search.case_sensitive\n         * @plugin search\n         */\n        case_sensitive : false,\n        /**\n         * Indicates if the tree should be filtered (by default) to show only matching nodes (keep in mind this can be a heavy on large trees in old browsers).\n         * This setting can be changed at runtime when calling the search method. Default is `false`.\n         * @name $.jstree.defaults.search.show_only_matches\n         * @plugin search\n         */\n        show_only_matches : false,\n        /**\n         * Indicates if the children of matched element are shown (when show_only_matches is true)\n         * This setting can be changed at runtime when calling the search method. Default is `false`.\n         * @name $.jstree.defaults.search.show_only_matches_children\n         * @plugin search\n         */\n        show_only_matches_children : false,\n        /**\n         * Indicates if all nodes opened to reveal the search result, should be closed when the search is cleared or a new search is performed. Default is `true`.\n         * @name $.jstree.defaults.search.close_opened_onclear\n         * @plugin search\n         */\n        close_opened_onclear : true,\n        /**\n         * Indicates if only leaf nodes should be included in search results. Default is `false`.\n         * @name $.jstree.defaults.search.search_leaves_only\n         * @plugin search\n         */\n        search_leaves_only : false,\n        /**\n         * If set to a function it wil be called in the instance's scope with two arguments - search string and node (where node will be every node in the structure, so use with caution).\n         * If the function returns a truthy value the node will be considered a match (it might not be displayed if search_only_leaves is set to true and the node is not a leaf). Default is `false`.\n         * @name $.jstree.defaults.search.search_callback\n         * @plugin search\n         */\n        search_callback : false\n    };\n\n    $.jstree.plugins.search = function (options, parent) {\n        this.bind = function () {\n            parent.bind.call(this);\n\n            this._data.search.str = \"\";\n            this._data.search.dom = $();\n            this._data.search.res = [];\n            this._data.search.opn = [];\n            this._data.search.som = false;\n            this._data.search.smc = false;\n            this._data.search.hdn = [];\n\n            this.element\n                .on(\"search.jstree\", function (e, data) {\n                    if(this._data.search.som && data.res.length) {\n                        var m = this._model.data, i, j, p = [], k, l;\n                        for(i = 0, j = data.res.length; i < j; i++) {\n                            if(m[data.res[i]] && !m[data.res[i]].state.hidden) {\n                                p.push(data.res[i]);\n                                p = p.concat(m[data.res[i]].parents);\n                                if(this._data.search.smc) {\n                                    for (k = 0, l = m[data.res[i]].children_d.length; k < l; k++) {\n                                        if (m[m[data.res[i]].children_d[k]] && !m[m[data.res[i]].children_d[k]].state.hidden) {\n                                            p.push(m[data.res[i]].children_d[k]);\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                        p = $.vakata.array_remove_item($.vakata.array_unique(p), $.jstree.root);\n                        this._data.search.hdn = this.hide_all(true);\n                        this.show_node(p, true);\n                        this.redraw(true);\n                    }\n                }.bind(this))\n                .on(\"clear_search.jstree\", function (e, data) {\n                    if(this._data.search.som && data.res.length) {\n                        this.show_node(this._data.search.hdn, true);\n                        this.redraw(true);\n                    }\n                }.bind(this));\n        };\n        /**\n         * used to search the tree nodes for a given string\n         * @name search(str [, skip_async])\n         * @param {String} str the search string\n         * @param {Boolean} skip_async if set to true server will not be queried even if configured\n         * @param {Boolean} show_only_matches if set to true only matching nodes will be shown (keep in mind this can be very slow on large trees or old browsers)\n         * @param {mixed} inside an optional node to whose children to limit the search\n         * @param {Boolean} append if set to true the results of this search are appended to the previous search\n         * @plugin search\n         * @trigger search.jstree\n         */\n        this.search = function (str, skip_async, show_only_matches, inside, append, show_only_matches_children) {\n            if(str === false || $.vakata.trim(str.toString()) === \"\") {\n                return this.clear_search();\n            }\n            inside = this.get_node(inside);\n            inside = inside && inside.id ? inside.id : null;\n            str = str.toString();\n            var s = this.settings.search,\n                a = s.ajax ? s.ajax : false,\n                m = this._model.data,\n                f = null,\n                r = [],\n                p = [], i, j;\n            if(this._data.search.res.length && !append) {\n                this.clear_search();\n            }\n            if(show_only_matches === undefined) {\n                show_only_matches = s.show_only_matches;\n            }\n            if(show_only_matches_children === undefined) {\n                show_only_matches_children = s.show_only_matches_children;\n            }\n            if(!skip_async && a !== false) {\n                if($.vakata.is_function(a)) {\n                    return a.call(this, str, function (d) {\n                        if(d && d.d) { d = d.d; }\n                        this._load_nodes(!$.vakata.is_array(d) ? [] : $.vakata.array_unique(d), function () {\n                            this.search(str, true, show_only_matches, inside, append, show_only_matches_children);\n                        });\n                    }.bind(this), inside);\n                }\n                else {\n                    a = $.extend({}, a);\n                    if(!a.data) { a.data = {}; }\n                    a.data.str = str;\n                    if(inside) {\n                        a.data.inside = inside;\n                    }\n                    if (this._data.search.lastRequest) {\n                        this._data.search.lastRequest.abort();\n                    }\n                    this._data.search.lastRequest = $.ajax(a)\n                        .fail(function () {\n                            this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'search', 'id' : 'search_01', 'reason' : 'Could not load search parents', 'data' : JSON.stringify(a) };\n                            this.settings.core.error.call(this, this._data.core.last_error);\n                        }.bind(this))\n                        .done(function (d) {\n                            if(d && d.d) { d = d.d; }\n                            this._load_nodes(!$.vakata.is_array(d) ? [] : $.vakata.array_unique(d), function () {\n                                this.search(str, true, show_only_matches, inside, append, show_only_matches_children);\n                            });\n                        }.bind(this));\n                    return this._data.search.lastRequest;\n                }\n            }\n            if(!append) {\n                this._data.search.str = str;\n                this._data.search.dom = $();\n                this._data.search.res = [];\n                this._data.search.opn = [];\n                this._data.search.som = show_only_matches;\n                this._data.search.smc = show_only_matches_children;\n            }\n\n            f = new $.vakata.search(str, true, { caseSensitive : s.case_sensitive, fuzzy : s.fuzzy });\n            $.each(m[inside ? inside : $.jstree.root].children_d, function (ii, i) {\n                var v = m[i];\n                if(v.text && !v.state.hidden && (!s.search_leaves_only || (v.state.loaded && v.children.length === 0)) && ( (s.search_callback && s.search_callback.call(this, str, v)) || (!s.search_callback && f.search(v.text).isMatch) ) ) {\n                    r.push(i);\n                    p = p.concat(v.parents);\n                }\n            });\n            if(r.length) {\n                p = $.vakata.array_unique(p);\n                for(i = 0, j = p.length; i < j; i++) {\n                    if(p[i] !== $.jstree.root && m[p[i]] && this.open_node(p[i], null, 0) === true) {\n                        this._data.search.opn.push(p[i]);\n                    }\n                }\n                if(!append) {\n                    this._data.search.dom = $(this.element[0].querySelectorAll('#' + $.map(r, function (v) { return \"0123456789\".indexOf(v[0]) !== -1 ? '\\\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\\\$&') : v.replace($.jstree.idregex,'\\\\$&'); }).join(', #')));\n                    this._data.search.res = r;\n                }\n                else {\n                    this._data.search.dom = this._data.search.dom.add($(this.element[0].querySelectorAll('#' + $.map(r, function (v) { return \"0123456789\".indexOf(v[0]) !== -1 ? '\\\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\\\$&') : v.replace($.jstree.idregex,'\\\\$&'); }).join(', #'))));\n                    this._data.search.res = $.vakata.array_unique(this._data.search.res.concat(r));\n                }\n                this._data.search.dom.children(\".jstree-anchor\").addClass('jstree-search');\n            }\n            /**\n             * triggered after search is complete\n             * @event\n             * @name search.jstree\n             * @param {jQuery} nodes a jQuery collection of matching nodes\n             * @param {String} str the search string\n             * @param {Array} res a collection of objects represeing the matching nodes\n             * @plugin search\n             */\n            this.trigger('search', { nodes : this._data.search.dom, str : str, res : this._data.search.res, show_only_matches : show_only_matches });\n        };\n        /**\n         * used to clear the last search (removes classes and shows all nodes if filtering is on)\n         * @name clear_search()\n         * @plugin search\n         * @trigger clear_search.jstree\n         */\n        this.clear_search = function () {\n            if(this.settings.search.close_opened_onclear) {\n                this.close_node(this._data.search.opn, 0);\n            }\n            /**\n             * triggered after search is complete\n             * @event\n             * @name clear_search.jstree\n             * @param {jQuery} nodes a jQuery collection of matching nodes (the result from the last search)\n             * @param {String} str the search string (the last search string)\n             * @param {Array} res a collection of objects represeing the matching nodes (the result from the last search)\n             * @plugin search\n             */\n            this.trigger('clear_search', { 'nodes' : this._data.search.dom, str : this._data.search.str, res : this._data.search.res });\n            if(this._data.search.res.length) {\n                this._data.search.dom = $(this.element[0].querySelectorAll('#' + $.map(this._data.search.res, function (v) {\n                    return \"0123456789\".indexOf(v[0]) !== -1 ? '\\\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\\\$&') : v.replace($.jstree.idregex,'\\\\$&');\n                }).join(', #')));\n                this._data.search.dom.children(\".jstree-anchor\").removeClass(\"jstree-search\");\n            }\n            this._data.search.str = \"\";\n            this._data.search.res = [];\n            this._data.search.opn = [];\n            this._data.search.dom = $();\n        };\n\n        this.redraw_node = function(obj, deep, callback, force_render) {\n            obj = parent.redraw_node.apply(this, arguments);\n            if(obj) {\n                if($.inArray(obj.id, this._data.search.res) !== -1) {\n                    var i, j, tmp = null;\n                    for(i = 0, j = obj.childNodes.length; i < j; i++) {\n                        if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf(\"jstree-anchor\") !== -1) {\n                            tmp = obj.childNodes[i];\n                            break;\n                        }\n                    }\n                    if(tmp) {\n                        tmp.className += ' jstree-search';\n                    }\n                }\n            }\n            return obj;\n        };\n    };\n\n    // helpers\n    (function ($) {\n        // from http://kiro.me/projects/fuse.html\n        $.vakata.search = function(pattern, txt, options) {\n            options = options || {};\n            options = $.extend({}, $.vakata.search.defaults, options);\n            if(options.fuzzy !== false) {\n                options.fuzzy = true;\n            }\n            pattern = options.caseSensitive ? pattern : pattern.toLowerCase();\n            var MATCH_LOCATION\t= options.location,\n                MATCH_DISTANCE\t= options.distance,\n                MATCH_THRESHOLD\t= options.threshold,\n                patternLen = pattern.length,\n                matchmask, pattern_alphabet, match_bitapScore, search;\n            if(patternLen > 32) {\n                options.fuzzy = false;\n            }\n            if(options.fuzzy) {\n                matchmask = 1 << (patternLen - 1);\n                pattern_alphabet = (function () {\n                    var mask = {},\n                        i = 0;\n                    for (i = 0; i < patternLen; i++) {\n                        mask[pattern.charAt(i)] = 0;\n                    }\n                    for (i = 0; i < patternLen; i++) {\n                        mask[pattern.charAt(i)] |= 1 << (patternLen - i - 1);\n                    }\n                    return mask;\n                }());\n                match_bitapScore = function (e, x) {\n                    var accuracy = e / patternLen,\n                        proximity = Math.abs(MATCH_LOCATION - x);\n                    if(!MATCH_DISTANCE) {\n                        return proximity ? 1.0 : accuracy;\n                    }\n                    return accuracy + (proximity / MATCH_DISTANCE);\n                };\n            }\n            search = function (text) {\n                text = options.caseSensitive ? text : text.toLowerCase();\n                if(pattern === text || text.indexOf(pattern) !== -1) {\n                    return {\n                        isMatch: true,\n                        score: 0\n                    };\n                }\n                if(!options.fuzzy) {\n                    return {\n                        isMatch: false,\n                        score: 1\n                    };\n                }\n                var i, j,\n                    textLen = text.length,\n                    scoreThreshold = MATCH_THRESHOLD,\n                    bestLoc = text.indexOf(pattern, MATCH_LOCATION),\n                    binMin, binMid,\n                    binMax = patternLen + textLen,\n                    lastRd, start, finish, rd, charMatch,\n                    score = 1,\n                    locations = [];\n                if (bestLoc !== -1) {\n                    scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold);\n                    bestLoc = text.lastIndexOf(pattern, MATCH_LOCATION + patternLen);\n                    if (bestLoc !== -1) {\n                        scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold);\n                    }\n                }\n                bestLoc = -1;\n                for (i = 0; i < patternLen; i++) {\n                    binMin = 0;\n                    binMid = binMax;\n                    while (binMin < binMid) {\n                        if (match_bitapScore(i, MATCH_LOCATION + binMid) <= scoreThreshold) {\n                            binMin = binMid;\n                        } else {\n                            binMax = binMid;\n                        }\n                        binMid = Math.floor((binMax - binMin) / 2 + binMin);\n                    }\n                    binMax = binMid;\n                    start = Math.max(1, MATCH_LOCATION - binMid + 1);\n                    finish = Math.min(MATCH_LOCATION + binMid, textLen) + patternLen;\n                    rd = new Array(finish + 2);\n                    rd[finish + 1] = (1 << i) - 1;\n                    for (j = finish; j >= start; j--) {\n                        charMatch = pattern_alphabet[text.charAt(j - 1)];\n                        if (i === 0) {\n                            rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;\n                        } else {\n                            rd[j] = ((rd[j + 1] << 1) | 1) & charMatch | (((lastRd[j + 1] | lastRd[j]) << 1) | 1) | lastRd[j + 1];\n                        }\n                        if (rd[j] & matchmask) {\n                            score = match_bitapScore(i, j - 1);\n                            if (score <= scoreThreshold) {\n                                scoreThreshold = score;\n                                bestLoc = j - 1;\n                                locations.push(bestLoc);\n                                if (bestLoc > MATCH_LOCATION) {\n                                    start = Math.max(1, 2 * MATCH_LOCATION - bestLoc);\n                                } else {\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                    if (match_bitapScore(i + 1, MATCH_LOCATION) > scoreThreshold) {\n                        break;\n                    }\n                    lastRd = rd;\n                }\n                return {\n                    isMatch: bestLoc >= 0,\n                    score: score\n                };\n            };\n            return txt === true ? { 'search' : search } : search(txt);\n        };\n        $.vakata.search.defaults = {\n            location : 0,\n            distance : 100,\n            threshold : 0.6,\n            fuzzy : false,\n            caseSensitive : false\n        };\n    }($));\n\n    // include the search plugin by default\n    // $.jstree.defaults.plugins.push(\"search\");\n\n\n    /**\n     * ### Sort plugin\n     *\n     * Automatically sorts all siblings in the tree according to a sorting function.\n     */\n\n    /**\n     * the settings function used to sort the nodes.\n     * It is executed in the tree's context, accepts two nodes as arguments and should return `1` or `-1`.\n     * @name $.jstree.defaults.sort\n     * @plugin sort\n     */\n    $.jstree.defaults.sort = function (a, b) {\n        //return this.get_type(a) === this.get_type(b) ? (this.get_text(a) > this.get_text(b) ? 1 : -1) : this.get_type(a) >= this.get_type(b);\n        return this.get_text(a) > this.get_text(b) ? 1 : -1;\n    };\n    $.jstree.plugins.sort = function (options, parent) {\n        this.bind = function () {\n            parent.bind.call(this);\n            this.element\n                .on(\"model.jstree\", function (e, data) {\n                    this.sort(data.parent, true);\n                }.bind(this))\n                .on(\"rename_node.jstree create_node.jstree\", function (e, data) {\n                    this.sort(data.parent || data.node.parent, false);\n                    this.redraw_node(data.parent || data.node.parent, true);\n                }.bind(this))\n                .on(\"move_node.jstree copy_node.jstree\", function (e, data) {\n                    this.sort(data.parent, false);\n                    this.redraw_node(data.parent, true);\n                }.bind(this));\n        };\n        /**\n         * used to sort a node's children\n         * @private\n         * @name sort(obj [, deep])\n         * @param  {mixed} obj the node\n         * @param {Boolean} deep if set to `true` nodes are sorted recursively.\n         * @plugin sort\n         * @trigger search.jstree\n         */\n        this.sort = function (obj, deep) {\n            var i, j;\n            obj = this.get_node(obj);\n            if(obj && obj.children && obj.children.length) {\n                obj.children.sort(this.settings.sort.bind(this));\n                if(deep) {\n                    for(i = 0, j = obj.children_d.length; i < j; i++) {\n                        this.sort(obj.children_d[i], false);\n                    }\n                }\n            }\n        };\n    };\n\n    // include the sort plugin by default\n    // $.jstree.defaults.plugins.push(\"sort\");\n\n    /**\n     * ### State plugin\n     *\n     * Saves the state of the tree (selected nodes, opened nodes) on the user's computer using available options (localStorage, cookies, etc)\n     */\n\n    var to = false;\n    /**\n     * stores all defaults for the state plugin\n     * @name $.jstree.defaults.state\n     * @plugin state\n     */\n    $.jstree.defaults.state = {\n        /**\n         * A string for the key to use when saving the current tree (change if using multiple trees in your project). Defaults to `jstree`.\n         * @name $.jstree.defaults.state.key\n         * @plugin state\n         */\n        key\t\t: 'jstree',\n        /**\n         * A space separated list of events that trigger a state save. Defaults to `changed.jstree open_node.jstree close_node.jstree`.\n         * @name $.jstree.defaults.state.events\n         * @plugin state\n         */\n        events\t: 'changed.jstree open_node.jstree close_node.jstree check_node.jstree uncheck_node.jstree',\n        /**\n         * Time in milliseconds after which the state will expire. Defaults to 'false' meaning - no expire.\n         * @name $.jstree.defaults.state.ttl\n         * @plugin state\n         */\n        ttl\t\t: false,\n        /**\n         * A function that will be executed prior to restoring state with one argument - the state object. Can be used to clear unwanted parts of the state.\n         * @name $.jstree.defaults.state.filter\n         * @plugin state\n         */\n        filter\t: false,\n        /**\n         * Should loaded nodes be restored (setting this to true means that it is possible that the whole tree will be loaded for some users - use with caution). Defaults to `false`\n         * @name $.jstree.defaults.state.preserve_loaded\n         * @plugin state\n         */\n        preserve_loaded : false\n    };\n    $.jstree.plugins.state = function (options, parent) {\n        this.bind = function () {\n            parent.bind.call(this);\n            var bind = function () {\n                this.element.on(this.settings.state.events, function () {\n                    if(to) { clearTimeout(to); }\n                    to = setTimeout(function () { this.save_state(); }.bind(this), 100);\n                }.bind(this));\n                /**\n                 * triggered when the state plugin is finished restoring the state (and immediately after ready if there is no state to restore).\n                 * @event\n                 * @name state_ready.jstree\n                 * @plugin state\n                 */\n                this.trigger('state_ready');\n            }.bind(this);\n            this.element\n                .on(\"ready.jstree\", function (e, data) {\n                    this.element.one(\"restore_state.jstree\", bind);\n                    if(!this.restore_state()) { bind(); }\n                }.bind(this));\n        };\n        /**\n         * save the state\n         * @name save_state()\n         * @plugin state\n         */\n        this.save_state = function () {\n            var tm = this.get_state();\n            if (!this.settings.state.preserve_loaded) {\n                delete tm.core.loaded;\n            }\n            var st = { 'state' : tm, 'ttl' : this.settings.state.ttl, 'sec' : +(new Date()) };\n            $.vakata.storage.set(this.settings.state.key, JSON.stringify(st));\n        };\n        /**\n         * restore the state from the user's computer\n         * @name restore_state()\n         * @plugin state\n         */\n        this.restore_state = function () {\n            var k = $.vakata.storage.get(this.settings.state.key);\n            if(!!k) { try { k = JSON.parse(k); } catch(ex) { return false; } }\n            if(!!k && k.ttl && k.sec && +(new Date()) - k.sec > k.ttl) { return false; }\n            if(!!k && k.state) { k = k.state; }\n            if(!!k && $.vakata.is_function(this.settings.state.filter)) { k = this.settings.state.filter.call(this, k); }\n            if(!!k) {\n                if (!this.settings.state.preserve_loaded) {\n                    delete k.core.loaded;\n                }\n                this.element.one(\"set_state.jstree\", function (e, data) { data.instance.trigger('restore_state', { 'state' : $.extend(true, {}, k) }); });\n                this.set_state(k);\n                return true;\n            }\n            return false;\n        };\n        /**\n         * clear the state on the user's computer\n         * @name clear_state()\n         * @plugin state\n         */\n        this.clear_state = function () {\n            return $.vakata.storage.del(this.settings.state.key);\n        };\n    };\n\n    (function ($, undefined) {\n        $.vakata.storage = {\n            // simply specifying the functions in FF throws an error\n            set : function (key, val) { return window.localStorage.setItem(key, val); },\n            get : function (key) { return window.localStorage.getItem(key); },\n            del : function (key) { return window.localStorage.removeItem(key); }\n        };\n    }($));\n\n    // include the state plugin by default\n    // $.jstree.defaults.plugins.push(\"state\");\n\n    /**\n     * ### Types plugin\n     *\n     * Makes it possible to add predefined types for groups of nodes, which make it possible to easily control nesting rules and icon for each group.\n     */\n\n    /**\n     * An object storing all types as key value pairs, where the key is the type name and the value is an object that could contain following keys (all optional).\n     *\n     * * `max_children` the maximum number of immediate children this node type can have. Do not specify or set to `-1` for unlimited.\n     * * `max_depth` the maximum number of nesting this node type can have. A value of `1` would mean that the node can have children, but no grandchildren. Do not specify or set to `-1` for unlimited.\n     * * `valid_children` an array of node type strings, that nodes of this type can have as children. Do not specify or set to `-1` for no limits.\n     * * `icon` a string - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class. Omit to use the default icon from your theme.\n     * * `li_attr` an object of values which will be used to add HTML attributes on the resulting LI DOM node (merged with the node's own data)\n     * * `a_attr` an object of values which will be used to add HTML attributes on the resulting A DOM node (merged with the node's own data)\n     *\n     * There are two predefined types:\n     *\n     * * `#` represents the root of the tree, for example `max_children` would control the maximum number of root nodes.\n     * * `default` represents the default node - any settings here will be applied to all nodes that do not have a type specified.\n     *\n     * @name $.jstree.defaults.types\n     * @plugin types\n     */\n    $.jstree.defaults.types = {\n        'default' : {}\n    };\n    $.jstree.defaults.types[$.jstree.root] = {};\n\n    $.jstree.plugins.types = function (options, parent) {\n        this.init = function (el, options) {\n            var i, j;\n            if(options && options.types && options.types['default']) {\n                for(i in options.types) {\n                    if(i !== \"default\" && i !== $.jstree.root && options.types.hasOwnProperty(i)) {\n                        for(j in options.types['default']) {\n                            if(options.types['default'].hasOwnProperty(j) && options.types[i][j] === undefined) {\n                                options.types[i][j] = options.types['default'][j];\n                            }\n                        }\n                    }\n                }\n            }\n            parent.init.call(this, el, options);\n            this._model.data[$.jstree.root].type = $.jstree.root;\n        };\n        this.refresh = function (skip_loading, forget_state) {\n            parent.refresh.call(this, skip_loading, forget_state);\n            this._model.data[$.jstree.root].type = $.jstree.root;\n        };\n        this.bind = function () {\n            this.element\n                .on('model.jstree', function (e, data) {\n                    var m = this._model.data,\n                        dpc = data.nodes,\n                        t = this.settings.types,\n                        i, j, c = 'default', k;\n                    for(i = 0, j = dpc.length; i < j; i++) {\n                        c = 'default';\n                        if(m[dpc[i]].original && m[dpc[i]].original.type && t[m[dpc[i]].original.type]) {\n                            c = m[dpc[i]].original.type;\n                        }\n                        if(m[dpc[i]].data && m[dpc[i]].data.jstree && m[dpc[i]].data.jstree.type && t[m[dpc[i]].data.jstree.type]) {\n                            c = m[dpc[i]].data.jstree.type;\n                        }\n                        m[dpc[i]].type = c;\n                        if(m[dpc[i]].icon === true && t[c].icon !== undefined) {\n                            m[dpc[i]].icon = t[c].icon;\n                        }\n                        if(t[c].li_attr !== undefined && typeof t[c].li_attr === 'object') {\n                            for (k in t[c].li_attr) {\n                                if (t[c].li_attr.hasOwnProperty(k)) {\n                                    if (k === 'id') {\n                                        continue;\n                                    }\n                                    else if (m[dpc[i]].li_attr[k] === undefined) {\n                                        m[dpc[i]].li_attr[k] = t[c].li_attr[k];\n                                    }\n                                    else if (k === 'class') {\n                                        m[dpc[i]].li_attr['class'] = t[c].li_attr['class'] + ' ' + m[dpc[i]].li_attr['class'];\n                                    }\n                                }\n                            }\n                        }\n                        if(t[c].a_attr !== undefined && typeof t[c].a_attr === 'object') {\n                            for (k in t[c].a_attr) {\n                                if (t[c].a_attr.hasOwnProperty(k)) {\n                                    if (k === 'id') {\n                                        continue;\n                                    }\n                                    else if (m[dpc[i]].a_attr[k] === undefined) {\n                                        m[dpc[i]].a_attr[k] = t[c].a_attr[k];\n                                    }\n                                    else if (k === 'href' && m[dpc[i]].a_attr[k] === '#') {\n                                        m[dpc[i]].a_attr['href'] = t[c].a_attr['href'];\n                                    }\n                                    else if (k === 'class') {\n                                        m[dpc[i]].a_attr['class'] = t[c].a_attr['class'] + ' ' + m[dpc[i]].a_attr['class'];\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    m[$.jstree.root].type = $.jstree.root;\n                }.bind(this));\n            parent.bind.call(this);\n        };\n        this.get_json = function (obj, options, flat) {\n            var i, j,\n                m = this._model.data,\n                opt = options ? $.extend(true, {}, options, {no_id:false}) : {},\n                tmp = parent.get_json.call(this, obj, opt, flat);\n            if(tmp === false) { return false; }\n            if($.vakata.is_array(tmp)) {\n                for(i = 0, j = tmp.length; i < j; i++) {\n                    tmp[i].type = tmp[i].id && m[tmp[i].id] && m[tmp[i].id].type ? m[tmp[i].id].type : \"default\";\n                    if(options && options.no_id) {\n                        delete tmp[i].id;\n                        if(tmp[i].li_attr && tmp[i].li_attr.id) {\n                            delete tmp[i].li_attr.id;\n                        }\n                        if(tmp[i].a_attr && tmp[i].a_attr.id) {\n                            delete tmp[i].a_attr.id;\n                        }\n                    }\n                }\n            }\n            else {\n                tmp.type = tmp.id && m[tmp.id] && m[tmp.id].type ? m[tmp.id].type : \"default\";\n                if(options && options.no_id) {\n                    tmp = this._delete_ids(tmp);\n                }\n            }\n            return tmp;\n        };\n        this._delete_ids = function (tmp) {\n            if($.vakata.is_array(tmp)) {\n                for(var i = 0, j = tmp.length; i < j; i++) {\n                    tmp[i] = this._delete_ids(tmp[i]);\n                }\n                return tmp;\n            }\n            delete tmp.id;\n            if(tmp.li_attr && tmp.li_attr.id) {\n                delete tmp.li_attr.id;\n            }\n            if(tmp.a_attr && tmp.a_attr.id) {\n                delete tmp.a_attr.id;\n            }\n            if(tmp.children && $.vakata.is_array(tmp.children)) {\n                tmp.children = this._delete_ids(tmp.children);\n            }\n            return tmp;\n        };\n        this.check = function (chk, obj, par, pos, more) {\n            if(parent.check.call(this, chk, obj, par, pos, more) === false) { return false; }\n            obj = obj && obj.id ? obj : this.get_node(obj);\n            par = par && par.id ? par : this.get_node(par);\n            var m = obj && obj.id ? (more && more.origin ? more.origin : $.jstree.reference(obj.id)) : null, tmp, d, i, j;\n            m = m && m._model && m._model.data ? m._model.data : null;\n            switch(chk) {\n                case \"create_node\":\n                case \"move_node\":\n                case \"copy_node\":\n                    if(chk !== 'move_node' || $.inArray(obj.id, par.children) === -1) {\n                        tmp = this.get_rules(par);\n                        if(tmp.max_children !== undefined && tmp.max_children !== -1 && tmp.max_children === par.children.length) {\n                            this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_01', 'reason' : 'max_children prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                            return false;\n                        }\n                        if(tmp.valid_children !== undefined && tmp.valid_children !== -1 && $.inArray((obj.type || 'default'), tmp.valid_children) === -1) {\n                            this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_02', 'reason' : 'valid_children prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                            return false;\n                        }\n                        if(m && obj.children_d && obj.parents) {\n                            d = 0;\n                            for(i = 0, j = obj.children_d.length; i < j; i++) {\n                                d = Math.max(d, m[obj.children_d[i]].parents.length);\n                            }\n                            d = d - obj.parents.length + 1;\n                        }\n                        if(d <= 0 || d === undefined) { d = 1; }\n                        do {\n                            if(tmp.max_depth !== undefined && tmp.max_depth !== -1 && tmp.max_depth < d) {\n                                this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_03', 'reason' : 'max_depth prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                                return false;\n                            }\n                            par = this.get_node(par.parent);\n                            tmp = this.get_rules(par);\n                            d++;\n                        } while(par);\n                    }\n                    break;\n            }\n            return true;\n        };\n        /**\n         * used to retrieve the type settings object for a node\n         * @name get_rules(obj)\n         * @param {mixed} obj the node to find the rules for\n         * @return {Object}\n         * @plugin types\n         */\n        this.get_rules = function (obj) {\n            obj = this.get_node(obj);\n            if(!obj) { return false; }\n            var tmp = this.get_type(obj, true);\n            if(tmp.max_depth === undefined) { tmp.max_depth = -1; }\n            if(tmp.max_children === undefined) { tmp.max_children = -1; }\n            if(tmp.valid_children === undefined) { tmp.valid_children = -1; }\n            return tmp;\n        };\n        /**\n         * used to retrieve the type string or settings object for a node\n         * @name get_type(obj [, rules])\n         * @param {mixed} obj the node to find the rules for\n         * @param {Boolean} rules if set to `true` instead of a string the settings object will be returned\n         * @return {String|Object}\n         * @plugin types\n         */\n        this.get_type = function (obj, rules) {\n            obj = this.get_node(obj);\n            return (!obj) ? false : ( rules ? $.extend({ 'type' : obj.type }, this.settings.types[obj.type]) : obj.type);\n        };\n        /**\n         * used to change a node's type\n         * @name set_type(obj, type)\n         * @param {mixed} obj the node to change\n         * @param {String} type the new type\n         * @plugin types\n         */\n        this.set_type = function (obj, type) {\n            var m = this._model.data, t, t1, t2, old_type, old_icon, k, d, a;\n            if($.vakata.is_array(obj)) {\n                obj = obj.slice();\n                for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {\n                    this.set_type(obj[t1], type);\n                }\n                return true;\n            }\n            t = this.settings.types;\n            obj = this.get_node(obj);\n            if(!t[type] || !obj) { return false; }\n            d = this.get_node(obj, true);\n            if (d && d.length) {\n                a = d.children('.jstree-anchor');\n            }\n            old_type = obj.type;\n            old_icon = this.get_icon(obj);\n            obj.type = type;\n            if(old_icon === true || !t[old_type] || (t[old_type].icon !== undefined && old_icon === t[old_type].icon)) {\n                this.set_icon(obj, t[type].icon !== undefined ? t[type].icon : true);\n            }\n\n            // remove old type props\n            if(t[old_type] && t[old_type].li_attr !== undefined && typeof t[old_type].li_attr === 'object') {\n                for (k in t[old_type].li_attr) {\n                    if (t[old_type].li_attr.hasOwnProperty(k)) {\n                        if (k === 'id') {\n                            continue;\n                        }\n                        else if (k === 'class') {\n                            m[obj.id].li_attr['class'] = (m[obj.id].li_attr['class'] || '').replace(t[old_type].li_attr[k], '');\n                            if (d) { d.removeClass(t[old_type].li_attr[k]); }\n                        }\n                        else if (m[obj.id].li_attr[k] === t[old_type].li_attr[k]) {\n                            m[obj.id].li_attr[k] = null;\n                            if (d) { d.removeAttr(k); }\n                        }\n                    }\n                }\n            }\n            if(t[old_type] && t[old_type].a_attr !== undefined && typeof t[old_type].a_attr === 'object') {\n                for (k in t[old_type].a_attr) {\n                    if (t[old_type].a_attr.hasOwnProperty(k)) {\n                        if (k === 'id') {\n                            continue;\n                        }\n                        else if (k === 'class') {\n                            m[obj.id].a_attr['class'] = (m[obj.id].a_attr['class'] || '').replace(t[old_type].a_attr[k], '');\n                            if (a) { a.removeClass(t[old_type].a_attr[k]); }\n                        }\n                        else if (m[obj.id].a_attr[k] === t[old_type].a_attr[k]) {\n                            if (k === 'href') {\n                                m[obj.id].a_attr[k] = '#';\n                                if (a) { a.attr('href', '#'); }\n                            }\n                            else {\n                                delete m[obj.id].a_attr[k];\n                                if (a) { a.removeAttr(k); }\n                            }\n                        }\n                    }\n                }\n            }\n\n            // add new props\n            if(t[type].li_attr !== undefined && typeof t[type].li_attr === 'object') {\n                for (k in t[type].li_attr) {\n                    if (t[type].li_attr.hasOwnProperty(k)) {\n                        if (k === 'id') {\n                            continue;\n                        }\n                        else if (m[obj.id].li_attr[k] === undefined) {\n                            m[obj.id].li_attr[k] = t[type].li_attr[k];\n                            if (d) {\n                                if (k === 'class') {\n                                    d.addClass(t[type].li_attr[k]);\n                                }\n                                else {\n                                    d.attr(k, t[type].li_attr[k]);\n                                }\n                            }\n                        }\n                        else if (k === 'class') {\n                            m[obj.id].li_attr['class'] = t[type].li_attr[k] + ' ' + m[obj.id].li_attr['class'];\n                            if (d) { d.addClass(t[type].li_attr[k]); }\n                        }\n                    }\n                }\n            }\n            if(t[type].a_attr !== undefined && typeof t[type].a_attr === 'object') {\n                for (k in t[type].a_attr) {\n                    if (t[type].a_attr.hasOwnProperty(k)) {\n                        if (k === 'id') {\n                            continue;\n                        }\n                        else if (m[obj.id].a_attr[k] === undefined) {\n                            m[obj.id].a_attr[k] = t[type].a_attr[k];\n                            if (a) {\n                                if (k === 'class') {\n                                    a.addClass(t[type].a_attr[k]);\n                                }\n                                else {\n                                    a.attr(k, t[type].a_attr[k]);\n                                }\n                            }\n                        }\n                        else if (k === 'href' && m[obj.id].a_attr[k] === '#') {\n                            m[obj.id].a_attr['href'] = t[type].a_attr['href'];\n                            if (a) { a.attr('href', t[type].a_attr['href']); }\n                        }\n                        else if (k === 'class') {\n                            m[obj.id].a_attr['class'] = t[type].a_attr['class'] + ' ' + m[obj.id].a_attr['class'];\n                            if (a) { a.addClass(t[type].a_attr[k]); }\n                        }\n                    }\n                }\n            }\n\n            return true;\n        };\n    };\n    // include the types plugin by default\n    // $.jstree.defaults.plugins.push(\"types\");\n\n\n    /**\n     * ### Unique plugin\n     *\n     * Enforces that no nodes with the same name can coexist as siblings.\n     */\n\n    /**\n     * stores all defaults for the unique plugin\n     * @name $.jstree.defaults.unique\n     * @plugin unique\n     */\n    $.jstree.defaults.unique = {\n        /**\n         * Indicates if the comparison should be case sensitive. Default is `false`.\n         * @name $.jstree.defaults.unique.case_sensitive\n         * @plugin unique\n         */\n        case_sensitive : false,\n        /**\n         * Indicates if white space should be trimmed before the comparison. Default is `false`.\n         * @name $.jstree.defaults.unique.trim_whitespace\n         * @plugin unique\n         */\n        trim_whitespace : false,\n        /**\n         * A callback executed in the instance's scope when a new node is created and the name is already taken, the two arguments are the conflicting name and the counter. The default will produce results like `New node (2)`.\n         * @name $.jstree.defaults.unique.duplicate\n         * @plugin unique\n         */\n        duplicate : function (name, counter) {\n            return name + ' (' + counter + ')';\n        }\n    };\n\n    $.jstree.plugins.unique = function (options, parent) {\n        this.check = function (chk, obj, par, pos, more) {\n            if(parent.check.call(this, chk, obj, par, pos, more) === false) { return false; }\n            obj = obj && obj.id ? obj : this.get_node(obj);\n            par = par && par.id ? par : this.get_node(par);\n            if(!par || !par.children) { return true; }\n            var n = chk === \"rename_node\" ? pos : obj.text,\n                c = [],\n                s = this.settings.unique.case_sensitive,\n                w = this.settings.unique.trim_whitespace,\n                m = this._model.data, i, j, t;\n            for(i = 0, j = par.children.length; i < j; i++) {\n                t = m[par.children[i]].text;\n                if (!s) {\n                    t = t.toLowerCase();\n                }\n                if (w) {\n                    t = t.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n                }\n                c.push(t);\n            }\n            if(!s) { n = n.toLowerCase(); }\n            if (w) { n = n.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, ''); }\n            switch(chk) {\n                case \"delete_node\":\n                    return true;\n                case \"rename_node\":\n                    t = obj.text || '';\n                    if (!s) {\n                        t = t.toLowerCase();\n                    }\n                    if (w) {\n                        t = t.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n                    }\n                    i = ($.inArray(n, c) === -1 || (obj.text && t === n));\n                    if(!i) {\n                        this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_01', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                    }\n                    return i;\n                case \"create_node\":\n                    i = ($.inArray(n, c) === -1);\n                    if(!i) {\n                        this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_04', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                    }\n                    return i;\n                case \"copy_node\":\n                    i = ($.inArray(n, c) === -1);\n                    if(!i) {\n                        this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_02', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                    }\n                    return i;\n                case \"move_node\":\n                    i = ( (obj.parent === par.id && (!more || !more.is_multi)) || $.inArray(n, c) === -1);\n                    if(!i) {\n                        this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_03', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };\n                    }\n                    return i;\n            }\n            return true;\n        };\n        this.create_node = function (par, node, pos, callback, is_loaded) {\n            if(!node || node.text === undefined) {\n                if(par === null) {\n                    par = $.jstree.root;\n                }\n                par = this.get_node(par);\n                if(!par) {\n                    return parent.create_node.call(this, par, node, pos, callback, is_loaded);\n                }\n                pos = pos === undefined ? \"last\" : pos;\n                if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {\n                    return parent.create_node.call(this, par, node, pos, callback, is_loaded);\n                }\n                if(!node) { node = {}; }\n                var tmp, n, dpc, i, j, m = this._model.data, s = this.settings.unique.case_sensitive, w = this.settings.unique.trim_whitespace, cb = this.settings.unique.duplicate, t;\n                n = tmp = this.get_string('New node');\n                dpc = [];\n                for(i = 0, j = par.children.length; i < j; i++) {\n                    t = m[par.children[i]].text;\n                    if (!s) {\n                        t = t.toLowerCase();\n                    }\n                    if (w) {\n                        t = t.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n                    }\n                    dpc.push(t);\n                }\n                i = 1;\n                t = n;\n                if (!s) {\n                    t = t.toLowerCase();\n                }\n                if (w) {\n                    t = t.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n                }\n                while($.inArray(t, dpc) !== -1) {\n                    n = cb.call(this, tmp, (++i)).toString();\n                    t = n;\n                    if (!s) {\n                        t = t.toLowerCase();\n                    }\n                    if (w) {\n                        t = t.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n                    }\n                }\n                node.text = n;\n            }\n            return parent.create_node.call(this, par, node, pos, callback, is_loaded);\n        };\n    };\n\n    // include the unique plugin by default\n    // $.jstree.defaults.plugins.push(\"unique\");\n\n\n    /**\n     * ### Wholerow plugin\n     *\n     * Makes each node appear block level. Making selection easier. May cause slow down for large trees in old browsers.\n     */\n\n    var div = document.createElement('DIV');\n    div.setAttribute('unselectable','on');\n    div.setAttribute('role','presentation');\n    div.className = 'jstree-wholerow';\n    div.innerHTML = '&#160;';\n    $.jstree.plugins.wholerow = function (options, parent) {\n        this.bind = function () {\n            parent.bind.call(this);\n\n            this.element\n                .on('ready.jstree set_state.jstree', function () {\n                    this.hide_dots();\n                }.bind(this))\n                .on(\"init.jstree loading.jstree ready.jstree\", function () {\n                    //div.style.height = this._data.core.li_height + 'px';\n                    this.get_container_ul().addClass('jstree-wholerow-ul');\n                }.bind(this))\n                .on(\"deselect_all.jstree\", function (e, data) {\n                    this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked');\n                }.bind(this))\n                .on(\"changed.jstree\", function (e, data) {\n                    this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked');\n                    var tmp = false, i, j;\n                    for(i = 0, j = data.selected.length; i < j; i++) {\n                        tmp = this.get_node(data.selected[i], true);\n                        if(tmp && tmp.length) {\n                            tmp.children('.jstree-wholerow').addClass('jstree-wholerow-clicked');\n                        }\n                    }\n                }.bind(this))\n                .on(\"open_node.jstree\", function (e, data) {\n                    this.get_node(data.node, true).find('.jstree-clicked').parent().children('.jstree-wholerow').addClass('jstree-wholerow-clicked');\n                }.bind(this))\n                .on(\"hover_node.jstree dehover_node.jstree\", function (e, data) {\n                    if(e.type === \"hover_node\" && this.is_disabled(data.node)) { return; }\n                    this.get_node(data.node, true).children('.jstree-wholerow')[e.type === \"hover_node\"?\"addClass\":\"removeClass\"]('jstree-wholerow-hovered');\n                }.bind(this))\n                .on(\"contextmenu.jstree\", \".jstree-wholerow\", function (e) {\n                    if (this._data.contextmenu) {\n                        e.preventDefault();\n                        var tmp = $.Event('contextmenu', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey, pageX : e.pageX, pageY : e.pageY });\n                        $(e.currentTarget).closest(\".jstree-node\").children(\".jstree-anchor\").first().trigger(tmp);\n                    }\n                }.bind(this))\n                /*!\n\t\t\t\t.on(\"mousedown.jstree touchstart.jstree\", \".jstree-wholerow\", function (e) {\n\t\t\t\t\t\tif(e.target === e.currentTarget) {\n\t\t\t\t\t\t\tvar a = $(e.currentTarget).closest(\".jstree-node\").children(\".jstree-anchor\");\n\t\t\t\t\t\t\te.target = a[0];\n\t\t\t\t\t\t\ta.trigger(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t*/\n                .on(\"click.jstree\", \".jstree-wholerow\", function (e) {\n                    e.stopImmediatePropagation();\n                    var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });\n                    $(e.currentTarget).closest(\".jstree-node\").children(\".jstree-anchor\").first().trigger(tmp).trigger('focus');\n                })\n                .on(\"dblclick.jstree\", \".jstree-wholerow\", function (e) {\n                    e.stopImmediatePropagation();\n                    var tmp = $.Event('dblclick', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });\n                    $(e.currentTarget).closest(\".jstree-node\").children(\".jstree-anchor\").first().trigger(tmp).trigger('focus');\n                })\n                .on(\"click.jstree\", \".jstree-leaf > .jstree-ocl\", function (e) {\n                    e.stopImmediatePropagation();\n                    var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });\n                    $(e.currentTarget).closest(\".jstree-node\").children(\".jstree-anchor\").first().trigger(tmp).trigger('focus');\n                }.bind(this))\n                .on(\"mouseover.jstree\", \".jstree-wholerow, .jstree-icon\", function (e) {\n                    e.stopImmediatePropagation();\n                    if(!this.is_disabled(e.currentTarget)) {\n                        this.hover_node(e.currentTarget);\n                    }\n                    return false;\n                }.bind(this))\n                .on(\"mouseleave.jstree\", \".jstree-node\", function (e) {\n                    this.dehover_node(e.currentTarget);\n                }.bind(this));\n        };\n        this.teardown = function () {\n            if(this.settings.wholerow) {\n                this.element.find(\".jstree-wholerow\").remove();\n            }\n            parent.teardown.call(this);\n        };\n        this.redraw_node = function(obj, deep, callback, force_render) {\n            obj = parent.redraw_node.apply(this, arguments);\n            if(obj) {\n                var tmp = div.cloneNode(true);\n                //tmp.style.height = this._data.core.li_height + 'px';\n                if($.inArray(obj.id, this._data.core.selected) !== -1) { tmp.className += ' jstree-wholerow-clicked'; }\n                if(this._data.core.focused && this._data.core.focused === obj.id) { tmp.className += ' jstree-wholerow-hovered'; }\n                obj.insertBefore(tmp, obj.childNodes[0]);\n            }\n            return obj;\n        };\n    };\n    // include the wholerow plugin by default\n    // $.jstree.defaults.plugins.push(\"wholerow\");\n    if(window.customElements && Object && Object.create) {\n        var proto = Object.create(HTMLElement.prototype);\n        proto.createdCallback = function () {\n            var c = { core : {}, plugins : [] }, i;\n            for(i in $.jstree.plugins) {\n                if($.jstree.plugins.hasOwnProperty(i) && this.attributes[i]) {\n                    c.plugins.push(i);\n                    if(this.getAttribute(i) && JSON.parse(this.getAttribute(i))) {\n                        c[i] = JSON.parse(this.getAttribute(i));\n                    }\n                }\n            }\n            for(i in $.jstree.defaults.core) {\n                if($.jstree.defaults.core.hasOwnProperty(i) && this.attributes[i]) {\n                    c.core[i] = JSON.parse(this.getAttribute(i)) || this.getAttribute(i);\n                }\n            }\n            $(this).jstree(c);\n        };\n        // proto.attributeChangedCallback = function (name, previous, value) { };\n        try {\n            window.customElements.define(\"vakata-jstree\", function() {}, { prototype: proto });\n        } catch (ignore) { }\n    }\n\n}));\n","jquery/spectrum/spectrum.js":"// Spectrum Colorpicker v1.8.1\n// https://github.com/bgrins/spectrum\n// Author: Brian Grinstead\n// License: MIT\n\n(function (factory) {\n    \"use strict\";\n\n    if (typeof define === 'function' && define.amd) { // AMD\n        define(['jquery'], factory);\n    }\n    else if (typeof exports == \"object\" && typeof module == \"object\") { // CommonJS\n        module.exports = factory(require('jquery'));\n    }\n    else { // Browser\n        factory(jQuery);\n    }\n})(function($, undefined) {\n    \"use strict\";\n\n    var defaultOpts = {\n\n            // Callbacks\n            beforeShow: noop,\n            move: noop,\n            change: noop,\n            show: noop,\n            hide: noop,\n\n            // Options\n            color: false,\n            flat: false,\n            showInput: false,\n            allowEmpty: false,\n            showButtons: true,\n            clickoutFiresChange: true,\n            showInitial: false,\n            showPalette: false,\n            showPaletteOnly: false,\n            hideAfterPaletteSelect: false,\n            togglePaletteOnly: false,\n            showSelectionPalette: true,\n            localStorageKey: false,\n            appendTo: \"body\",\n            maxSelectionSize: 7,\n            cancelText: \"cancel\",\n            chooseText: \"choose\",\n            togglePaletteMoreText: \"more\",\n            togglePaletteLessText: \"less\",\n            clearText: \"Clear Color Selection\",\n            noColorSelectedText: \"No Color Selected\",\n            preferredFormat: false,\n            className: \"\", // Deprecated - use containerClassName and replacerClassName instead.\n            containerClassName: \"\",\n            replacerClassName: \"\",\n            showAlpha: false,\n            theme: \"sp-light\",\n            palette: [[\"#ffffff\", \"#000000\", \"#ff0000\", \"#ff8000\", \"#ffff00\", \"#008000\", \"#0000ff\", \"#4b0082\", \"#9400d3\"]],\n            selectionPalette: [],\n            disabled: false,\n            offset: null\n        },\n        spectrums = [],\n        IE = !!/msie/i.exec( window.navigator.userAgent ),\n        rgbaSupport = (function() {\n            function contains( str, substr ) {\n                return !!~('' + str).indexOf(substr);\n            }\n\n            var elem = document.createElement('div');\n            var style = elem.style;\n            style.cssText = 'background-color:rgba(0,0,0,.5)';\n            return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');\n        })(),\n        replaceInput = [\n            \"<div class='sp-replacer'>\",\n            \"<div class='sp-preview'><div class='sp-preview-inner'></div></div>\",\n            \"<div class='sp-dd'>&#9660;</div>\",\n            \"</div>\"\n        ].join(''),\n        markup = (function () {\n\n            // IE does not support gradients with multiple stops, so we need to simulate\n            //  that for the rainbow slider with 8 divs that each have a single gradient\n            var gradientFix = \"\";\n            if (IE) {\n                for (var i = 1; i <= 6; i++) {\n                    gradientFix += \"<div class='sp-\" + i + \"'></div>\";\n                }\n            }\n\n            return [\n                \"<div class='sp-container sp-hidden'>\",\n                \"<div class='sp-palette-container'>\",\n                \"<div class='sp-palette sp-thumb sp-cf'></div>\",\n                \"<div class='sp-palette-button-container sp-cf'>\",\n                \"<button type='button' class='sp-palette-toggle'></button>\",\n                \"</div>\",\n                \"</div>\",\n                \"<div class='sp-picker-container'>\",\n                \"<div class='sp-top sp-cf'>\",\n                \"<div class='sp-fill'></div>\",\n                \"<div class='sp-top-inner'>\",\n                \"<div class='sp-color'>\",\n                \"<div class='sp-sat'>\",\n                \"<div class='sp-val'>\",\n                \"<div class='sp-dragger'></div>\",\n                \"</div>\",\n                \"</div>\",\n                \"</div>\",\n                \"<div class='sp-clear sp-clear-display'>\",\n                \"</div>\",\n                \"<div class='sp-hue'>\",\n                \"<div class='sp-slider'></div>\",\n                gradientFix,\n                \"</div>\",\n                \"</div>\",\n                \"<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle'></div></div></div>\",\n                \"</div>\",\n                \"<div class='sp-input-container sp-cf'>\",\n                \"<input class='sp-input' type='text' spellcheck='false'  />\",\n                \"</div>\",\n                \"<div class='sp-initial sp-thumb sp-cf'></div>\",\n                \"<div class='sp-button-container sp-cf'>\",\n                \"<a class='sp-cancel' href='#'></a>\",\n                \"<button type='button' class='sp-choose'></button>\",\n                \"</div>\",\n                \"</div>\",\n                \"</div>\"\n            ].join(\"\");\n        })();\n\n    function paletteTemplate (p, color, className, opts) {\n        var html = [];\n        for (var i = 0; i < p.length; i++) {\n            var current = p[i];\n            if(current) {\n                var tiny = tinycolor(current);\n                var c = tiny.toHsl().l < 0.5 ? \"sp-thumb-el sp-thumb-dark\" : \"sp-thumb-el sp-thumb-light\";\n                c += (tinycolor.equals(color, current)) ? \" sp-thumb-active\" : \"\";\n                var formattedString = tiny.toString(opts.preferredFormat || \"rgb\");\n                var swatchStyle = rgbaSupport ? (\"background-color:\" + tiny.toRgbString()) : \"filter:\" + tiny.toFilter();\n                html.push('<span title=\"' + formattedString + '\" data-color=\"' + tiny.toRgbString() + '\" class=\"' + c + '\"><span class=\"sp-thumb-inner\" style=\"' + swatchStyle + ';\"></span></span>');\n            } else {\n                var cls = 'sp-clear-display';\n                html.push($('<div />')\n                    .append($('<span data-color=\"\" style=\"background-color:transparent;\" class=\"' + cls + '\"></span>')\n                        .attr('title', opts.noColorSelectedText)\n                    )\n                    .html()\n                );\n            }\n        }\n        return \"<div class='sp-cf \" + className + \"'>\" + html.join('') + \"</div>\";\n    }\n\n    function hideAll() {\n        for (var i = 0; i < spectrums.length; i++) {\n            if (spectrums[i]) {\n                spectrums[i].hide();\n            }\n        }\n    }\n\n    function instanceOptions(o, callbackContext) {\n        var opts = $.extend({}, defaultOpts, o);\n        opts.callbacks = {\n            'move': bind(opts.move, callbackContext),\n            'change': bind(opts.change, callbackContext),\n            'show': bind(opts.show, callbackContext),\n            'hide': bind(opts.hide, callbackContext),\n            'beforeShow': bind(opts.beforeShow, callbackContext)\n        };\n\n        return opts;\n    }\n\n    function spectrum(element, o) {\n\n        var opts = instanceOptions(o, element),\n            flat = opts.flat,\n            showSelectionPalette = opts.showSelectionPalette,\n            localStorageKey = opts.localStorageKey,\n            theme = opts.theme,\n            callbacks = opts.callbacks,\n            resize = throttle(reflow, 10),\n            visible = false,\n            isDragging = false,\n            dragWidth = 0,\n            dragHeight = 0,\n            dragHelperHeight = 0,\n            slideHeight = 0,\n            slideWidth = 0,\n            alphaWidth = 0,\n            alphaSlideHelperWidth = 0,\n            slideHelperHeight = 0,\n            currentHue = 0,\n            currentSaturation = 0,\n            currentValue = 0,\n            currentAlpha = 1,\n            palette = [],\n            paletteArray = [],\n            paletteLookup = {},\n            selectionPalette = opts.selectionPalette.slice(0),\n            maxSelectionSize = opts.maxSelectionSize,\n            draggingClass = \"sp-dragging\",\n            shiftMovementDirection = null;\n\n        var doc = element.ownerDocument,\n            body = doc.body,\n            boundElement = $(element),\n            disabled = false,\n            container = $(markup, doc).addClass(theme),\n            pickerContainer = container.find(\".sp-picker-container\"),\n            dragger = container.find(\".sp-color\"),\n            dragHelper = container.find(\".sp-dragger\"),\n            slider = container.find(\".sp-hue\"),\n            slideHelper = container.find(\".sp-slider\"),\n            alphaSliderInner = container.find(\".sp-alpha-inner\"),\n            alphaSlider = container.find(\".sp-alpha\"),\n            alphaSlideHelper = container.find(\".sp-alpha-handle\"),\n            textInput = container.find(\".sp-input\"),\n            paletteContainer = container.find(\".sp-palette\"),\n            initialColorContainer = container.find(\".sp-initial\"),\n            cancelButton = container.find(\".sp-cancel\"),\n            clearButton = container.find(\".sp-clear\"),\n            chooseButton = container.find(\".sp-choose\"),\n            toggleButton = container.find(\".sp-palette-toggle\"),\n            isInput = boundElement.is(\"input\"),\n            isInputTypeColor = isInput && boundElement.attr(\"type\") === \"color\" && inputTypeColorSupport(),\n            shouldReplace = isInput && !flat,\n            replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]),\n            offsetElement = (shouldReplace) ? replacer : boundElement,\n            previewElement = replacer.find(\".sp-preview-inner\"),\n            initialColor = opts.color || (isInput && boundElement.val()),\n            colorOnShow = false,\n            currentPreferredFormat = opts.preferredFormat,\n            clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange,\n            isEmpty = !initialColor,\n            allowEmpty = opts.allowEmpty && !isInputTypeColor;\n\n        function applyOptions() {\n\n            if (opts.showPaletteOnly) {\n                opts.showPalette = true;\n            }\n\n            toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);\n\n            if (opts.palette) {\n                palette = opts.palette.slice(0);\n                paletteArray = $.isArray(palette[0]) ? palette : [palette];\n                paletteLookup = {};\n                for (var i = 0; i < paletteArray.length; i++) {\n                    for (var j = 0; j < paletteArray[i].length; j++) {\n                        var rgb = tinycolor(paletteArray[i][j]).toRgbString();\n                        paletteLookup[rgb] = true;\n                    }\n                }\n            }\n\n            container.toggleClass(\"sp-flat\", flat);\n            container.toggleClass(\"sp-input-disabled\", !opts.showInput);\n            container.toggleClass(\"sp-alpha-enabled\", opts.showAlpha);\n            container.toggleClass(\"sp-clear-enabled\", allowEmpty);\n            container.toggleClass(\"sp-buttons-disabled\", !opts.showButtons);\n            container.toggleClass(\"sp-palette-buttons-disabled\", !opts.togglePaletteOnly);\n            container.toggleClass(\"sp-palette-disabled\", !opts.showPalette);\n            container.toggleClass(\"sp-palette-only\", opts.showPaletteOnly);\n            container.toggleClass(\"sp-initial-disabled\", !opts.showInitial);\n            container.addClass(opts.className).addClass(opts.containerClassName);\n\n            reflow();\n        }\n\n        function initialize() {\n\n            if (IE) {\n                container.find(\"*:not(input)\").attr(\"unselectable\", \"on\");\n            }\n\n            applyOptions();\n\n            if (shouldReplace) {\n                boundElement.after(replacer).hide();\n            }\n\n            if (!allowEmpty) {\n                clearButton.hide();\n            }\n\n            if (flat) {\n                boundElement.after(container).hide();\n            }\n            else {\n\n                var appendTo = opts.appendTo === \"parent\" ? boundElement.parent() : $(opts.appendTo);\n                if (appendTo.length !== 1) {\n                    appendTo = $(\"body\");\n                }\n\n                appendTo.append(container);\n            }\n\n            updateSelectionPaletteFromStorage();\n\n            offsetElement.on(\"click.spectrum touchstart.spectrum\", function (e) {\n                if (!disabled) {\n                    toggle();\n                }\n\n                e.stopPropagation();\n\n                if (!$(e.target).is(\"input\")) {\n                    e.preventDefault();\n                }\n            });\n\n            if(boundElement.is(\":disabled\") || (opts.disabled === true)) {\n                disable();\n            }\n\n            // Prevent clicks from bubbling up to document.  This would cause it to be hidden.\n            container.click(stopPropagation);\n\n            // Handle user typed input\n            textInput.change(setFromTextInput);\n            textInput.on(\"paste\", function () {\n                setTimeout(setFromTextInput, 1);\n            });\n            textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } });\n\n            cancelButton.text(opts.cancelText);\n            cancelButton.on(\"click.spectrum\", function (e) {\n                e.stopPropagation();\n                e.preventDefault();\n                revert();\n                hide();\n            });\n\n            clearButton.attr(\"title\", opts.clearText);\n            clearButton.on(\"click.spectrum\", function (e) {\n                e.stopPropagation();\n                e.preventDefault();\n                isEmpty = true;\n                move();\n\n                if(flat) {\n                    //for the flat style, this is a change event\n                    updateOriginalInput(true);\n                }\n            });\n\n            chooseButton.text(opts.chooseText);\n            chooseButton.on(\"click.spectrum\", function (e) {\n                e.stopPropagation();\n                e.preventDefault();\n\n                if (IE && textInput.is(\":focus\")) {\n                    textInput.trigger('change');\n                }\n\n                if (isValid()) {\n                    updateOriginalInput(true);\n                    hide();\n                }\n            });\n\n            toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);\n            toggleButton.on(\"click.spectrum\", function (e) {\n                e.stopPropagation();\n                e.preventDefault();\n\n                opts.showPaletteOnly = !opts.showPaletteOnly;\n\n                // To make sure the Picker area is drawn on the right, next to the\n                // Palette area (and not below the palette), first move the Palette\n                // to the left to make space for the picker, plus 5px extra.\n                // The 'applyOptions' function puts the whole container back into place\n                // and takes care of the button-text and the sp-palette-only CSS class.\n                if (!opts.showPaletteOnly && !flat) {\n                    container.css('left', '-=' + (pickerContainer.outerWidth(true) + 5));\n                }\n                applyOptions();\n            });\n\n            draggable(alphaSlider, function (dragX, dragY, e) {\n                currentAlpha = (dragX / alphaWidth);\n                isEmpty = false;\n                if (e.shiftKey) {\n                    currentAlpha = Math.round(currentAlpha * 10) / 10;\n                }\n\n                move();\n            }, dragStart, dragStop);\n\n            draggable(slider, function (dragX, dragY) {\n                currentHue = parseFloat(dragY / slideHeight);\n                isEmpty = false;\n                if (!opts.showAlpha) {\n                    currentAlpha = 1;\n                }\n                move();\n            }, dragStart, dragStop);\n\n            draggable(dragger, function (dragX, dragY, e) {\n\n                // shift+drag should snap the movement to either the x or y axis.\n                if (!e.shiftKey) {\n                    shiftMovementDirection = null;\n                }\n                else if (!shiftMovementDirection) {\n                    var oldDragX = currentSaturation * dragWidth;\n                    var oldDragY = dragHeight - (currentValue * dragHeight);\n                    var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY);\n\n                    shiftMovementDirection = furtherFromX ? \"x\" : \"y\";\n                }\n\n                var setSaturation = !shiftMovementDirection || shiftMovementDirection === \"x\";\n                var setValue = !shiftMovementDirection || shiftMovementDirection === \"y\";\n\n                if (setSaturation) {\n                    currentSaturation = parseFloat(dragX / dragWidth);\n                }\n                if (setValue) {\n                    currentValue = parseFloat((dragHeight - dragY) / dragHeight);\n                }\n\n                isEmpty = false;\n                if (!opts.showAlpha) {\n                    currentAlpha = 1;\n                }\n\n                move();\n\n            }, dragStart, dragStop);\n\n            if (!!initialColor) {\n                set(initialColor);\n\n                // In case color was black - update the preview UI and set the format\n                // since the set function will not run (default color is black).\n                updateUI();\n                currentPreferredFormat = opts.preferredFormat || tinycolor(initialColor).format;\n\n                addColorToSelectionPalette(initialColor);\n            }\n            else {\n                updateUI();\n            }\n\n            if (flat) {\n                show();\n            }\n\n            function paletteElementClick(e) {\n                if (e.data && e.data.ignore) {\n                    set($(e.target).closest(\".sp-thumb-el\").data(\"color\"));\n                    move();\n                }\n                else {\n                    set($(e.target).closest(\".sp-thumb-el\").data(\"color\"));\n                    move();\n\n                    updateOriginalInput(true);\n                    if (opts.hideAfterPaletteSelect) {\n                        hide();\n                    }\n                }\n\n                return false;\n            }\n\n            var paletteEvent = IE ? \"mousedown.spectrum\" : \"click.spectrum touchstart.spectrum\";\n            paletteContainer.on(paletteEvent, \".sp-thumb-el\", paletteElementClick);\n            initialColorContainer.on(paletteEvent, \".sp-thumb-el:nth-child(1)\", { ignore: true }, paletteElementClick);\n        }\n\n        function updateSelectionPaletteFromStorage() {\n\n            if (localStorageKey && window.localStorage) {\n\n                // Migrate old palettes over to new format.  May want to remove this eventually.\n                try {\n                    var oldPalette = window.localStorage[localStorageKey].split(\",#\");\n                    if (oldPalette.length > 1) {\n                        delete window.localStorage[localStorageKey];\n                        $.each(oldPalette, function(i, c) {\n                            addColorToSelectionPalette(c);\n                        });\n                    }\n                }\n                catch(e) { }\n\n                try {\n                    selectionPalette = window.localStorage[localStorageKey].split(\";\");\n                }\n                catch (e) { }\n            }\n        }\n\n        function addColorToSelectionPalette(color) {\n            if (showSelectionPalette) {\n                var rgb = tinycolor(color).toRgbString();\n                if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) {\n                    selectionPalette.push(rgb);\n                    while(selectionPalette.length > maxSelectionSize) {\n                        selectionPalette.shift();\n                    }\n                }\n\n                if (localStorageKey && window.localStorage) {\n                    try {\n                        window.localStorage[localStorageKey] = selectionPalette.join(\";\");\n                    }\n                    catch(e) { }\n                }\n            }\n        }\n\n        function getUniqueSelectionPalette() {\n            var unique = [];\n            if (opts.showPalette) {\n                for (var i = 0; i < selectionPalette.length; i++) {\n                    var rgb = tinycolor(selectionPalette[i]).toRgbString();\n\n                    if (!paletteLookup[rgb]) {\n                        unique.push(selectionPalette[i]);\n                    }\n                }\n            }\n\n            return unique.reverse().slice(0, opts.maxSelectionSize);\n        }\n\n        function drawPalette() {\n\n            var currentColor = get();\n\n            var html = $.map(paletteArray, function (palette, i) {\n                return paletteTemplate(palette, currentColor, \"sp-palette-row sp-palette-row-\" + i, opts);\n            });\n\n            updateSelectionPaletteFromStorage();\n\n            if (selectionPalette) {\n                html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, \"sp-palette-row sp-palette-row-selection\", opts));\n            }\n\n            paletteContainer.html(html.join(\"\"));\n        }\n\n        function drawInitial() {\n            if (opts.showInitial) {\n                var initial = colorOnShow;\n                var current = get();\n                initialColorContainer.html(paletteTemplate([initial, current], current, \"sp-palette-row-initial\", opts));\n            }\n        }\n\n        function dragStart() {\n            if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) {\n                reflow();\n            }\n            isDragging = true;\n            container.addClass(draggingClass);\n            shiftMovementDirection = null;\n            boundElement.trigger('dragstart.spectrum', [ get() ]);\n        }\n\n        function dragStop() {\n            isDragging = false;\n            container.removeClass(draggingClass);\n            boundElement.trigger('dragstop.spectrum', [ get() ]);\n        }\n\n        function setFromTextInput() {\n\n            var value = textInput.val();\n\n            if ((value === null || value === \"\") && allowEmpty) {\n                set(null);\n                move();\n                updateOriginalInput();\n            }\n            else {\n                var tiny = tinycolor(value);\n                if (tiny.isValid()) {\n                    set(tiny);\n                    move();\n                    updateOriginalInput(true);\n                }\n                else {\n                    textInput.addClass(\"sp-validation-error\");\n                }\n            }\n        }\n\n        function toggle() {\n            if (visible) {\n                hide();\n            }\n            else {\n                show();\n            }\n        }\n\n        function show() {\n            var event = $.Event('beforeShow.spectrum');\n\n            if (visible) {\n                reflow();\n                return;\n            }\n\n            boundElement.trigger(event, [ get() ]);\n\n            if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) {\n                return;\n            }\n\n            hideAll();\n            visible = true;\n\n            $(doc).on(\"keydown.spectrum\", onkeydown);\n            $(doc).on(\"click.spectrum\", clickout);\n            $(window).on(\"resize.spectrum\", resize);\n            replacer.addClass(\"sp-active\");\n            container.removeClass(\"sp-hidden\");\n\n            reflow();\n            updateUI();\n\n            colorOnShow = get();\n\n            drawInitial();\n            callbacks.show(colorOnShow);\n            boundElement.trigger('show.spectrum', [ colorOnShow ]);\n        }\n\n        function onkeydown(e) {\n            // Close on ESC\n            if (e.keyCode === 27) {\n                hide();\n            }\n        }\n\n        function clickout(e) {\n            // Return on right click.\n            if (e.button == 2) { return; }\n\n            // If a drag event was happening during the mouseup, don't hide\n            // on click.\n            if (isDragging) { return; }\n\n            if (clickoutFiresChange) {\n                updateOriginalInput(true);\n            }\n            else {\n                revert();\n            }\n            hide();\n        }\n\n        function hide() {\n            // Return if hiding is unnecessary\n            if (!visible || flat) { return; }\n            visible = false;\n\n            $(doc).off(\"keydown.spectrum\", onkeydown);\n            $(doc).off(\"click.spectrum\", clickout);\n            $(window).off(\"resize.spectrum\", resize);\n\n            replacer.removeClass(\"sp-active\");\n            container.addClass(\"sp-hidden\");\n\n            callbacks.hide(get());\n            boundElement.trigger('hide.spectrum', [ get() ]);\n        }\n\n        function revert() {\n            set(colorOnShow, true);\n            updateOriginalInput(true);\n        }\n\n        function set(color, ignoreFormatChange) {\n            if (tinycolor.equals(color, get())) {\n                // Update UI just in case a validation error needs\n                // to be cleared.\n                updateUI();\n                return;\n            }\n\n            var newColor, newHsv;\n            if (!color && allowEmpty) {\n                isEmpty = true;\n            } else {\n                isEmpty = false;\n                newColor = tinycolor(color);\n                newHsv = newColor.toHsv();\n\n                currentHue = (newHsv.h % 360) / 360;\n                currentSaturation = newHsv.s;\n                currentValue = newHsv.v;\n                currentAlpha = newHsv.a;\n            }\n            updateUI();\n\n            if (newColor && newColor.isValid() && !ignoreFormatChange) {\n                currentPreferredFormat = opts.preferredFormat || newColor.getFormat();\n            }\n        }\n\n        function get(opts) {\n            opts = opts || { };\n\n            if (allowEmpty && isEmpty) {\n                return null;\n            }\n\n            return tinycolor.fromRatio({\n                h: currentHue,\n                s: currentSaturation,\n                v: currentValue,\n                a: Math.round(currentAlpha * 1000) / 1000\n            }, { format: opts.format || currentPreferredFormat });\n        }\n\n        function isValid() {\n            return !textInput.hasClass(\"sp-validation-error\");\n        }\n\n        function move() {\n            updateUI();\n\n            callbacks.move(get());\n            boundElement.trigger('move.spectrum', [ get() ]);\n        }\n\n        function updateUI() {\n\n            textInput.removeClass(\"sp-validation-error\");\n\n            updateHelperLocations();\n\n            // Update dragger background color (gradients take care of saturation and value).\n            var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 });\n            dragger.css(\"background-color\", flatColor.toHexString());\n\n            // Get a format that alpha will be included in (hex and names ignore alpha)\n            var format = currentPreferredFormat;\n            if (currentAlpha < 1 && !(currentAlpha === 0 && format === \"name\")) {\n                if (format === \"hex\" || format === \"hex3\" || format === \"hex6\" || format === \"name\") {\n                    format = \"rgb\";\n                }\n            }\n\n            var realColor = get({ format: format }),\n                displayColor = '';\n\n            //reset background info for preview element\n            previewElement.removeClass(\"sp-clear-display\");\n            previewElement.css('background-color', 'transparent');\n\n            if (!realColor && allowEmpty) {\n                // Update the replaced elements background with icon indicating no color selection\n                previewElement.addClass(\"sp-clear-display\");\n            }\n            else {\n                var realHex = realColor.toHexString(),\n                    realRgb = realColor.toRgbString();\n\n                // Update the replaced elements background color (with actual selected color)\n                if (rgbaSupport || realColor.alpha === 1) {\n                    previewElement.css(\"background-color\", realRgb);\n                }\n                else {\n                    previewElement.css(\"background-color\", \"transparent\");\n                    previewElement.css(\"filter\", realColor.toFilter());\n                }\n\n                if (opts.showAlpha) {\n                    var rgb = realColor.toRgb();\n                    rgb.a = 0;\n                    var realAlpha = tinycolor(rgb).toRgbString();\n                    var gradient = \"linear-gradient(left, \" + realAlpha + \", \" + realHex + \")\";\n\n                    if (IE) {\n                        alphaSliderInner.css(\"filter\", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex));\n                    }\n                    else {\n                        alphaSliderInner.css(\"background\", \"-webkit-\" + gradient);\n                        alphaSliderInner.css(\"background\", \"-moz-\" + gradient);\n                        alphaSliderInner.css(\"background\", \"-ms-\" + gradient);\n                        // Use current syntax gradient on unprefixed property.\n                        alphaSliderInner.css(\"background\",\n                            \"linear-gradient(to right, \" + realAlpha + \", \" + realHex + \")\");\n                    }\n                }\n\n                displayColor = realColor.toString(format);\n            }\n\n            // Update the text entry input as it changes happen\n            if (opts.showInput) {\n                textInput.val(displayColor);\n            }\n\n            if (opts.showPalette) {\n                drawPalette();\n            }\n\n            drawInitial();\n        }\n\n        function updateHelperLocations() {\n            var s = currentSaturation;\n            var v = currentValue;\n\n            if(allowEmpty && isEmpty) {\n                //if selected color is empty, hide the helpers\n                alphaSlideHelper.hide();\n                slideHelper.hide();\n                dragHelper.hide();\n            }\n            else {\n                //make sure helpers are visible\n                alphaSlideHelper.show();\n                slideHelper.show();\n                dragHelper.show();\n\n                // Where to show the little circle in that displays your current selected color\n                var dragX = s * dragWidth;\n                var dragY = dragHeight - (v * dragHeight);\n                dragX = Math.max(\n                    -dragHelperHeight,\n                    Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight)\n                );\n                dragY = Math.max(\n                    -dragHelperHeight,\n                    Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight)\n                );\n                dragHelper.css({\n                    \"top\": dragY + \"px\",\n                    \"left\": dragX + \"px\"\n                });\n\n                var alphaX = currentAlpha * alphaWidth;\n                alphaSlideHelper.css({\n                    \"left\": (alphaX - (alphaSlideHelperWidth / 2)) + \"px\"\n                });\n\n                // Where to show the bar that displays your current selected hue\n                var slideY = (currentHue) * slideHeight;\n                slideHelper.css({\n                    \"top\": (slideY - slideHelperHeight) + \"px\"\n                });\n            }\n        }\n\n        function updateOriginalInput(fireCallback) {\n            var color = get(),\n                displayColor = '',\n                hasChanged = !tinycolor.equals(color, colorOnShow);\n\n            if (color) {\n                displayColor = color.toString(currentPreferredFormat);\n                // Update the selection palette with the current color\n                addColorToSelectionPalette(color);\n            }\n\n            if (isInput) {\n                boundElement.val(displayColor);\n            }\n\n            if (fireCallback && hasChanged) {\n                callbacks.change(color);\n                boundElement.trigger('change', [ color ]);\n            }\n        }\n\n        function reflow() {\n            if (!visible) {\n                return; // Calculations would be useless and wouldn't be reliable anyways\n            }\n            dragWidth = dragger.width();\n            dragHeight = dragger.height();\n            dragHelperHeight = dragHelper.height();\n            slideWidth = slider.width();\n            slideHeight = slider.height();\n            slideHelperHeight = slideHelper.height();\n            alphaWidth = alphaSlider.width();\n            alphaSlideHelperWidth = alphaSlideHelper.width();\n\n            if (!flat) {\n                container.css(\"position\", \"absolute\");\n                if (opts.offset) {\n                    container.offset(opts.offset);\n                } else {\n                    container.offset(getOffset(container, offsetElement));\n                }\n            }\n\n            updateHelperLocations();\n\n            if (opts.showPalette) {\n                drawPalette();\n            }\n\n            boundElement.trigger('reflow.spectrum');\n        }\n\n        function destroy() {\n            boundElement.show();\n            offsetElement.off(\"click.spectrum touchstart.spectrum\");\n            container.remove();\n            replacer.remove();\n            spectrums[spect.id] = null;\n        }\n\n        function option(optionName, optionValue) {\n            if (optionName === undefined) {\n                return $.extend({}, opts);\n            }\n            if (optionValue === undefined) {\n                return opts[optionName];\n            }\n\n            opts[optionName] = optionValue;\n\n            if (optionName === \"preferredFormat\") {\n                currentPreferredFormat = opts.preferredFormat;\n            }\n            applyOptions();\n        }\n\n        function enable() {\n            disabled = false;\n            boundElement.attr(\"disabled\", false);\n            offsetElement.removeClass(\"sp-disabled\");\n        }\n\n        function disable() {\n            hide();\n            disabled = true;\n            boundElement.attr(\"disabled\", true);\n            offsetElement.addClass(\"sp-disabled\");\n        }\n\n        function setOffset(coord) {\n            opts.offset = coord;\n            reflow();\n        }\n\n        initialize();\n\n        var spect = {\n            show: show,\n            hide: hide,\n            toggle: toggle,\n            reflow: reflow,\n            option: option,\n            enable: enable,\n            disable: disable,\n            offset: setOffset,\n            set: function (c) {\n                set(c);\n                updateOriginalInput();\n            },\n            get: get,\n            destroy: destroy,\n            container: container\n        };\n\n        spect.id = spectrums.push(spect) - 1;\n\n        return spect;\n    }\n\n    /**\n     * checkOffset - get the offset below/above and left/right element depending on screen position\n     * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js\n     */\n    function getOffset(picker, input) {\n        var extraY = 0;\n        var dpWidth = picker.outerWidth();\n        var dpHeight = picker.outerHeight();\n        var inputHeight = input.outerHeight();\n        var doc = picker[0].ownerDocument;\n        var docElem = doc.documentElement;\n        var viewWidth = docElem.clientWidth + $(doc).scrollLeft();\n        var viewHeight = docElem.clientHeight + $(doc).scrollTop();\n        var offset = input.offset();\n        var offsetLeft = offset.left;\n        var offsetTop = offset.top;\n\n        offsetTop += inputHeight;\n\n        offsetLeft -=\n            Math.min(offsetLeft, (offsetLeft + dpWidth > viewWidth && viewWidth > dpWidth) ?\n                Math.abs(offsetLeft + dpWidth - viewWidth) : 0);\n\n        offsetTop -=\n            Math.min(offsetTop, ((offsetTop + dpHeight > viewHeight && viewHeight > dpHeight) ?\n                Math.abs(dpHeight + inputHeight - extraY) : extraY));\n\n        return {\n            top: offsetTop,\n            bottom: offset.bottom,\n            left: offsetLeft,\n            right: offset.right,\n            width: offset.width,\n            height: offset.height\n        };\n    }\n\n    /**\n     * noop - do nothing\n     */\n    function noop() {\n\n    }\n\n    /**\n     * stopPropagation - makes the code only doing this a little easier to read in line\n     */\n    function stopPropagation(e) {\n        e.stopPropagation();\n    }\n\n    /**\n     * Create a function bound to a given object\n     * Thanks to underscore.js\n     */\n    function bind(func, obj) {\n        var slice = Array.prototype.slice;\n        var args = slice.call(arguments, 2);\n        return function () {\n            return func.apply(obj, args.concat(slice.call(arguments)));\n        };\n    }\n\n    /**\n     * Lightweight drag helper.  Handles containment within the element, so that\n     * when dragging, the x is within [0,element.width] and y is within [0,element.height]\n     */\n    function draggable(element, onmove, onstart, onstop) {\n        onmove = onmove || function () { };\n        onstart = onstart || function () { };\n        onstop = onstop || function () { };\n        var doc = document;\n        var dragging = false;\n        var offset = {};\n        var maxHeight = 0;\n        var maxWidth = 0;\n        var hasTouch = ('ontouchstart' in window);\n\n        var duringDragEvents = {};\n        duringDragEvents[\"selectstart\"] = prevent;\n        duringDragEvents[\"dragstart\"] = prevent;\n        duringDragEvents[\"touchmove mousemove\"] = move;\n        duringDragEvents[\"touchend mouseup\"] = stop;\n\n        function prevent(e) {\n            if (e.stopPropagation) {\n                e.stopPropagation();\n            }\n            if (e.preventDefault) {\n                e.preventDefault();\n            }\n            e.returnValue = false;\n        }\n\n        function move(e) {\n            if (dragging) {\n                // Mouseup happened outside of window\n                if (IE && doc.documentMode < 9 && !e.button) {\n                    return stop();\n                }\n\n                var t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0];\n                var pageX = t0 && t0.pageX || e.pageX;\n                var pageY = t0 && t0.pageY || e.pageY;\n\n                var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));\n                var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));\n\n                if (hasTouch) {\n                    // Stop scrolling in iOS\n                    prevent(e);\n                }\n\n                onmove.apply(element, [dragX, dragY, e]);\n            }\n        }\n\n        function start(e) {\n            var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);\n\n            if (!rightclick && !dragging) {\n                if (onstart.apply(element, arguments) !== false) {\n                    dragging = true;\n                    maxHeight = $(element).height();\n                    maxWidth = $(element).width();\n                    offset = $(element).offset();\n\n                    $(doc).on(duringDragEvents);\n                    $(doc.body).addClass(\"sp-dragging\");\n\n                    move(e);\n\n                    prevent(e);\n                }\n            }\n        }\n\n        function stop() {\n            if (dragging) {\n                $(doc).off(duringDragEvents);\n                $(doc.body).removeClass(\"sp-dragging\");\n\n                // Wait a tick before notifying observers to allow the click event\n                // to fire in Chrome.\n                setTimeout(function() {\n                    onstop.apply(element, arguments);\n                }, 0);\n            }\n            dragging = false;\n        }\n\n        $(element).on(\"touchstart mousedown\", start);\n    }\n\n    function throttle(func, wait, debounce) {\n        var timeout;\n        return function () {\n            var context = this, args = arguments;\n            var throttler = function () {\n                timeout = null;\n                func.apply(context, args);\n            };\n            if (debounce) clearTimeout(timeout);\n            if (debounce || !timeout) timeout = setTimeout(throttler, wait);\n        };\n    }\n\n    function inputTypeColorSupport() {\n        return $.fn.spectrum.inputTypeColorSupport();\n    }\n\n    /**\n     * Define a jQuery plugin\n     */\n    var dataID = \"spectrum.id\";\n    $.fn.spectrum = function (opts, extra) {\n\n        if (typeof opts == \"string\") {\n\n            var returnValue = this;\n            var args = Array.prototype.slice.call( arguments, 1 );\n\n            this.each(function () {\n                var spect = spectrums[$(this).data(dataID)];\n                if (spect) {\n                    var method = spect[opts];\n                    if (!method) {\n                        throw new Error( \"Spectrum: no such method: '\" + opts + \"'\" );\n                    }\n\n                    if (opts == \"get\") {\n                        returnValue = spect.get();\n                    }\n                    else if (opts == \"container\") {\n                        returnValue = spect.container;\n                    }\n                    else if (opts == \"option\") {\n                        returnValue = spect.option.apply(spect, args);\n                    }\n                    else if (opts == \"destroy\") {\n                        spect.destroy();\n                        $(this).removeData(dataID);\n                    }\n                    else {\n                        method.apply(spect, args);\n                    }\n                }\n            });\n\n            return returnValue;\n        }\n\n        // Initializing a new instance of spectrum\n        return this.spectrum(\"destroy\").each(function () {\n            var options = $.extend({}, $(this).data(), opts);\n            var spect = spectrum(this, options);\n            $(this).data(dataID, spect.id);\n        });\n    };\n\n    $.fn.spectrum.load = true;\n    $.fn.spectrum.loadOpts = {};\n    $.fn.spectrum.draggable = draggable;\n    $.fn.spectrum.defaults = defaultOpts;\n    $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() {\n        if (typeof inputTypeColorSupport._cachedResult === \"undefined\") {\n            var colorInput = $(\"<input type='color'/>\")[0]; // if color element is supported, value will default to not null\n            inputTypeColorSupport._cachedResult = colorInput.type === \"color\" && colorInput.value !== \"\";\n        }\n        return inputTypeColorSupport._cachedResult;\n    };\n\n    $.spectrum = { };\n    $.spectrum.localization = { };\n    $.spectrum.palettes = { };\n\n    $.fn.spectrum.processNativeColorInputs = function () {\n        var colorInputs = $(\"input[type=color]\");\n        if (colorInputs.length && !inputTypeColorSupport()) {\n            colorInputs.spectrum({\n                preferredFormat: \"hex6\"\n            });\n        }\n    };\n\n    // TinyColor v1.1.2\n    // https://github.com/bgrins/TinyColor\n    // Brian Grinstead, MIT License\n\n    (function() {\n\n        var trimLeft = /^[\\s,#]+/,\n            trimRight = /\\s+$/,\n            tinyCounter = 0,\n            math = Math,\n            mathRound = math.round,\n            mathMin = math.min,\n            mathMax = math.max,\n            mathRandom = math.random;\n\n        var tinycolor = function(color, opts) {\n\n            color = (color) ? color : '';\n            opts = opts || { };\n\n            // If input is already a tinycolor, return itself\n            if (color instanceof tinycolor) {\n                return color;\n            }\n            // If we are called as a function, call using new instead\n            if (!(this instanceof tinycolor)) {\n                return new tinycolor(color, opts);\n            }\n\n            var rgb = inputToRGB(color);\n            this._originalInput = color;\n            this._r = rgb.r;\n            this._g = rgb.g;\n            this._b = rgb.b;\n            this._a = rgb.a;\n            this._roundA = mathRound(1000 * this._a) / 1000;\n            this._format = opts.format || rgb.format;\n            this._gradientType = opts.gradientType;\n\n            // Don't let the range of [0,255] come back in [0,1].\n            // Potentially lose a little bit of precision here, but will fix issues where\n            // .5 gets interpreted as half of the total, instead of half of 1\n            // If it was supposed to be 128, this was already taken care of by `inputToRgb`\n            if (this._r < 1) { this._r = mathRound(this._r); }\n            if (this._g < 1) { this._g = mathRound(this._g); }\n            if (this._b < 1) { this._b = mathRound(this._b); }\n\n            this._ok = rgb.ok;\n            this._tc_id = tinyCounter++;\n        };\n\n        tinycolor.prototype = {\n            isDark: function() {\n                return this.getBrightness() < 128;\n            },\n            isLight: function() {\n                return !this.isDark();\n            },\n            isValid: function() {\n                return this._ok;\n            },\n            getOriginalInput: function() {\n                return this._originalInput;\n            },\n            getFormat: function() {\n                return this._format;\n            },\n            getAlpha: function() {\n                return this._a;\n            },\n            getBrightness: function() {\n                var rgb = this.toRgb();\n                return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;\n            },\n            setAlpha: function(value) {\n                this._a = boundAlpha(value);\n                this._roundA = mathRound(1000 * this._a) / 1000;\n                return this;\n            },\n            toHsv: function() {\n                var hsv = rgbToHsv(this._r, this._g, this._b);\n                return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };\n            },\n            toHsvString: function() {\n                var hsv = rgbToHsv(this._r, this._g, this._b);\n                var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);\n                return (this._a == 1) ?\n                    \"hsv(\"  + h + \", \" + s + \"%, \" + v + \"%)\" :\n                    \"hsva(\" + h + \", \" + s + \"%, \" + v + \"%, \"+ this._roundA + \")\";\n            },\n            toHsl: function() {\n                var hsl = rgbToHsl(this._r, this._g, this._b);\n                return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };\n            },\n            toHslString: function() {\n                var hsl = rgbToHsl(this._r, this._g, this._b);\n                var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);\n                return (this._a == 1) ?\n                    \"hsl(\"  + h + \", \" + s + \"%, \" + l + \"%)\" :\n                    \"hsla(\" + h + \", \" + s + \"%, \" + l + \"%, \"+ this._roundA + \")\";\n            },\n            toHex: function(allow3Char) {\n                return rgbToHex(this._r, this._g, this._b, allow3Char);\n            },\n            toHexString: function(allow3Char) {\n                return '#' + this.toHex(allow3Char);\n            },\n            toHex8: function() {\n                return rgbaToHex(this._r, this._g, this._b, this._a);\n            },\n            toHex8String: function() {\n                return '#' + this.toHex8();\n            },\n            toRgb: function() {\n                return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };\n            },\n            toRgbString: function() {\n                return (this._a == 1) ?\n                    \"rgb(\"  + mathRound(this._r) + \", \" + mathRound(this._g) + \", \" + mathRound(this._b) + \")\" :\n                    \"rgba(\" + mathRound(this._r) + \", \" + mathRound(this._g) + \", \" + mathRound(this._b) + \", \" + this._roundA + \")\";\n            },\n            toPercentageRgb: function() {\n                return { r: mathRound(bound01(this._r, 255) * 100) + \"%\", g: mathRound(bound01(this._g, 255) * 100) + \"%\", b: mathRound(bound01(this._b, 255) * 100) + \"%\", a: this._a };\n            },\n            toPercentageRgbString: function() {\n                return (this._a == 1) ?\n                    \"rgb(\"  + mathRound(bound01(this._r, 255) * 100) + \"%, \" + mathRound(bound01(this._g, 255) * 100) + \"%, \" + mathRound(bound01(this._b, 255) * 100) + \"%)\" :\n                    \"rgba(\" + mathRound(bound01(this._r, 255) * 100) + \"%, \" + mathRound(bound01(this._g, 255) * 100) + \"%, \" + mathRound(bound01(this._b, 255) * 100) + \"%, \" + this._roundA + \")\";\n            },\n            toName: function() {\n                if (this._a === 0) {\n                    return \"transparent\";\n                }\n\n                if (this._a < 1) {\n                    return false;\n                }\n\n                return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;\n            },\n            toFilter: function(secondColor) {\n                var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);\n                var secondHex8String = hex8String;\n                var gradientType = this._gradientType ? \"GradientType = 1, \" : \"\";\n\n                if (secondColor) {\n                    var s = tinycolor(secondColor);\n                    secondHex8String = s.toHex8String();\n                }\n\n                return \"progid:DXImageTransform.Microsoft.gradient(\"+gradientType+\"startColorstr=\"+hex8String+\",endColorstr=\"+secondHex8String+\")\";\n            },\n            toString: function(format) {\n                var formatSet = !!format;\n                format = format || this._format;\n\n                var formattedString = false;\n                var hasAlpha = this._a < 1 && this._a >= 0;\n                var needsAlphaFormat = !formatSet && hasAlpha && (format === \"hex\" || format === \"hex6\" || format === \"hex3\" || format === \"name\");\n\n                if (needsAlphaFormat) {\n                    // Special case for \"transparent\", all other non-alpha formats\n                    // will return rgba when there is transparency.\n                    if (format === \"name\" && this._a === 0) {\n                        return this.toName();\n                    }\n                    return this.toRgbString();\n                }\n                if (format === \"rgb\") {\n                    formattedString = this.toRgbString();\n                }\n                if (format === \"prgb\") {\n                    formattedString = this.toPercentageRgbString();\n                }\n                if (format === \"hex\" || format === \"hex6\") {\n                    formattedString = this.toHexString();\n                }\n                if (format === \"hex3\") {\n                    formattedString = this.toHexString(true);\n                }\n                if (format === \"hex8\") {\n                    formattedString = this.toHex8String();\n                }\n                if (format === \"name\") {\n                    formattedString = this.toName();\n                }\n                if (format === \"hsl\") {\n                    formattedString = this.toHslString();\n                }\n                if (format === \"hsv\") {\n                    formattedString = this.toHsvString();\n                }\n\n                return formattedString || this.toHexString();\n            },\n\n            _applyModification: function(fn, args) {\n                var color = fn.apply(null, [this].concat([].slice.call(args)));\n                this._r = color._r;\n                this._g = color._g;\n                this._b = color._b;\n                this.setAlpha(color._a);\n                return this;\n            },\n            lighten: function() {\n                return this._applyModification(lighten, arguments);\n            },\n            brighten: function() {\n                return this._applyModification(brighten, arguments);\n            },\n            darken: function() {\n                return this._applyModification(darken, arguments);\n            },\n            desaturate: function() {\n                return this._applyModification(desaturate, arguments);\n            },\n            saturate: function() {\n                return this._applyModification(saturate, arguments);\n            },\n            greyscale: function() {\n                return this._applyModification(greyscale, arguments);\n            },\n            spin: function() {\n                return this._applyModification(spin, arguments);\n            },\n\n            _applyCombination: function(fn, args) {\n                return fn.apply(null, [this].concat([].slice.call(args)));\n            },\n            analogous: function() {\n                return this._applyCombination(analogous, arguments);\n            },\n            complement: function() {\n                return this._applyCombination(complement, arguments);\n            },\n            monochromatic: function() {\n                return this._applyCombination(monochromatic, arguments);\n            },\n            splitcomplement: function() {\n                return this._applyCombination(splitcomplement, arguments);\n            },\n            triad: function() {\n                return this._applyCombination(triad, arguments);\n            },\n            tetrad: function() {\n                return this._applyCombination(tetrad, arguments);\n            }\n        };\n\n        // If input is an object, force 1 into \"1.0\" to handle ratios properly\n        // String input requires \"1.0\" as input, so 1 will be treated as 1\n        tinycolor.fromRatio = function(color, opts) {\n            if (typeof color == \"object\") {\n                var newColor = {};\n                for (var i in color) {\n                    if (color.hasOwnProperty(i)) {\n                        if (i === \"a\") {\n                            newColor[i] = color[i];\n                        }\n                        else {\n                            newColor[i] = convertToPercentage(color[i]);\n                        }\n                    }\n                }\n                color = newColor;\n            }\n\n            return tinycolor(color, opts);\n        };\n\n        // Given a string or object, convert that input to RGB\n        // Possible string inputs:\n        //\n        //     \"red\"\n        //     \"#f00\" or \"f00\"\n        //     \"#ff0000\" or \"ff0000\"\n        //     \"#ff000000\" or \"ff000000\"\n        //     \"rgb 255 0 0\" or \"rgb (255, 0, 0)\"\n        //     \"rgb 1.0 0 0\" or \"rgb (1, 0, 0)\"\n        //     \"rgba (255, 0, 0, 1)\" or \"rgba 255, 0, 0, 1\"\n        //     \"rgba (1.0, 0, 0, 1)\" or \"rgba 1.0, 0, 0, 1\"\n        //     \"hsl(0, 100%, 50%)\" or \"hsl 0 100% 50%\"\n        //     \"hsla(0, 100%, 50%, 1)\" or \"hsla 0 100% 50%, 1\"\n        //     \"hsv(0, 100%, 100%)\" or \"hsv 0 100% 100%\"\n        //\n        function inputToRGB(color) {\n\n            var rgb = { r: 0, g: 0, b: 0 };\n            var a = 1;\n            var ok = false;\n            var format = false;\n\n            if (typeof color == \"string\") {\n                color = stringInputToObject(color);\n            }\n\n            if (typeof color == \"object\") {\n                if (color.hasOwnProperty(\"r\") && color.hasOwnProperty(\"g\") && color.hasOwnProperty(\"b\")) {\n                    rgb = rgbToRgb(color.r, color.g, color.b);\n                    ok = true;\n                    format = String(color.r).substr(-1) === \"%\" ? \"prgb\" : \"rgb\";\n                }\n                else if (color.hasOwnProperty(\"h\") && color.hasOwnProperty(\"s\") && color.hasOwnProperty(\"v\")) {\n                    color.s = convertToPercentage(color.s);\n                    color.v = convertToPercentage(color.v);\n                    rgb = hsvToRgb(color.h, color.s, color.v);\n                    ok = true;\n                    format = \"hsv\";\n                }\n                else if (color.hasOwnProperty(\"h\") && color.hasOwnProperty(\"s\") && color.hasOwnProperty(\"l\")) {\n                    color.s = convertToPercentage(color.s);\n                    color.l = convertToPercentage(color.l);\n                    rgb = hslToRgb(color.h, color.s, color.l);\n                    ok = true;\n                    format = \"hsl\";\n                }\n\n                if (color.hasOwnProperty(\"a\")) {\n                    a = color.a;\n                }\n            }\n\n            a = boundAlpha(a);\n\n            return {\n                ok: ok,\n                format: color.format || format,\n                r: mathMin(255, mathMax(rgb.r, 0)),\n                g: mathMin(255, mathMax(rgb.g, 0)),\n                b: mathMin(255, mathMax(rgb.b, 0)),\n                a: a\n            };\n        }\n\n\n        // Conversion Functions\n        // --------------------\n\n        // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:\n        // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>\n\n        // `rgbToRgb`\n        // Handle bounds / percentage checking to conform to CSS color spec\n        // <http://www.w3.org/TR/css3-color/>\n        // *Assumes:* r, g, b in [0, 255] or [0, 1]\n        // *Returns:* { r, g, b } in [0, 255]\n        function rgbToRgb(r, g, b){\n            return {\n                r: bound01(r, 255) * 255,\n                g: bound01(g, 255) * 255,\n                b: bound01(b, 255) * 255\n            };\n        }\n\n        // `rgbToHsl`\n        // Converts an RGB color value to HSL.\n        // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]\n        // *Returns:* { h, s, l } in [0,1]\n        function rgbToHsl(r, g, b) {\n\n            r = bound01(r, 255);\n            g = bound01(g, 255);\n            b = bound01(b, 255);\n\n            var max = mathMax(r, g, b), min = mathMin(r, g, b);\n            var h, s, l = (max + min) / 2;\n\n            if(max == min) {\n                h = s = 0; // achromatic\n            }\n            else {\n                var d = max - min;\n                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n                switch(max) {\n                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;\n                    case g: h = (b - r) / d + 2; break;\n                    case b: h = (r - g) / d + 4; break;\n                }\n\n                h /= 6;\n            }\n\n            return { h: h, s: s, l: l };\n        }\n\n        // `hslToRgb`\n        // Converts an HSL color value to RGB.\n        // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]\n        // *Returns:* { r, g, b } in the set [0, 255]\n        function hslToRgb(h, s, l) {\n            var r, g, b;\n\n            h = bound01(h, 360);\n            s = bound01(s, 100);\n            l = bound01(l, 100);\n\n            function hue2rgb(p, q, t) {\n                if(t < 0) t += 1;\n                if(t > 1) t -= 1;\n                if(t < 1/6) return p + (q - p) * 6 * t;\n                if(t < 1/2) return q;\n                if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;\n                return p;\n            }\n\n            if(s === 0) {\n                r = g = b = l; // achromatic\n            }\n            else {\n                var q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n                var p = 2 * l - q;\n                r = hue2rgb(p, q, h + 1/3);\n                g = hue2rgb(p, q, h);\n                b = hue2rgb(p, q, h - 1/3);\n            }\n\n            return { r: r * 255, g: g * 255, b: b * 255 };\n        }\n\n        // `rgbToHsv`\n        // Converts an RGB color value to HSV\n        // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]\n        // *Returns:* { h, s, v } in [0,1]\n        function rgbToHsv(r, g, b) {\n\n            r = bound01(r, 255);\n            g = bound01(g, 255);\n            b = bound01(b, 255);\n\n            var max = mathMax(r, g, b), min = mathMin(r, g, b);\n            var h, s, v = max;\n\n            var d = max - min;\n            s = max === 0 ? 0 : d / max;\n\n            if(max == min) {\n                h = 0; // achromatic\n            }\n            else {\n                switch(max) {\n                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;\n                    case g: h = (b - r) / d + 2; break;\n                    case b: h = (r - g) / d + 4; break;\n                }\n                h /= 6;\n            }\n            return { h: h, s: s, v: v };\n        }\n\n        // `hsvToRgb`\n        // Converts an HSV color value to RGB.\n        // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]\n        // *Returns:* { r, g, b } in the set [0, 255]\n        function hsvToRgb(h, s, v) {\n\n            h = bound01(h, 360) * 6;\n            s = bound01(s, 100);\n            v = bound01(v, 100);\n\n            var i = math.floor(h),\n                f = h - i,\n                p = v * (1 - s),\n                q = v * (1 - f * s),\n                t = v * (1 - (1 - f) * s),\n                mod = i % 6,\n                r = [v, q, p, p, t, v][mod],\n                g = [t, v, v, q, p, p][mod],\n                b = [p, p, t, v, v, q][mod];\n\n            return { r: r * 255, g: g * 255, b: b * 255 };\n        }\n\n        // `rgbToHex`\n        // Converts an RGB color to hex\n        // Assumes r, g, and b are contained in the set [0, 255]\n        // Returns a 3 or 6 character hex\n        function rgbToHex(r, g, b, allow3Char) {\n\n            var hex = [\n                pad2(mathRound(r).toString(16)),\n                pad2(mathRound(g).toString(16)),\n                pad2(mathRound(b).toString(16))\n            ];\n\n            // Return a 3 character hex if possible\n            if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {\n                return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);\n            }\n\n            return hex.join(\"\");\n        }\n        // `rgbaToHex`\n        // Converts an RGBA color plus alpha transparency to hex\n        // Assumes r, g, b and a are contained in the set [0, 255]\n        // Returns an 8 character hex\n        function rgbaToHex(r, g, b, a) {\n\n            var hex = [\n                pad2(convertDecimalToHex(a)),\n                pad2(mathRound(r).toString(16)),\n                pad2(mathRound(g).toString(16)),\n                pad2(mathRound(b).toString(16))\n            ];\n\n            return hex.join(\"\");\n        }\n\n        // `equals`\n        // Can be called with any tinycolor input\n        tinycolor.equals = function (color1, color2) {\n            if (!color1 || !color2) { return false; }\n            return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();\n        };\n        tinycolor.random = function() {\n            return tinycolor.fromRatio({\n                r: mathRandom(),\n                g: mathRandom(),\n                b: mathRandom()\n            });\n        };\n\n\n        // Modification Functions\n        // ----------------------\n        // Thanks to less.js for some of the basics here\n        // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>\n\n        function desaturate(color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var hsl = tinycolor(color).toHsl();\n            hsl.s -= amount / 100;\n            hsl.s = clamp01(hsl.s);\n            return tinycolor(hsl);\n        }\n\n        function saturate(color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var hsl = tinycolor(color).toHsl();\n            hsl.s += amount / 100;\n            hsl.s = clamp01(hsl.s);\n            return tinycolor(hsl);\n        }\n\n        function greyscale(color) {\n            return tinycolor(color).desaturate(100);\n        }\n\n        function lighten (color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var hsl = tinycolor(color).toHsl();\n            hsl.l += amount / 100;\n            hsl.l = clamp01(hsl.l);\n            return tinycolor(hsl);\n        }\n\n        function brighten(color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var rgb = tinycolor(color).toRgb();\n            rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));\n            rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));\n            rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));\n            return tinycolor(rgb);\n        }\n\n        function darken (color, amount) {\n            amount = (amount === 0) ? 0 : (amount || 10);\n            var hsl = tinycolor(color).toHsl();\n            hsl.l -= amount / 100;\n            hsl.l = clamp01(hsl.l);\n            return tinycolor(hsl);\n        }\n\n        // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.\n        // Values outside of this range will be wrapped into this range.\n        function spin(color, amount) {\n            var hsl = tinycolor(color).toHsl();\n            var hue = (mathRound(hsl.h) + amount) % 360;\n            hsl.h = hue < 0 ? 360 + hue : hue;\n            return tinycolor(hsl);\n        }\n\n        // Combination Functions\n        // ---------------------\n        // Thanks to jQuery xColor for some of the ideas behind these\n        // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>\n\n        function complement(color) {\n            var hsl = tinycolor(color).toHsl();\n            hsl.h = (hsl.h + 180) % 360;\n            return tinycolor(hsl);\n        }\n\n        function triad(color) {\n            var hsl = tinycolor(color).toHsl();\n            var h = hsl.h;\n            return [\n                tinycolor(color),\n                tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),\n                tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })\n            ];\n        }\n\n        function tetrad(color) {\n            var hsl = tinycolor(color).toHsl();\n            var h = hsl.h;\n            return [\n                tinycolor(color),\n                tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),\n                tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),\n                tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })\n            ];\n        }\n\n        function splitcomplement(color) {\n            var hsl = tinycolor(color).toHsl();\n            var h = hsl.h;\n            return [\n                tinycolor(color),\n                tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),\n                tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})\n            ];\n        }\n\n        function analogous(color, results, slices) {\n            results = results || 6;\n            slices = slices || 30;\n\n            var hsl = tinycolor(color).toHsl();\n            var part = 360 / slices;\n            var ret = [tinycolor(color)];\n\n            for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {\n                hsl.h = (hsl.h + part) % 360;\n                ret.push(tinycolor(hsl));\n            }\n            return ret;\n        }\n\n        function monochromatic(color, results) {\n            results = results || 6;\n            var hsv = tinycolor(color).toHsv();\n            var h = hsv.h, s = hsv.s, v = hsv.v;\n            var ret = [];\n            var modification = 1 / results;\n\n            while (results--) {\n                ret.push(tinycolor({ h: h, s: s, v: v}));\n                v = (v + modification) % 1;\n            }\n\n            return ret;\n        }\n\n        // Utility Functions\n        // ---------------------\n\n        tinycolor.mix = function(color1, color2, amount) {\n            amount = (amount === 0) ? 0 : (amount || 50);\n\n            var rgb1 = tinycolor(color1).toRgb();\n            var rgb2 = tinycolor(color2).toRgb();\n\n            var p = amount / 100;\n            var w = p * 2 - 1;\n            var a = rgb2.a - rgb1.a;\n\n            var w1;\n\n            if (w * a == -1) {\n                w1 = w;\n            } else {\n                w1 = (w + a) / (1 + w * a);\n            }\n\n            w1 = (w1 + 1) / 2;\n\n            var w2 = 1 - w1;\n\n            var rgba = {\n                r: rgb2.r * w1 + rgb1.r * w2,\n                g: rgb2.g * w1 + rgb1.g * w2,\n                b: rgb2.b * w1 + rgb1.b * w2,\n                a: rgb2.a * p  + rgb1.a * (1 - p)\n            };\n\n            return tinycolor(rgba);\n        };\n\n\n        // Readability Functions\n        // ---------------------\n        // <http://www.w3.org/TR/AERT#color-contrast>\n\n        // `readability`\n        // Analyze the 2 colors and returns an object with the following properties:\n        //    `brightness`: difference in brightness between the two colors\n        //    `color`: difference in color/hue between the two colors\n        tinycolor.readability = function(color1, color2) {\n            var c1 = tinycolor(color1);\n            var c2 = tinycolor(color2);\n            var rgb1 = c1.toRgb();\n            var rgb2 = c2.toRgb();\n            var brightnessA = c1.getBrightness();\n            var brightnessB = c2.getBrightness();\n            var colorDiff = (\n                Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) +\n                Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) +\n                Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b)\n            );\n\n            return {\n                brightness: Math.abs(brightnessA - brightnessB),\n                color: colorDiff\n            };\n        };\n\n        // `readable`\n        // http://www.w3.org/TR/AERT#color-contrast\n        // Ensure that foreground and background color combinations provide sufficient contrast.\n        // *Example*\n        //    tinycolor.isReadable(\"#000\", \"#111\") => false\n        tinycolor.isReadable = function(color1, color2) {\n            var readability = tinycolor.readability(color1, color2);\n            return readability.brightness > 125 && readability.color > 500;\n        };\n\n        // `mostReadable`\n        // Given a base color and a list of possible foreground or background\n        // colors for that base, returns the most readable color.\n        // *Example*\n        //    tinycolor.mostReadable(\"#123\", [\"#fff\", \"#000\"]) => \"#000\"\n        tinycolor.mostReadable = function(baseColor, colorList) {\n            var bestColor = null;\n            var bestScore = 0;\n            var bestIsReadable = false;\n            for (var i=0; i < colorList.length; i++) {\n\n                // We normalize both around the \"acceptable\" breaking point,\n                // but rank brightness constrast higher than hue.\n\n                var readability = tinycolor.readability(baseColor, colorList[i]);\n                var readable = readability.brightness > 125 && readability.color > 500;\n                var score = 3 * (readability.brightness / 125) + (readability.color / 500);\n\n                if ((readable && ! bestIsReadable) ||\n                    (readable && bestIsReadable && score > bestScore) ||\n                    ((! readable) && (! bestIsReadable) && score > bestScore)) {\n                    bestIsReadable = readable;\n                    bestScore = score;\n                    bestColor = tinycolor(colorList[i]);\n                }\n            }\n            return bestColor;\n        };\n\n\n        // Big List of Colors\n        // ------------------\n        // <http://www.w3.org/TR/css3-color/#svg-color>\n        var names = tinycolor.names = {\n            aliceblue: \"f0f8ff\",\n            antiquewhite: \"faebd7\",\n            aqua: \"0ff\",\n            aquamarine: \"7fffd4\",\n            azure: \"f0ffff\",\n            beige: \"f5f5dc\",\n            bisque: \"ffe4c4\",\n            black: \"000\",\n            blanchedalmond: \"ffebcd\",\n            blue: \"00f\",\n            blueviolet: \"8a2be2\",\n            brown: \"a52a2a\",\n            burlywood: \"deb887\",\n            burntsienna: \"ea7e5d\",\n            cadetblue: \"5f9ea0\",\n            chartreuse: \"7fff00\",\n            chocolate: \"d2691e\",\n            coral: \"ff7f50\",\n            cornflowerblue: \"6495ed\",\n            cornsilk: \"fff8dc\",\n            crimson: \"dc143c\",\n            cyan: \"0ff\",\n            darkblue: \"00008b\",\n            darkcyan: \"008b8b\",\n            darkgoldenrod: \"b8860b\",\n            darkgray: \"a9a9a9\",\n            darkgreen: \"006400\",\n            darkgrey: \"a9a9a9\",\n            darkkhaki: \"bdb76b\",\n            darkmagenta: \"8b008b\",\n            darkolivegreen: \"556b2f\",\n            darkorange: \"ff8c00\",\n            darkorchid: \"9932cc\",\n            darkred: \"8b0000\",\n            darksalmon: \"e9967a\",\n            darkseagreen: \"8fbc8f\",\n            darkslateblue: \"483d8b\",\n            darkslategray: \"2f4f4f\",\n            darkslategrey: \"2f4f4f\",\n            darkturquoise: \"00ced1\",\n            darkviolet: \"9400d3\",\n            deeppink: \"ff1493\",\n            deepskyblue: \"00bfff\",\n            dimgray: \"696969\",\n            dimgrey: \"696969\",\n            dodgerblue: \"1e90ff\",\n            firebrick: \"b22222\",\n            floralwhite: \"fffaf0\",\n            forestgreen: \"228b22\",\n            fuchsia: \"f0f\",\n            gainsboro: \"dcdcdc\",\n            ghostwhite: \"f8f8ff\",\n            gold: \"ffd700\",\n            goldenrod: \"daa520\",\n            gray: \"808080\",\n            green: \"008000\",\n            greenyellow: \"adff2f\",\n            grey: \"808080\",\n            honeydew: \"f0fff0\",\n            hotpink: \"ff69b4\",\n            indianred: \"cd5c5c\",\n            indigo: \"4b0082\",\n            ivory: \"fffff0\",\n            khaki: \"f0e68c\",\n            lavender: \"e6e6fa\",\n            lavenderblush: \"fff0f5\",\n            lawngreen: \"7cfc00\",\n            lemonchiffon: \"fffacd\",\n            lightblue: \"add8e6\",\n            lightcoral: \"f08080\",\n            lightcyan: \"e0ffff\",\n            lightgoldenrodyellow: \"fafad2\",\n            lightgray: \"d3d3d3\",\n            lightgreen: \"90ee90\",\n            lightgrey: \"d3d3d3\",\n            lightpink: \"ffb6c1\",\n            lightsalmon: \"ffa07a\",\n            lightseagreen: \"20b2aa\",\n            lightskyblue: \"87cefa\",\n            lightslategray: \"789\",\n            lightslategrey: \"789\",\n            lightsteelblue: \"b0c4de\",\n            lightyellow: \"ffffe0\",\n            lime: \"0f0\",\n            limegreen: \"32cd32\",\n            linen: \"faf0e6\",\n            magenta: \"f0f\",\n            maroon: \"800000\",\n            mediumaquamarine: \"66cdaa\",\n            mediumblue: \"0000cd\",\n            mediumorchid: \"ba55d3\",\n            mediumpurple: \"9370db\",\n            mediumseagreen: \"3cb371\",\n            mediumslateblue: \"7b68ee\",\n            mediumspringgreen: \"00fa9a\",\n            mediumturquoise: \"48d1cc\",\n            mediumvioletred: \"c71585\",\n            midnightblue: \"191970\",\n            mintcream: \"f5fffa\",\n            mistyrose: \"ffe4e1\",\n            moccasin: \"ffe4b5\",\n            navajowhite: \"ffdead\",\n            navy: \"000080\",\n            oldlace: \"fdf5e6\",\n            olive: \"808000\",\n            olivedrab: \"6b8e23\",\n            orange: \"ffa500\",\n            orangered: \"ff4500\",\n            orchid: \"da70d6\",\n            palegoldenrod: \"eee8aa\",\n            palegreen: \"98fb98\",\n            paleturquoise: \"afeeee\",\n            palevioletred: \"db7093\",\n            papayawhip: \"ffefd5\",\n            peachpuff: \"ffdab9\",\n            peru: \"cd853f\",\n            pink: \"ffc0cb\",\n            plum: \"dda0dd\",\n            powderblue: \"b0e0e6\",\n            purple: \"800080\",\n            rebeccapurple: \"663399\",\n            red: \"f00\",\n            rosybrown: \"bc8f8f\",\n            royalblue: \"4169e1\",\n            saddlebrown: \"8b4513\",\n            salmon: \"fa8072\",\n            sandybrown: \"f4a460\",\n            seagreen: \"2e8b57\",\n            seashell: \"fff5ee\",\n            sienna: \"a0522d\",\n            silver: \"c0c0c0\",\n            skyblue: \"87ceeb\",\n            slateblue: \"6a5acd\",\n            slategray: \"708090\",\n            slategrey: \"708090\",\n            snow: \"fffafa\",\n            springgreen: \"00ff7f\",\n            steelblue: \"4682b4\",\n            tan: \"d2b48c\",\n            teal: \"008080\",\n            thistle: \"d8bfd8\",\n            tomato: \"ff6347\",\n            turquoise: \"40e0d0\",\n            violet: \"ee82ee\",\n            wheat: \"f5deb3\",\n            white: \"fff\",\n            whitesmoke: \"f5f5f5\",\n            yellow: \"ff0\",\n            yellowgreen: \"9acd32\"\n        };\n\n        // Make it easy to access colors via `hexNames[hex]`\n        var hexNames = tinycolor.hexNames = flip(names);\n\n\n        // Utilities\n        // ---------\n\n        // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`\n        function flip(o) {\n            var flipped = { };\n            for (var i in o) {\n                if (o.hasOwnProperty(i)) {\n                    flipped[o[i]] = i;\n                }\n            }\n            return flipped;\n        }\n\n        // Return a valid alpha value [0,1] with all invalid values being set to 1\n        function boundAlpha(a) {\n            a = parseFloat(a);\n\n            if (isNaN(a) || a < 0 || a > 1) {\n                a = 1;\n            }\n\n            return a;\n        }\n\n        // Take input from [0, n] and return it as [0, 1]\n        function bound01(n, max) {\n            if (isOnePointZero(n)) { n = \"100%\"; }\n\n            var processPercent = isPercentage(n);\n            n = mathMin(max, mathMax(0, parseFloat(n)));\n\n            // Automatically convert percentage into number\n            if (processPercent) {\n                n = parseInt(n * max, 10) / 100;\n            }\n\n            // Handle floating point rounding errors\n            if ((math.abs(n - max) < 0.000001)) {\n                return 1;\n            }\n\n            // Convert into [0, 1] range if it isn't already\n            return (n % max) / parseFloat(max);\n        }\n\n        // Force a number between 0 and 1\n        function clamp01(val) {\n            return mathMin(1, mathMax(0, val));\n        }\n\n        // Parse a base-16 hex value into a base-10 integer\n        function parseIntFromHex(val) {\n            return parseInt(val, 16);\n        }\n\n        // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1\n        // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>\n        function isOnePointZero(n) {\n            return typeof n == \"string\" && n.indexOf('.') != -1 && parseFloat(n) === 1;\n        }\n\n        // Check to see if string passed in is a percentage\n        function isPercentage(n) {\n            return typeof n === \"string\" && n.indexOf('%') != -1;\n        }\n\n        // Force a hex value to have 2 characters\n        function pad2(c) {\n            return c.length == 1 ? '0' + c : '' + c;\n        }\n\n        // Replace a decimal with it's percentage value\n        function convertToPercentage(n) {\n            if (n <= 1) {\n                n = (n * 100) + \"%\";\n            }\n\n            return n;\n        }\n\n        // Converts a decimal to a hex value\n        function convertDecimalToHex(d) {\n            return Math.round(parseFloat(d) * 255).toString(16);\n        }\n        // Converts a hex value to a decimal\n        function convertHexToDecimal(h) {\n            return (parseIntFromHex(h) / 255);\n        }\n\n        var matchers = (function() {\n\n            // <http://www.w3.org/TR/css3-values/#integers>\n            var CSS_INTEGER = \"[-\\\\+]?\\\\d+%?\";\n\n            // <http://www.w3.org/TR/css3-values/#number-value>\n            var CSS_NUMBER = \"[-\\\\+]?\\\\d*\\\\.\\\\d+%?\";\n\n            // Allow positive/negative integer/number.  Don't capture the either/or, just the entire outcome.\n            var CSS_UNIT = \"(?:\" + CSS_NUMBER + \")|(?:\" + CSS_INTEGER + \")\";\n\n            // Actual matching.\n            // Parentheses and commas are optional, but not required.\n            // Whitespace can take the place of commas or opening paren\n            var PERMISSIVE_MATCH3 = \"[\\\\s|\\\\(]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")\\\\s*\\\\)?\";\n            var PERMISSIVE_MATCH4 = \"[\\\\s|\\\\(]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")\\\\s*\\\\)?\";\n\n            return {\n                rgb: new RegExp(\"rgb\" + PERMISSIVE_MATCH3),\n                rgba: new RegExp(\"rgba\" + PERMISSIVE_MATCH4),\n                hsl: new RegExp(\"hsl\" + PERMISSIVE_MATCH3),\n                hsla: new RegExp(\"hsla\" + PERMISSIVE_MATCH4),\n                hsv: new RegExp(\"hsv\" + PERMISSIVE_MATCH3),\n                hsva: new RegExp(\"hsva\" + PERMISSIVE_MATCH4),\n                hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,\n                hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,\n                hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/\n            };\n        })();\n\n        // `stringInputToObject`\n        // Permissive string parsing.  Take in a number of formats, and output an object\n        // based on detected format.  Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`\n        function stringInputToObject(color) {\n\n            color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();\n            var named = false;\n            if (names[color]) {\n                color = names[color];\n                named = true;\n            }\n            else if (color == 'transparent') {\n                return { r: 0, g: 0, b: 0, a: 0, format: \"name\" };\n            }\n\n            // Try to match string input using regular expressions.\n            // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]\n            // Just return an object and let the conversion functions handle that.\n            // This way the result will be the same whether the tinycolor is initialized with string or object.\n            var match;\n            if ((match = matchers.rgb.exec(color))) {\n                return { r: match[1], g: match[2], b: match[3] };\n            }\n            if ((match = matchers.rgba.exec(color))) {\n                return { r: match[1], g: match[2], b: match[3], a: match[4] };\n            }\n            if ((match = matchers.hsl.exec(color))) {\n                return { h: match[1], s: match[2], l: match[3] };\n            }\n            if ((match = matchers.hsla.exec(color))) {\n                return { h: match[1], s: match[2], l: match[3], a: match[4] };\n            }\n            if ((match = matchers.hsv.exec(color))) {\n                return { h: match[1], s: match[2], v: match[3] };\n            }\n            if ((match = matchers.hsva.exec(color))) {\n                return { h: match[1], s: match[2], v: match[3], a: match[4] };\n            }\n            if ((match = matchers.hex8.exec(color))) {\n                return {\n                    a: convertHexToDecimal(match[1]),\n                    r: parseIntFromHex(match[2]),\n                    g: parseIntFromHex(match[3]),\n                    b: parseIntFromHex(match[4]),\n                    format: named ? \"name\" : \"hex8\"\n                };\n            }\n            if ((match = matchers.hex6.exec(color))) {\n                return {\n                    r: parseIntFromHex(match[1]),\n                    g: parseIntFromHex(match[2]),\n                    b: parseIntFromHex(match[3]),\n                    format: named ? \"name\" : \"hex\"\n                };\n            }\n            if ((match = matchers.hex3.exec(color))) {\n                return {\n                    r: parseIntFromHex(match[1] + '' + match[1]),\n                    g: parseIntFromHex(match[2] + '' + match[2]),\n                    b: parseIntFromHex(match[3] + '' + match[3]),\n                    format: named ? \"name\" : \"hex\"\n                };\n            }\n\n            return false;\n        }\n\n        window.tinycolor = tinycolor;\n    })();\n\n    $(function () {\n        if ($.fn.spectrum.load) {\n            $.fn.spectrum.processNativeColorInputs();\n        }\n    });\n\n});\n","jquery/spectrum/tinycolor.js":"// TinyColor v1.4.2\n// https://github.com/bgrins/TinyColor\n// Brian Grinstead, MIT License\n\n(function(Math) {\n\n    var trimLeft = /^\\s+/,\n        trimRight = /\\s+$/,\n        tinyCounter = 0,\n        mathRound = Math.round,\n        mathMin = Math.min,\n        mathMax = Math.max,\n        mathRandom = Math.random;\n\n    function tinycolor (color, opts) {\n\n        color = (color) ? color : '';\n        opts = opts || { };\n\n        // If input is already a tinycolor, return itself\n        if (color instanceof tinycolor) {\n            return color;\n        }\n        // If we are called as a function, call using new instead\n        if (!(this instanceof tinycolor)) {\n            return new tinycolor(color, opts);\n        }\n\n        var rgb = inputToRGB(color);\n        this._originalInput = color,\n            this._r = rgb.r,\n            this._g = rgb.g,\n            this._b = rgb.b,\n            this._a = rgb.a,\n            this._roundA = mathRound(100*this._a) / 100,\n            this._format = opts.format || rgb.format;\n        this._gradientType = opts.gradientType;\n\n        // Don't let the range of [0,255] come back in [0,1].\n        // Potentially lose a little bit of precision here, but will fix issues where\n        // .5 gets interpreted as half of the total, instead of half of 1\n        // If it was supposed to be 128, this was already taken care of by `inputToRgb`\n        if (this._r < 1) { this._r = mathRound(this._r); }\n        if (this._g < 1) { this._g = mathRound(this._g); }\n        if (this._b < 1) { this._b = mathRound(this._b); }\n\n        this._ok = rgb.ok;\n        this._tc_id = tinyCounter++;\n    }\n\n    tinycolor.prototype = {\n        isDark: function() {\n            return this.getBrightness() < 128;\n        },\n        isLight: function() {\n            return !this.isDark();\n        },\n        isValid: function() {\n            return this._ok;\n        },\n        getOriginalInput: function() {\n            return this._originalInput;\n        },\n        getFormat: function() {\n            return this._format;\n        },\n        getAlpha: function() {\n            return this._a;\n        },\n        getBrightness: function() {\n            //http://www.w3.org/TR/AERT#color-contrast\n            var rgb = this.toRgb();\n            return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;\n        },\n        getLuminance: function() {\n            //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef\n            var rgb = this.toRgb();\n            var RsRGB, GsRGB, BsRGB, R, G, B;\n            RsRGB = rgb.r/255;\n            GsRGB = rgb.g/255;\n            BsRGB = rgb.b/255;\n\n            if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}\n            if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}\n            if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}\n            return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);\n        },\n        setAlpha: function(value) {\n            this._a = boundAlpha(value);\n            this._roundA = mathRound(100*this._a) / 100;\n            return this;\n        },\n        toHsv: function() {\n            var hsv = rgbToHsv(this._r, this._g, this._b);\n            return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };\n        },\n        toHsvString: function() {\n            var hsv = rgbToHsv(this._r, this._g, this._b);\n            var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);\n            return (this._a == 1) ?\n                \"hsv(\"  + h + \", \" + s + \"%, \" + v + \"%)\" :\n                \"hsva(\" + h + \", \" + s + \"%, \" + v + \"%, \"+ this._roundA + \")\";\n        },\n        toHsl: function() {\n            var hsl = rgbToHsl(this._r, this._g, this._b);\n            return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };\n        },\n        toHslString: function() {\n            var hsl = rgbToHsl(this._r, this._g, this._b);\n            var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);\n            return (this._a == 1) ?\n                \"hsl(\"  + h + \", \" + s + \"%, \" + l + \"%)\" :\n                \"hsla(\" + h + \", \" + s + \"%, \" + l + \"%, \"+ this._roundA + \")\";\n        },\n        toHex: function(allow3Char) {\n            return rgbToHex(this._r, this._g, this._b, allow3Char);\n        },\n        toHexString: function(allow3Char) {\n            return '#' + this.toHex(allow3Char);\n        },\n        toHex8: function(allow4Char) {\n            return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);\n        },\n        toHex8String: function(allow4Char) {\n            return '#' + this.toHex8(allow4Char);\n        },\n        toRgb: function() {\n            return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };\n        },\n        toRgbString: function() {\n            return (this._a == 1) ?\n                \"rgb(\"  + mathRound(this._r) + \", \" + mathRound(this._g) + \", \" + mathRound(this._b) + \")\" :\n                \"rgba(\" + mathRound(this._r) + \", \" + mathRound(this._g) + \", \" + mathRound(this._b) + \", \" + this._roundA + \")\";\n        },\n        toPercentageRgb: function() {\n            return { r: mathRound(bound01(this._r, 255) * 100) + \"%\", g: mathRound(bound01(this._g, 255) * 100) + \"%\", b: mathRound(bound01(this._b, 255) * 100) + \"%\", a: this._a };\n        },\n        toPercentageRgbString: function() {\n            return (this._a == 1) ?\n                \"rgb(\"  + mathRound(bound01(this._r, 255) * 100) + \"%, \" + mathRound(bound01(this._g, 255) * 100) + \"%, \" + mathRound(bound01(this._b, 255) * 100) + \"%)\" :\n                \"rgba(\" + mathRound(bound01(this._r, 255) * 100) + \"%, \" + mathRound(bound01(this._g, 255) * 100) + \"%, \" + mathRound(bound01(this._b, 255) * 100) + \"%, \" + this._roundA + \")\";\n        },\n        toName: function() {\n            if (this._a === 0) {\n                return \"transparent\";\n            }\n\n            if (this._a < 1) {\n                return false;\n            }\n\n            return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;\n        },\n        toFilter: function(secondColor) {\n            var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);\n            var secondHex8String = hex8String;\n            var gradientType = this._gradientType ? \"GradientType = 1, \" : \"\";\n\n            if (secondColor) {\n                var s = tinycolor(secondColor);\n                secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);\n            }\n\n            return \"progid:DXImageTransform.Microsoft.gradient(\"+gradientType+\"startColorstr=\"+hex8String+\",endColorstr=\"+secondHex8String+\")\";\n        },\n        toString: function(format) {\n            var formatSet = !!format;\n            format = format || this._format;\n\n            var formattedString = false;\n            var hasAlpha = this._a < 1 && this._a >= 0;\n            var needsAlphaFormat = !formatSet && hasAlpha && (format === \"hex\" || format === \"hex6\" || format === \"hex3\" || format === \"hex4\" || format === \"hex8\" || format === \"name\");\n\n            if (needsAlphaFormat) {\n                // Special case for \"transparent\", all other non-alpha formats\n                // will return rgba when there is transparency.\n                if (format === \"name\" && this._a === 0) {\n                    return this.toName();\n                }\n                return this.toRgbString();\n            }\n            if (format === \"rgb\") {\n                formattedString = this.toRgbString();\n            }\n            if (format === \"prgb\") {\n                formattedString = this.toPercentageRgbString();\n            }\n            if (format === \"hex\" || format === \"hex6\") {\n                formattedString = this.toHexString();\n            }\n            if (format === \"hex3\") {\n                formattedString = this.toHexString(true);\n            }\n            if (format === \"hex4\") {\n                formattedString = this.toHex8String(true);\n            }\n            if (format === \"hex8\") {\n                formattedString = this.toHex8String();\n            }\n            if (format === \"name\") {\n                formattedString = this.toName();\n            }\n            if (format === \"hsl\") {\n                formattedString = this.toHslString();\n            }\n            if (format === \"hsv\") {\n                formattedString = this.toHsvString();\n            }\n\n            return formattedString || this.toHexString();\n        },\n        clone: function() {\n            return tinycolor(this.toString());\n        },\n\n        _applyModification: function(fn, args) {\n            var color = fn.apply(null, [this].concat([].slice.call(args)));\n            this._r = color._r;\n            this._g = color._g;\n            this._b = color._b;\n            this.setAlpha(color._a);\n            return this;\n        },\n        lighten: function() {\n            return this._applyModification(lighten, arguments);\n        },\n        brighten: function() {\n            return this._applyModification(brighten, arguments);\n        },\n        darken: function() {\n            return this._applyModification(darken, arguments);\n        },\n        desaturate: function() {\n            return this._applyModification(desaturate, arguments);\n        },\n        saturate: function() {\n            return this._applyModification(saturate, arguments);\n        },\n        greyscale: function() {\n            return this._applyModification(greyscale, arguments);\n        },\n        spin: function() {\n            return this._applyModification(spin, arguments);\n        },\n\n        _applyCombination: function(fn, args) {\n            return fn.apply(null, [this].concat([].slice.call(args)));\n        },\n        analogous: function() {\n            return this._applyCombination(analogous, arguments);\n        },\n        complement: function() {\n            return this._applyCombination(complement, arguments);\n        },\n        monochromatic: function() {\n            return this._applyCombination(monochromatic, arguments);\n        },\n        splitcomplement: function() {\n            return this._applyCombination(splitcomplement, arguments);\n        },\n        triad: function() {\n            return this._applyCombination(triad, arguments);\n        },\n        tetrad: function() {\n            return this._applyCombination(tetrad, arguments);\n        }\n    };\n\n// If input is an object, force 1 into \"1.0\" to handle ratios properly\n// String input requires \"1.0\" as input, so 1 will be treated as 1\n    tinycolor.fromRatio = function(color, opts) {\n        if (typeof color == \"object\") {\n            var newColor = {};\n            for (var i in color) {\n                if (color.hasOwnProperty(i)) {\n                    if (i === \"a\") {\n                        newColor[i] = color[i];\n                    }\n                    else {\n                        newColor[i] = convertToPercentage(color[i]);\n                    }\n                }\n            }\n            color = newColor;\n        }\n\n        return tinycolor(color, opts);\n    };\n\n// Given a string or object, convert that input to RGB\n// Possible string inputs:\n//\n//     \"red\"\n//     \"#f00\" or \"f00\"\n//     \"#ff0000\" or \"ff0000\"\n//     \"#ff000000\" or \"ff000000\"\n//     \"rgb 255 0 0\" or \"rgb (255, 0, 0)\"\n//     \"rgb 1.0 0 0\" or \"rgb (1, 0, 0)\"\n//     \"rgba (255, 0, 0, 1)\" or \"rgba 255, 0, 0, 1\"\n//     \"rgba (1.0, 0, 0, 1)\" or \"rgba 1.0, 0, 0, 1\"\n//     \"hsl(0, 100%, 50%)\" or \"hsl 0 100% 50%\"\n//     \"hsla(0, 100%, 50%, 1)\" or \"hsla 0 100% 50%, 1\"\n//     \"hsv(0, 100%, 100%)\" or \"hsv 0 100% 100%\"\n//\n    function inputToRGB(color) {\n\n        var rgb = { r: 0, g: 0, b: 0 };\n        var a = 1;\n        var s = null;\n        var v = null;\n        var l = null;\n        var ok = false;\n        var format = false;\n\n        if (typeof color == \"string\") {\n            color = stringInputToObject(color);\n        }\n\n        if (typeof color == \"object\") {\n            if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {\n                rgb = rgbToRgb(color.r, color.g, color.b);\n                ok = true;\n                format = String(color.r).substr(-1) === \"%\" ? \"prgb\" : \"rgb\";\n            }\n            else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {\n                s = convertToPercentage(color.s);\n                v = convertToPercentage(color.v);\n                rgb = hsvToRgb(color.h, s, v);\n                ok = true;\n                format = \"hsv\";\n            }\n            else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {\n                s = convertToPercentage(color.s);\n                l = convertToPercentage(color.l);\n                rgb = hslToRgb(color.h, s, l);\n                ok = true;\n                format = \"hsl\";\n            }\n\n            if (color.hasOwnProperty(\"a\")) {\n                a = color.a;\n            }\n        }\n\n        a = boundAlpha(a);\n\n        return {\n            ok: ok,\n            format: color.format || format,\n            r: mathMin(255, mathMax(rgb.r, 0)),\n            g: mathMin(255, mathMax(rgb.g, 0)),\n            b: mathMin(255, mathMax(rgb.b, 0)),\n            a: a\n        };\n    }\n\n\n// Conversion Functions\n// --------------------\n\n// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:\n// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>\n\n// `rgbToRgb`\n// Handle bounds / percentage checking to conform to CSS color spec\n// <http://www.w3.org/TR/css3-color/>\n// *Assumes:* r, g, b in [0, 255] or [0, 1]\n// *Returns:* { r, g, b } in [0, 255]\n    function rgbToRgb(r, g, b){\n        return {\n            r: bound01(r, 255) * 255,\n            g: bound01(g, 255) * 255,\n            b: bound01(b, 255) * 255\n        };\n    }\n\n// `rgbToHsl`\n// Converts an RGB color value to HSL.\n// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]\n// *Returns:* { h, s, l } in [0,1]\n    function rgbToHsl(r, g, b) {\n\n        r = bound01(r, 255);\n        g = bound01(g, 255);\n        b = bound01(b, 255);\n\n        var max = mathMax(r, g, b), min = mathMin(r, g, b);\n        var h, s, l = (max + min) / 2;\n\n        if(max == min) {\n            h = s = 0; // achromatic\n        }\n        else {\n            var d = max - min;\n            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n            switch(max) {\n                case r: h = (g - b) / d + (g < b ? 6 : 0); break;\n                case g: h = (b - r) / d + 2; break;\n                case b: h = (r - g) / d + 4; break;\n            }\n\n            h /= 6;\n        }\n\n        return { h: h, s: s, l: l };\n    }\n\n// `hslToRgb`\n// Converts an HSL color value to RGB.\n// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]\n// *Returns:* { r, g, b } in the set [0, 255]\n    function hslToRgb(h, s, l) {\n        var r, g, b;\n\n        h = bound01(h, 360);\n        s = bound01(s, 100);\n        l = bound01(l, 100);\n\n        function hue2rgb(p, q, t) {\n            if(t < 0) t += 1;\n            if(t > 1) t -= 1;\n            if(t < 1/6) return p + (q - p) * 6 * t;\n            if(t < 1/2) return q;\n            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;\n            return p;\n        }\n\n        if(s === 0) {\n            r = g = b = l; // achromatic\n        }\n        else {\n            var q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n            var p = 2 * l - q;\n            r = hue2rgb(p, q, h + 1/3);\n            g = hue2rgb(p, q, h);\n            b = hue2rgb(p, q, h - 1/3);\n        }\n\n        return { r: r * 255, g: g * 255, b: b * 255 };\n    }\n\n// `rgbToHsv`\n// Converts an RGB color value to HSV\n// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]\n// *Returns:* { h, s, v } in [0,1]\n    function rgbToHsv(r, g, b) {\n\n        r = bound01(r, 255);\n        g = bound01(g, 255);\n        b = bound01(b, 255);\n\n        var max = mathMax(r, g, b), min = mathMin(r, g, b);\n        var h, s, v = max;\n\n        var d = max - min;\n        s = max === 0 ? 0 : d / max;\n\n        if(max == min) {\n            h = 0; // achromatic\n        }\n        else {\n            switch(max) {\n                case r: h = (g - b) / d + (g < b ? 6 : 0); break;\n                case g: h = (b - r) / d + 2; break;\n                case b: h = (r - g) / d + 4; break;\n            }\n            h /= 6;\n        }\n        return { h: h, s: s, v: v };\n    }\n\n// `hsvToRgb`\n// Converts an HSV color value to RGB.\n// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]\n// *Returns:* { r, g, b } in the set [0, 255]\n    function hsvToRgb(h, s, v) {\n\n        h = bound01(h, 360) * 6;\n        s = bound01(s, 100);\n        v = bound01(v, 100);\n\n        var i = Math.floor(h),\n            f = h - i,\n            p = v * (1 - s),\n            q = v * (1 - f * s),\n            t = v * (1 - (1 - f) * s),\n            mod = i % 6,\n            r = [v, q, p, p, t, v][mod],\n            g = [t, v, v, q, p, p][mod],\n            b = [p, p, t, v, v, q][mod];\n\n        return { r: r * 255, g: g * 255, b: b * 255 };\n    }\n\n// `rgbToHex`\n// Converts an RGB color to hex\n// Assumes r, g, and b are contained in the set [0, 255]\n// Returns a 3 or 6 character hex\n    function rgbToHex(r, g, b, allow3Char) {\n\n        var hex = [\n            pad2(mathRound(r).toString(16)),\n            pad2(mathRound(g).toString(16)),\n            pad2(mathRound(b).toString(16))\n        ];\n\n        // Return a 3 character hex if possible\n        if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {\n            return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);\n        }\n\n        return hex.join(\"\");\n    }\n\n// `rgbaToHex`\n// Converts an RGBA color plus alpha transparency to hex\n// Assumes r, g, b are contained in the set [0, 255] and\n// a in [0, 1]. Returns a 4 or 8 character rgba hex\n    function rgbaToHex(r, g, b, a, allow4Char) {\n\n        var hex = [\n            pad2(mathRound(r).toString(16)),\n            pad2(mathRound(g).toString(16)),\n            pad2(mathRound(b).toString(16)),\n            pad2(convertDecimalToHex(a))\n        ];\n\n        // Return a 4 character hex if possible\n        if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) {\n            return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);\n        }\n\n        return hex.join(\"\");\n    }\n\n// `rgbaToArgbHex`\n// Converts an RGBA color to an ARGB Hex8 string\n// Rarely used, but required for \"toFilter()\"\n    function rgbaToArgbHex(r, g, b, a) {\n\n        var hex = [\n            pad2(convertDecimalToHex(a)),\n            pad2(mathRound(r).toString(16)),\n            pad2(mathRound(g).toString(16)),\n            pad2(mathRound(b).toString(16))\n        ];\n\n        return hex.join(\"\");\n    }\n\n// `equals`\n// Can be called with any tinycolor input\n    tinycolor.equals = function (color1, color2) {\n        if (!color1 || !color2) { return false; }\n        return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();\n    };\n\n    tinycolor.random = function() {\n        return tinycolor.fromRatio({\n            r: mathRandom(),\n            g: mathRandom(),\n            b: mathRandom()\n        });\n    };\n\n\n// Modification Functions\n// ----------------------\n// Thanks to less.js for some of the basics here\n// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>\n\n    function desaturate(color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var hsl = tinycolor(color).toHsl();\n        hsl.s -= amount / 100;\n        hsl.s = clamp01(hsl.s);\n        return tinycolor(hsl);\n    }\n\n    function saturate(color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var hsl = tinycolor(color).toHsl();\n        hsl.s += amount / 100;\n        hsl.s = clamp01(hsl.s);\n        return tinycolor(hsl);\n    }\n\n    function greyscale(color) {\n        return tinycolor(color).desaturate(100);\n    }\n\n    function lighten (color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var hsl = tinycolor(color).toHsl();\n        hsl.l += amount / 100;\n        hsl.l = clamp01(hsl.l);\n        return tinycolor(hsl);\n    }\n\n    function brighten(color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var rgb = tinycolor(color).toRgb();\n        rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));\n        rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));\n        rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));\n        return tinycolor(rgb);\n    }\n\n    function darken (color, amount) {\n        amount = (amount === 0) ? 0 : (amount || 10);\n        var hsl = tinycolor(color).toHsl();\n        hsl.l -= amount / 100;\n        hsl.l = clamp01(hsl.l);\n        return tinycolor(hsl);\n    }\n\n// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.\n// Values outside of this range will be wrapped into this range.\n    function spin(color, amount) {\n        var hsl = tinycolor(color).toHsl();\n        var hue = (hsl.h + amount) % 360;\n        hsl.h = hue < 0 ? 360 + hue : hue;\n        return tinycolor(hsl);\n    }\n\n// Combination Functions\n// ---------------------\n// Thanks to jQuery xColor for some of the ideas behind these\n// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>\n\n    function complement(color) {\n        var hsl = tinycolor(color).toHsl();\n        hsl.h = (hsl.h + 180) % 360;\n        return tinycolor(hsl);\n    }\n\n    function triad(color) {\n        var hsl = tinycolor(color).toHsl();\n        var h = hsl.h;\n        return [\n            tinycolor(color),\n            tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),\n            tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })\n        ];\n    }\n\n    function tetrad(color) {\n        var hsl = tinycolor(color).toHsl();\n        var h = hsl.h;\n        return [\n            tinycolor(color),\n            tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),\n            tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),\n            tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })\n        ];\n    }\n\n    function splitcomplement(color) {\n        var hsl = tinycolor(color).toHsl();\n        var h = hsl.h;\n        return [\n            tinycolor(color),\n            tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),\n            tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})\n        ];\n    }\n\n    function analogous(color, results, slices) {\n        results = results || 6;\n        slices = slices || 30;\n\n        var hsl = tinycolor(color).toHsl();\n        var part = 360 / slices;\n        var ret = [tinycolor(color)];\n\n        for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {\n            hsl.h = (hsl.h + part) % 360;\n            ret.push(tinycolor(hsl));\n        }\n        return ret;\n    }\n\n    function monochromatic(color, results) {\n        results = results || 6;\n        var hsv = tinycolor(color).toHsv();\n        var h = hsv.h, s = hsv.s, v = hsv.v;\n        var ret = [];\n        var modification = 1 / results;\n\n        while (results--) {\n            ret.push(tinycolor({ h: h, s: s, v: v}));\n            v = (v + modification) % 1;\n        }\n\n        return ret;\n    }\n\n// Utility Functions\n// ---------------------\n\n    tinycolor.mix = function(color1, color2, amount) {\n        amount = (amount === 0) ? 0 : (amount || 50);\n\n        var rgb1 = tinycolor(color1).toRgb();\n        var rgb2 = tinycolor(color2).toRgb();\n\n        var p = amount / 100;\n\n        var rgba = {\n            r: ((rgb2.r - rgb1.r) * p) + rgb1.r,\n            g: ((rgb2.g - rgb1.g) * p) + rgb1.g,\n            b: ((rgb2.b - rgb1.b) * p) + rgb1.b,\n            a: ((rgb2.a - rgb1.a) * p) + rgb1.a\n        };\n\n        return tinycolor(rgba);\n    };\n\n\n// Readability Functions\n// ---------------------\n// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)\n\n// `contrast`\n// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)\n    tinycolor.readability = function(color1, color2) {\n        var c1 = tinycolor(color1);\n        var c2 = tinycolor(color2);\n        return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);\n    };\n\n// `isReadable`\n// Ensure that foreground and background color combinations meet WCAG2 guidelines.\n// The third argument is an optional Object.\n//      the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';\n//      the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.\n// If the entire object is absent, isReadable defaults to {level:\"AA\",size:\"small\"}.\n\n// *Example*\n//    tinycolor.isReadable(\"#000\", \"#111\") => false\n//    tinycolor.isReadable(\"#000\", \"#111\",{level:\"AA\",size:\"large\"}) => false\n    tinycolor.isReadable = function(color1, color2, wcag2) {\n        var readability = tinycolor.readability(color1, color2);\n        var wcag2Parms, out;\n\n        out = false;\n\n        wcag2Parms = validateWCAG2Parms(wcag2);\n        switch (wcag2Parms.level + wcag2Parms.size) {\n            case \"AAsmall\":\n            case \"AAAlarge\":\n                out = readability >= 4.5;\n                break;\n            case \"AAlarge\":\n                out = readability >= 3;\n                break;\n            case \"AAAsmall\":\n                out = readability >= 7;\n                break;\n        }\n        return out;\n\n    };\n\n// `mostReadable`\n// Given a base color and a list of possible foreground or background\n// colors for that base, returns the most readable color.\n// Optionally returns Black or White if the most readable color is unreadable.\n// *Example*\n//    tinycolor.mostReadable(tinycolor.mostReadable(\"#123\", [\"#124\", \"#125\"],{includeFallbackColors:false}).toHexString(); // \"#112255\"\n//    tinycolor.mostReadable(tinycolor.mostReadable(\"#123\", [\"#124\", \"#125\"],{includeFallbackColors:true}).toHexString();  // \"#ffffff\"\n//    tinycolor.mostReadable(\"#a8015a\", [\"#faf3f3\"],{includeFallbackColors:true,level:\"AAA\",size:\"large\"}).toHexString(); // \"#faf3f3\"\n//    tinycolor.mostReadable(\"#a8015a\", [\"#faf3f3\"],{includeFallbackColors:true,level:\"AAA\",size:\"small\"}).toHexString(); // \"#ffffff\"\n    tinycolor.mostReadable = function(baseColor, colorList, args) {\n        var bestColor = null;\n        var bestScore = 0;\n        var readability;\n        var includeFallbackColors, level, size ;\n        args = args || {};\n        includeFallbackColors = args.includeFallbackColors ;\n        level = args.level;\n        size = args.size;\n\n        for (var i= 0; i < colorList.length ; i++) {\n            readability = tinycolor.readability(baseColor, colorList[i]);\n            if (readability > bestScore) {\n                bestScore = readability;\n                bestColor = tinycolor(colorList[i]);\n            }\n        }\n\n        if (tinycolor.isReadable(baseColor, bestColor, {\"level\":level,\"size\":size}) || !includeFallbackColors) {\n            return bestColor;\n        }\n        else {\n            args.includeFallbackColors=false;\n            return tinycolor.mostReadable(baseColor,[\"#fff\", \"#000\"],args);\n        }\n    };\n\n\n// Big List of Colors\n// ------------------\n// <http://www.w3.org/TR/css3-color/#svg-color>\n    var names = tinycolor.names = {\n        aliceblue: \"f0f8ff\",\n        antiquewhite: \"faebd7\",\n        aqua: \"0ff\",\n        aquamarine: \"7fffd4\",\n        azure: \"f0ffff\",\n        beige: \"f5f5dc\",\n        bisque: \"ffe4c4\",\n        black: \"000\",\n        blanchedalmond: \"ffebcd\",\n        blue: \"00f\",\n        blueviolet: \"8a2be2\",\n        brown: \"a52a2a\",\n        burlywood: \"deb887\",\n        burntsienna: \"ea7e5d\",\n        cadetblue: \"5f9ea0\",\n        chartreuse: \"7fff00\",\n        chocolate: \"d2691e\",\n        coral: \"ff7f50\",\n        cornflowerblue: \"6495ed\",\n        cornsilk: \"fff8dc\",\n        crimson: \"dc143c\",\n        cyan: \"0ff\",\n        darkblue: \"00008b\",\n        darkcyan: \"008b8b\",\n        darkgoldenrod: \"b8860b\",\n        darkgray: \"a9a9a9\",\n        darkgreen: \"006400\",\n        darkgrey: \"a9a9a9\",\n        darkkhaki: \"bdb76b\",\n        darkmagenta: \"8b008b\",\n        darkolivegreen: \"556b2f\",\n        darkorange: \"ff8c00\",\n        darkorchid: \"9932cc\",\n        darkred: \"8b0000\",\n        darksalmon: \"e9967a\",\n        darkseagreen: \"8fbc8f\",\n        darkslateblue: \"483d8b\",\n        darkslategray: \"2f4f4f\",\n        darkslategrey: \"2f4f4f\",\n        darkturquoise: \"00ced1\",\n        darkviolet: \"9400d3\",\n        deeppink: \"ff1493\",\n        deepskyblue: \"00bfff\",\n        dimgray: \"696969\",\n        dimgrey: \"696969\",\n        dodgerblue: \"1e90ff\",\n        firebrick: \"b22222\",\n        floralwhite: \"fffaf0\",\n        forestgreen: \"228b22\",\n        fuchsia: \"f0f\",\n        gainsboro: \"dcdcdc\",\n        ghostwhite: \"f8f8ff\",\n        gold: \"ffd700\",\n        goldenrod: \"daa520\",\n        gray: \"808080\",\n        green: \"008000\",\n        greenyellow: \"adff2f\",\n        grey: \"808080\",\n        honeydew: \"f0fff0\",\n        hotpink: \"ff69b4\",\n        indianred: \"cd5c5c\",\n        indigo: \"4b0082\",\n        ivory: \"fffff0\",\n        khaki: \"f0e68c\",\n        lavender: \"e6e6fa\",\n        lavenderblush: \"fff0f5\",\n        lawngreen: \"7cfc00\",\n        lemonchiffon: \"fffacd\",\n        lightblue: \"add8e6\",\n        lightcoral: \"f08080\",\n        lightcyan: \"e0ffff\",\n        lightgoldenrodyellow: \"fafad2\",\n        lightgray: \"d3d3d3\",\n        lightgreen: \"90ee90\",\n        lightgrey: \"d3d3d3\",\n        lightpink: \"ffb6c1\",\n        lightsalmon: \"ffa07a\",\n        lightseagreen: \"20b2aa\",\n        lightskyblue: \"87cefa\",\n        lightslategray: \"789\",\n        lightslategrey: \"789\",\n        lightsteelblue: \"b0c4de\",\n        lightyellow: \"ffffe0\",\n        lime: \"0f0\",\n        limegreen: \"32cd32\",\n        linen: \"faf0e6\",\n        magenta: \"f0f\",\n        maroon: \"800000\",\n        mediumaquamarine: \"66cdaa\",\n        mediumblue: \"0000cd\",\n        mediumorchid: \"ba55d3\",\n        mediumpurple: \"9370db\",\n        mediumseagreen: \"3cb371\",\n        mediumslateblue: \"7b68ee\",\n        mediumspringgreen: \"00fa9a\",\n        mediumturquoise: \"48d1cc\",\n        mediumvioletred: \"c71585\",\n        midnightblue: \"191970\",\n        mintcream: \"f5fffa\",\n        mistyrose: \"ffe4e1\",\n        moccasin: \"ffe4b5\",\n        navajowhite: \"ffdead\",\n        navy: \"000080\",\n        oldlace: \"fdf5e6\",\n        olive: \"808000\",\n        olivedrab: \"6b8e23\",\n        orange: \"ffa500\",\n        orangered: \"ff4500\",\n        orchid: \"da70d6\",\n        palegoldenrod: \"eee8aa\",\n        palegreen: \"98fb98\",\n        paleturquoise: \"afeeee\",\n        palevioletred: \"db7093\",\n        papayawhip: \"ffefd5\",\n        peachpuff: \"ffdab9\",\n        peru: \"cd853f\",\n        pink: \"ffc0cb\",\n        plum: \"dda0dd\",\n        powderblue: \"b0e0e6\",\n        purple: \"800080\",\n        rebeccapurple: \"663399\",\n        red: \"f00\",\n        rosybrown: \"bc8f8f\",\n        royalblue: \"4169e1\",\n        saddlebrown: \"8b4513\",\n        salmon: \"fa8072\",\n        sandybrown: \"f4a460\",\n        seagreen: \"2e8b57\",\n        seashell: \"fff5ee\",\n        sienna: \"a0522d\",\n        silver: \"c0c0c0\",\n        skyblue: \"87ceeb\",\n        slateblue: \"6a5acd\",\n        slategray: \"708090\",\n        slategrey: \"708090\",\n        snow: \"fffafa\",\n        springgreen: \"00ff7f\",\n        steelblue: \"4682b4\",\n        tan: \"d2b48c\",\n        teal: \"008080\",\n        thistle: \"d8bfd8\",\n        tomato: \"ff6347\",\n        turquoise: \"40e0d0\",\n        violet: \"ee82ee\",\n        wheat: \"f5deb3\",\n        white: \"fff\",\n        whitesmoke: \"f5f5f5\",\n        yellow: \"ff0\",\n        yellowgreen: \"9acd32\"\n    };\n\n// Make it easy to access colors via `hexNames[hex]`\n    var hexNames = tinycolor.hexNames = flip(names);\n\n\n// Utilities\n// ---------\n\n// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`\n    function flip(o) {\n        var flipped = { };\n        for (var i in o) {\n            if (o.hasOwnProperty(i)) {\n                flipped[o[i]] = i;\n            }\n        }\n        return flipped;\n    }\n\n// Return a valid alpha value [0,1] with all invalid values being set to 1\n    function boundAlpha(a) {\n        a = parseFloat(a);\n\n        if (isNaN(a) || a < 0 || a > 1) {\n            a = 1;\n        }\n\n        return a;\n    }\n\n// Take input from [0, n] and return it as [0, 1]\n    function bound01(n, max) {\n        if (isOnePointZero(n)) { n = \"100%\"; }\n\n        var processPercent = isPercentage(n);\n        n = mathMin(max, mathMax(0, parseFloat(n)));\n\n        // Automatically convert percentage into number\n        if (processPercent) {\n            n = parseInt(n * max, 10) / 100;\n        }\n\n        // Handle floating point rounding errors\n        if ((Math.abs(n - max) < 0.000001)) {\n            return 1;\n        }\n\n        // Convert into [0, 1] range if it isn't already\n        return (n % max) / parseFloat(max);\n    }\n\n// Force a number between 0 and 1\n    function clamp01(val) {\n        return mathMin(1, mathMax(0, val));\n    }\n\n// Parse a base-16 hex value into a base-10 integer\n    function parseIntFromHex(val) {\n        return parseInt(val, 16);\n    }\n\n// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1\n// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>\n    function isOnePointZero(n) {\n        return typeof n == \"string\" && n.indexOf('.') != -1 && parseFloat(n) === 1;\n    }\n\n// Check to see if string passed in is a percentage\n    function isPercentage(n) {\n        return typeof n === \"string\" && n.indexOf('%') != -1;\n    }\n\n// Force a hex value to have 2 characters\n    function pad2(c) {\n        return c.length == 1 ? '0' + c : '' + c;\n    }\n\n// Replace a decimal with it's percentage value\n    function convertToPercentage(n) {\n        if (n <= 1) {\n            n = (n * 100) + \"%\";\n        }\n\n        return n;\n    }\n\n// Converts a decimal to a hex value\n    function convertDecimalToHex(d) {\n        return Math.round(parseFloat(d) * 255).toString(16);\n    }\n// Converts a hex value to a decimal\n    function convertHexToDecimal(h) {\n        return (parseIntFromHex(h) / 255);\n    }\n\n    var matchers = (function() {\n\n        // <http://www.w3.org/TR/css3-values/#integers>\n        var CSS_INTEGER = \"[-\\\\+]?\\\\d+%?\";\n\n        // <http://www.w3.org/TR/css3-values/#number-value>\n        var CSS_NUMBER = \"[-\\\\+]?\\\\d*\\\\.\\\\d+%?\";\n\n        // Allow positive/negative integer/number.  Don't capture the either/or, just the entire outcome.\n        var CSS_UNIT = \"(?:\" + CSS_NUMBER + \")|(?:\" + CSS_INTEGER + \")\";\n\n        // Actual matching.\n        // Parentheses and commas are optional, but not required.\n        // Whitespace can take the place of commas or opening paren\n        var PERMISSIVE_MATCH3 = \"[\\\\s|\\\\(]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")\\\\s*\\\\)?\";\n        var PERMISSIVE_MATCH4 = \"[\\\\s|\\\\(]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")[,|\\\\s]+(\" + CSS_UNIT + \")\\\\s*\\\\)?\";\n\n        return {\n            CSS_UNIT: new RegExp(CSS_UNIT),\n            rgb: new RegExp(\"rgb\" + PERMISSIVE_MATCH3),\n            rgba: new RegExp(\"rgba\" + PERMISSIVE_MATCH4),\n            hsl: new RegExp(\"hsl\" + PERMISSIVE_MATCH3),\n            hsla: new RegExp(\"hsla\" + PERMISSIVE_MATCH4),\n            hsv: new RegExp(\"hsv\" + PERMISSIVE_MATCH3),\n            hsva: new RegExp(\"hsva\" + PERMISSIVE_MATCH4),\n            hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,\n            hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,\n            hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,\n            hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/\n        };\n    })();\n\n// `isValidCSSUnit`\n// Take in a single string / number and check to see if it looks like a CSS unit\n// (see `matchers` above for definition).\n    function isValidCSSUnit(color) {\n        return !!matchers.CSS_UNIT.exec(color);\n    }\n\n// `stringInputToObject`\n// Permissive string parsing.  Take in a number of formats, and output an object\n// based on detected format.  Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`\n    function stringInputToObject(color) {\n\n        color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();\n        var named = false;\n        if (names[color]) {\n            color = names[color];\n            named = true;\n        }\n        else if (color == 'transparent') {\n            return { r: 0, g: 0, b: 0, a: 0, format: \"name\" };\n        }\n\n        // Try to match string input using regular expressions.\n        // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]\n        // Just return an object and let the conversion functions handle that.\n        // This way the result will be the same whether the tinycolor is initialized with string or object.\n        var match;\n        if ((match = matchers.rgb.exec(color))) {\n            return { r: match[1], g: match[2], b: match[3] };\n        }\n        if ((match = matchers.rgba.exec(color))) {\n            return { r: match[1], g: match[2], b: match[3], a: match[4] };\n        }\n        if ((match = matchers.hsl.exec(color))) {\n            return { h: match[1], s: match[2], l: match[3] };\n        }\n        if ((match = matchers.hsla.exec(color))) {\n            return { h: match[1], s: match[2], l: match[3], a: match[4] };\n        }\n        if ((match = matchers.hsv.exec(color))) {\n            return { h: match[1], s: match[2], v: match[3] };\n        }\n        if ((match = matchers.hsva.exec(color))) {\n            return { h: match[1], s: match[2], v: match[3], a: match[4] };\n        }\n        if ((match = matchers.hex8.exec(color))) {\n            return {\n                r: parseIntFromHex(match[1]),\n                g: parseIntFromHex(match[2]),\n                b: parseIntFromHex(match[3]),\n                a: convertHexToDecimal(match[4]),\n                format: named ? \"name\" : \"hex8\"\n            };\n        }\n        if ((match = matchers.hex6.exec(color))) {\n            return {\n                r: parseIntFromHex(match[1]),\n                g: parseIntFromHex(match[2]),\n                b: parseIntFromHex(match[3]),\n                format: named ? \"name\" : \"hex\"\n            };\n        }\n        if ((match = matchers.hex4.exec(color))) {\n            return {\n                r: parseIntFromHex(match[1] + '' + match[1]),\n                g: parseIntFromHex(match[2] + '' + match[2]),\n                b: parseIntFromHex(match[3] + '' + match[3]),\n                a: convertHexToDecimal(match[4] + '' + match[4]),\n                format: named ? \"name\" : \"hex8\"\n            };\n        }\n        if ((match = matchers.hex3.exec(color))) {\n            return {\n                r: parseIntFromHex(match[1] + '' + match[1]),\n                g: parseIntFromHex(match[2] + '' + match[2]),\n                b: parseIntFromHex(match[3] + '' + match[3]),\n                format: named ? \"name\" : \"hex\"\n            };\n        }\n\n        return false;\n    }\n\n    function validateWCAG2Parms(parms) {\n        // return valid WCAG2 parms for isReadable.\n        // If input parms are invalid, return {\"level\":\"AA\", \"size\":\"small\"}\n        var level, size;\n        parms = parms || {\"level\":\"AA\", \"size\":\"small\"};\n        level = (parms.level || \"AA\").toUpperCase();\n        size = (parms.size || \"small\").toLowerCase();\n        if (level !== \"AA\" && level !== \"AAA\") {\n            level = \"AA\";\n        }\n        if (size !== \"small\" && size !== \"large\") {\n            size = \"small\";\n        }\n        return {\"level\":level, \"size\":size};\n    }\n\n// Node: Export function\n    if (typeof module !== \"undefined\" && module.exports) {\n        module.exports = tinycolor;\n    }\n// AMD/requirejs: Define the module\n    else if (typeof define === 'function' && define.amd) {\n        define(function () {return tinycolor;});\n    }\n// Browser: Expose to window\n    else {\n        window.tinycolor = tinycolor;\n    }\n\n})(Math);\n","Magento_Bundle/js/bundle-product.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/*global FORM_KEY*/\n/*global bSelection*/\n/**\n * @api\n */\ndefine([\n    'jquery',\n    'Magento_Catalog/js/product/weight-handler',\n    'Magento_Ui/js/modal/modal',\n    'jquery/ui',\n    'mage/translate',\n    'Magento_Theme/js/sortable',\n    'prototype'\n], function ($, weightHandler) {\n    'use strict';\n\n    $.widget('mage.bundleProduct', {\n        /** @inheritdoc */\n        _create: function () {\n            this._initOptionBoxes()\n                ._initSortableSelections()\n                ._bindCheckboxHandlers()\n                ._initCheckboxState()\n                ._bindAddSelectionDialog()\n                ._hideProductTypeSwitcher();\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _initOptionBoxes: function () {\n            var syncOptionTitle;\n\n            this.element.sortable({\n                axis: 'y',\n                handle: '[data-role=draggable-handle]',\n                items: '.option-box',\n                update: this._updateOptionBoxPositions,\n                tolerance: 'pointer'\n            });\n\n            /**\n             * @param {jQuery.Event} event\n             */\n            syncOptionTitle = function (event) {\n                var originalValue = $(event.target).attr('data-original-value'),\n                    currentValue = $(event.target).val(),\n                    optionBoxTitle = $('.title > span', $(event.target).closest('.option-box')),\n                    newOptionTitle = $.mage.__('New Option');\n\n                optionBoxTitle.text(currentValue === '' && !originalValue.length ? newOptionTitle : currentValue);\n            };\n            this._on({\n                'change .field-option-title input[name$=\"[title]\"]': syncOptionTitle,\n                'keyup .field-option-title input[name$=\"[title]\"]': syncOptionTitle,\n                'paste .field-option-title input[name$=\"[title]\"]': syncOptionTitle\n            });\n\n            return this;\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _initSortableSelections: function () {\n            this.element.find('.option-box .form-list tbody').sortable({\n                axis: 'y',\n                handle: '[data-role=draggable-handle]',\n\n                /**\n                 * @param {jQuery.Event} event\n                 * @param {jQuery} ui\n                 * @return {jQuery}\n                 */\n                helper: function (event, ui) {\n                    ui.children().each(function () {\n                        $(this).width($(this).width());\n                    });\n\n                    return ui;\n                },\n                update: this._updateSelectionsPositions,\n                tolerance: 'pointer'\n            });\n\n            return this;\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _initCheckboxState: function () {\n            this.element.find('.is-required').each(function () {\n                $(this).prop('checked', $(this).closest('.option-box').find('[name$=\"[required]\"]').val() > 0);\n            });\n\n            this.element.find('.is-user-defined-qty').each(function () {\n                $(this).prop('checked', $(this).closest('.qty-box').find('.select').val() > 0);\n            });\n\n            return this;\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _bindAddSelectionDialog: function () {\n            var widget = this;\n\n            this._on({\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'click .add-selection': function (event) {\n                    var $optionBox = $(event.target).closest('.option-box'),\n                        $selectionGrid = $optionBox.find('.selection-search').clone(),\n                        optionIndex = $optionBox.attr('id').replace('bundle_option_', ''),\n                        productIds = [],\n                        productSkus = [],\n                        selectedProductList = {};\n\n                    $optionBox.find('[name$=\"[product_id]\"]').each(function () {\n                        if (!$(this).closest('tr').find('[name$=\"[delete]\"]').val()) {\n                            productIds.push($(this).val());\n                            productSkus.push($(this).closest('tr').find('.col-sku').text());\n                        }\n                    });\n\n                    bSelection.gridSelection.set(optionIndex, $H({}));\n                    bSelection.gridRemoval = $H({});\n                    bSelection.gridSelectedProductSkus = productSkus;\n\n                    $selectionGrid.on('contentUpdated', bSelection.gridUpdateCallback);\n                    $selectionGrid.on('change', '.col-id input', function () {\n                        var tr = $(this).closest('tr');\n\n                        if ($(this).is(':checked')) {\n                            selectedProductList[$(this).val()] = {\n                                name: tr.find('.col-name').html().trim(),\n                                sku: tr.find('.col-sku').html().trim(),\n                                'product_id': $(this).val(),\n                                'option_id': $('bundle_selection_id_' + optionIndex).val(),\n                                'selection_price_value': 0,\n                                'selection_qty': 1\n                            };\n                        } else {\n                            delete selectedProductList[$(this).val()];\n                        }\n                    });\n\n                    $selectionGrid.modal({\n                        title: $optionBox.find('input[name$=\"[title]\"]').val() === '' ?\n                            $.mage.__('Add Products to New Option') :\n                            $.mage.__('Add Products to Option \"%1\"').replace(\n                                '%1',\n                                $('<div>').text($optionBox.find('input[name$=\"[title]\"]').val()).html()\n                            ),\n                        modalClass: 'bundle',\n                        type: 'slide',\n\n                        /**\n                         * @param {jQuery.Event} e\n                         * @param {Object} modalWindow\n                         */\n                        closed: function (e, modalWindow) {\n                            modalWindow.modal.remove();\n                        },\n                        buttons: [{\n                            text: $.mage.__('Add Selected Products'),\n                            'class': 'action-primary action-add',\n\n                            /** Click action. */\n                            click: function () {\n                                $.each(selectedProductList, function () {\n                                    window.bSelection.addRow(optionIndex, this);\n                                });\n                                bSelection.gridRemoval.each(function (pair) {\n                                    $optionBox.find('.col-sku').filter(function () {\n                                        let text = $(this).text();\n\n                                        return text.trim() === pair.key; // find row by SKU\n                                    }).closest('tr').find('button.delete').trigger('click');\n                                });\n                                widget.refreshSortableElements();\n                                widget._updateSelectionsPositions.apply(widget.element);\n                                $selectionGrid.modal('closeModal');\n                            }\n                        }]\n                    });\n                    $.ajax({\n                        url: bSelection.selectionSearchUrl,\n                        dataType: 'html',\n                        data: {\n                            index: optionIndex,\n                            products: productIds,\n                            'selected_products': productIds,\n                            'form_key': FORM_KEY\n                        },\n\n                        /**\n                         * @param {*} data\n                         */\n                        success: function (data) {\n                            $selectionGrid.html(data).modal('openModal');\n                        },\n                        context: $('body'),\n                        showLoader: true\n                    });\n                }\n            });\n\n            return this;\n        },\n\n        /**\n         * @private\n         */\n        _hideProductTypeSwitcher: function () {\n            weightHandler.hideWeightSwitcher();\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _bindCheckboxHandlers: function () {\n            this._on({\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'change .is-required': function (event) {\n                    var $this = $(event.target);\n\n                    $this.closest('.option-box').find('[name$=\"[required]\"]').val($this.is(':checked') ? 1 : 0);\n                },\n\n                /**\n                 * @param {jQuery.Event} event\n                 */\n                'change .is-user-defined-qty': function (event) {\n                    var $this = $(event.target);\n\n                    $this.closest('.qty-box').find('.select').val($this.is(':checked') ? 1 : 0);\n                }\n            });\n\n            return this;\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _updateOptionBoxPositions: function () {\n            $(this).find('[name^=bundle_options][name$=\"[position]\"]').each(function (index) {\n                $(this).val(index);\n            });\n\n            return this;\n        },\n\n        /**\n         * @return {Object}\n         * @private\n         */\n        _updateSelectionsPositions: function () {\n            $(this).find('[name^=bundle_selections][name$=\"[position]\"]').each(function (index) {\n                $(this).val(index);\n            });\n\n            return this;\n        },\n\n        /**\n         *\n         * @return {Object}\n         */\n        refreshSortableElements: function () {\n            this.element.sortable('refresh');\n            this._updateOptionBoxPositions.apply(this.element);\n            this._initSortableSelections();\n            this._initCheckboxState();\n\n            return this;\n        }\n    });\n\n});\n","Magento_Bundle/js/bundle-type-handler.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'Magento_Catalog/catalog/type-events',\n    'Magento_Catalog/js/product/weight-handler'\n], function ($, productType, weight) {\n    'use strict';\n\n    return {\n\n        /**\n         * Constructor component\n         */\n        'Magento_Bundle/js/bundle-type-handler': function () {\n            this.bindAll();\n            this._initType();\n        },\n\n        /**\n         * Bind all\n         */\n        bindAll: function () {\n            $(document).on('changeTypeProduct', this._initType.bind(this));\n        },\n\n        /**\n         * Init type\n         * @private\n         */\n        _initType: function () {\n            if (\n                productType.type.init === 'bundle' &&\n                productType.type.current !== 'bundle' &&\n                !weight.isLocked()\n            ) {\n                weight.switchWeight();\n            }\n        }\n    };\n});\n","Magento_Bundle/js/price-bundle.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @api\n */\ndefine([\n    'jquery',\n    'underscore',\n    'mage/template',\n    'priceUtils',\n    'priceBox'\n], function ($, _, mageTemplate, utils) {\n    'use strict';\n\n    var globalOptions = {\n        optionConfig: null,\n        productBundleSelector: 'input.bundle.option, select.bundle.option, textarea.bundle.option',\n        qtyFieldSelector: 'input.qty',\n        priceBoxSelector: '.price-box',\n        optionHandlers: {},\n        optionTemplate: '<%- data.label %>' +\n            '<% if (data.finalPrice.value) { %>' +\n            ' +<%- data.finalPrice.formatted %>' +\n            '<% } %>',\n        controlContainer: 'dd', // should be eliminated\n        priceFormat: {},\n        isFixedPrice: false,\n        optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role=\"selection-tier-prices\"]',\n        isOptionsInitialized: false\n    };\n\n    $.widget('mage.priceBundle', {\n        options: globalOptions,\n\n        /**\n         * @private\n         */\n        _init: function initPriceBundle() {\n            var form = this.element,\n                options = $(this.options.productBundleSelector, form);\n\n            options.trigger('change');\n        },\n\n        /**\n         * @private\n         */\n        _create: function createPriceBundle() {\n            var form = this.element,\n                options = $(this.options.productBundleSelector, form),\n                priceBox = $(this.options.priceBoxSelector, form),\n                qty = $(this.options.qtyFieldSelector, form);\n\n            this._updatePriceBox();\n            priceBox.on('price-box-initialized', this._updatePriceBox.bind(this));\n            options.on('change', this._onBundleOptionChanged.bind(this));\n            qty.on('change', this._onQtyFieldChanged.bind(this));\n        },\n\n        /**\n         * Update price box config with bundle option prices\n         * @private\n         */\n        _updatePriceBox: function () {\n            var form = this.element,\n                options = $(this.options.productBundleSelector, form),\n                priceBox = $(this.options.priceBoxSelector, form);\n\n            if (!this.options.isOptionsInitialized) {\n                if (priceBox.data('magePriceBox') &&\n                    priceBox.priceBox('option') &&\n                    priceBox.priceBox('option').priceConfig\n                ) {\n                    if (priceBox.priceBox('option').priceConfig.optionTemplate) { //eslint-disable-line max-depth\n                        this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate);\n                    }\n                    this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat);\n                    priceBox.priceBox('setDefault', this.options.optionConfig.prices);\n                    this.options.isOptionsInitialized = true;\n                }\n                this._applyOptionNodeFix(options);\n            }\n\n            return this;\n        },\n\n        /**\n         * Handle change on bundle option inputs\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _onBundleOptionChanged: function onBundleOptionChanged(event) {\n            var changes,\n                bundleOption = $(event.target),\n                priceBox = $(this.options.priceBoxSelector, this.element),\n                handler = this.options.optionHandlers[bundleOption.data('role')];\n\n            bundleOption.data('optionContainer', bundleOption.closest(this.options.controlContainer));\n            bundleOption.data('qtyField', bundleOption.data('optionContainer').find(this.options.qtyFieldSelector));\n\n            if (handler && handler instanceof Function) {\n                changes = handler(bundleOption, this.options.optionConfig, this);\n            } else {\n                changes = defaultGetOptionValue(bundleOption, this.options.optionConfig);//eslint-disable-line\n            }\n\n            // eslint-disable-next-line no-use-before-define\n            if (isValidQty(bundleOption)) {\n                if (changes) {\n                    priceBox.trigger('updatePrice', changes);\n                }\n\n                this._displayTierPriceBlock(bundleOption);\n                this.updateProductSummary();\n            }\n        },\n\n        /**\n         * Handle change on qty inputs near bundle option\n         * @param {jQuery.Event} event\n         * @private\n         */\n        _onQtyFieldChanged: function onQtyFieldChanged(event) {\n            var field = $(event.target),\n                optionInstance,\n                optionConfig;\n\n            if (field.data('optionId') && field.data('optionValueId')) {\n                optionInstance = field.data('option');\n                optionConfig = this.options.optionConfig\n                    .options[field.data('optionId')]\n                    .selections[field.data('optionValueId')];\n                optionConfig.qty = field.val();\n\n                // eslint-disable-next-line no-use-before-define\n                if (isValidQty(optionInstance)) {\n                    optionInstance.trigger('change');\n                }\n            }\n        },\n\n        /**\n         * Helper to fix backend behavior:\n         *  - if default qty large than 1 then backend multiply price in config\n         *\n         * @deprecated\n         * @private\n         */\n        _applyQtyFix: function applyQtyFix() {\n            var config = this.options.optionConfig;\n\n            if (config.isFixedPrice) {\n                _.each(config.options, function (option) {\n                    _.each(option.selections, function (item) {\n                        if (item.qty && item.qty !== 1) {\n                            _.each(item.prices, function (price) {\n                                price.amount /= item.qty;\n                            });\n                        }\n                    });\n                });\n            }\n        },\n\n        /**\n         * Helper to fix issue with option nodes:\n         *  - you can't place any html in option ->\n         *    so you can't style it via CSS\n         * @param {jQuery} options\n         * @private\n         */\n        _applyOptionNodeFix: function applyOptionNodeFix(options) {\n            var config = this.options,\n                format = config.priceFormat,\n                template = config.optionTemplate;\n\n            template = mageTemplate(template);\n            options.filter('select').each(function (index, element) {\n                var $element = $(element),\n                    optionId = utils.findOptionId($element),\n                    optionConfig = config.optionConfig && config.optionConfig.options[optionId].selections,\n                    value;\n\n                $element.find('option').each(function (idx, option) {\n                    var $option,\n                        optionValue,\n                        toTemplate,\n                        prices;\n\n                    $option = $(option);\n                    optionValue = $option.val();\n\n                    if (!optionValue && optionValue !== 0) {\n                        return;\n                    }\n\n                    toTemplate = {\n                        data: {\n                            label: optionConfig[optionValue] && optionConfig[optionValue].name\n                        }\n                    };\n                    prices = optionConfig[optionValue].prices;\n\n                    _.each(prices, function (price, type) {\n                        value = +price.amount;\n                        value += _.reduce(price.adjustments, function (sum, x) {//eslint-disable-line\n                            return sum + x;\n                        }, 0);\n                        toTemplate.data[type] = {\n                            value: value,\n                            formatted: utils.formatPriceLocale(value, format)\n                        };\n                    });\n\n                    $option.html(template(toTemplate));\n                });\n            });\n        },\n\n        /**\n         * Custom behavior on getting options:\n         * now widget able to deep merge accepted configuration with instance options.\n         * @param  {Object}  options\n         * @return {$.Widget}\n         */\n        _setOptions: function setOptions(options) {\n            $.extend(true, this.options, options);\n\n            this._super(options);\n\n            return this;\n        },\n\n        /**\n         * Show or hide option tier prices block\n         *\n         * @param {Object} optionElement\n         * @private\n         */\n        _displayTierPriceBlock: function (optionElement) {\n            var optionType = optionElement.prop('type'),\n                optionId,\n                optionValue,\n                optionTierPricesElements;\n\n            if (optionType === 'select-one') {\n                optionId = utils.findOptionId(optionElement[0]);\n                optionValue = optionElement.val() || null;\n                optionTierPricesElements = $(this.options.optionTierPricesBlocksSelector.replace('{1}', optionId));\n\n                _.each(optionTierPricesElements, function (tierPriceElement) {\n                    var selectionId = $(tierPriceElement).data('selection-id') + '';\n\n                    if (selectionId === optionValue) {\n                        $(tierPriceElement).show();\n                    } else {\n                        $(tierPriceElement).hide();\n                    }\n                });\n            }\n        },\n\n        /**\n         * Handler to update productSummary box\n         */\n        updateProductSummary: function updateProductSummary() {\n            this.element.trigger('updateProductSummary', {\n                config: this.options.optionConfig\n            });\n        }\n    });\n\n    return $.mage.priceBundle;\n\n    /**\n     * Converts option value to priceBox object\n     *\n     * @param   {jQuery} element\n     * @param   {Object} config\n     * @returns {Object|null} - priceBox object with additional prices\n     */\n    function defaultGetOptionValue(element, config) {\n        var changes = {},\n            optionHash,\n            tempChanges,\n            qtyField,\n            optionId = utils.findOptionId(element[0]),\n            optionValue = element.val() || null,\n            optionName = element.prop('name'),\n            optionType = element.prop('type'),\n            optionConfig = config.options[optionId].selections,\n            optionQty = 0,\n            canQtyCustomize = false,\n            selectedIds = config.selected;\n\n        switch (optionType) {\n            case 'radio':\n            case 'select-one':\n\n                if (optionType === 'radio' && !element.is(':checked')) {\n                    return null;\n                }\n\n                qtyField = element.data('qtyField');\n                qtyField.data('option', element);\n\n                if (optionValue) {\n                    optionQty = optionConfig[optionValue].qty || 0;\n                    canQtyCustomize = optionConfig[optionValue].customQty === '1';\n                    toggleQtyField(qtyField, optionQty, optionId, optionValue, canQtyCustomize);//eslint-disable-line\n                    tempChanges = utils.deepClone(optionConfig[optionValue].prices);\n                    tempChanges = applyTierPrice(//eslint-disable-line\n                        tempChanges,\n                        optionQty,\n                        optionConfig[optionValue]\n                    );\n                    tempChanges = applyQty(tempChanges, optionQty);//eslint-disable-line\n                } else {\n                    tempChanges = {};\n                    toggleQtyField(qtyField, '0', optionId, optionValue, false);//eslint-disable-line\n                }\n                optionHash = 'bundle-option-' + optionName;\n                changes[optionHash] = tempChanges;\n                selectedIds[optionId] = [optionValue];\n                break;\n\n            case 'select-multiple':\n                optionValue = _.compact(optionValue);\n\n                _.each(optionConfig, function (row, optionValueCode) {\n                    optionHash = 'bundle-option-' + optionName + '##' + optionValueCode;\n                    optionQty = row.qty || 0;\n                    tempChanges = utils.deepClone(row.prices);\n                    tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig);//eslint-disable-line\n                    tempChanges = applyQty(tempChanges, optionQty);//eslint-disable-line\n                    changes[optionHash] = _.contains(optionValue, optionValueCode) ? tempChanges : {};\n                });\n\n                selectedIds[optionId] = optionValue || [];\n                break;\n\n            case 'checkbox':\n                optionHash = 'bundle-option-' + optionName + '##' + optionValue;\n                optionQty = optionConfig[optionValue].qty || 0;\n                tempChanges = utils.deepClone(optionConfig[optionValue].prices);\n                tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig);//eslint-disable-line\n                tempChanges = applyQty(tempChanges, optionQty);//eslint-disable-line\n                changes[optionHash] = element.is(':checked') ? tempChanges : {};\n\n                selectedIds[optionId] = selectedIds[optionId] || [];\n\n                if (!_.contains(selectedIds[optionId], optionValue) && element.is(':checked')) {\n                    selectedIds[optionId].push(optionValue);\n                } else if (!element.is(':checked')) {\n                    selectedIds[optionId] = _.without(selectedIds[optionId], optionValue);\n                }\n                break;\n\n            case 'hidden':\n                optionHash = 'bundle-option-' + optionName + '##' + optionValue;\n                optionQty = optionConfig[optionValue].qty || 0;\n                canQtyCustomize = optionConfig[optionValue].customQty === '1';\n                qtyField = element.data('qtyField');\n                qtyField.data('option', element);\n                toggleQtyField(qtyField, optionQty, optionId, optionValue, canQtyCustomize);//eslint-disable-line\n                tempChanges = utils.deepClone(optionConfig[optionValue].prices);\n                tempChanges = applyTierPrice(tempChanges, optionQty, optionConfig);//eslint-disable-line\n                tempChanges = applyQty(tempChanges, optionQty);//eslint-disable-line\n\n                optionHash = 'bundle-option-' + optionName;\n                changes[optionHash] = tempChanges;\n                selectedIds[optionId] = [optionValue];\n                break;\n        }\n\n        return changes;\n    }\n\n    /**\n     * Check the quantity field if negative value occurs.\n     *\n     * @param {Object} bundleOption\n     */\n    function isValidQty(bundleOption) {\n        var isValid = true,\n            qtyElem = bundleOption.data('qtyField'),\n            bundleOptionType = bundleOption.prop('type');\n\n        if (['radio', 'select-one'].includes(bundleOptionType) && qtyElem.val() < 0) {\n            isValid = false;\n        }\n\n        return isValid;\n    }\n\n    /**\n     * Helper to toggle qty field\n     * @param {jQuery} element\n     * @param {String|Number} value\n     * @param {String|Number} optionId\n     * @param {String|Number} optionValueId\n     * @param {Boolean} canEdit\n     */\n    function toggleQtyField(element, value, optionId, optionValueId, canEdit) {\n        element\n            .val(value)\n            .data('optionId', optionId)\n            .data('optionValueId', optionValueId)\n            .attr('disabled', !canEdit);\n\n        if (canEdit) {\n            element.removeClass('qty-disabled');\n        } else {\n            element.addClass('qty-disabled');\n        }\n    }\n\n    /**\n     * Helper to multiply on qty\n     *\n     * @param   {Object} prices\n     * @param   {Number} qty\n     * @returns {Object}\n     */\n    function applyQty(prices, qty) {\n        _.each(prices, function (everyPrice) {\n            everyPrice.amount *= qty;\n            _.each(everyPrice.adjustments, function (el, index) {\n                everyPrice.adjustments[index] *= qty;\n            });\n        });\n\n        return prices;\n    }\n\n    /**\n     * Helper to limit price with tier price\n     *\n     * @param {Object} oneItemPrice\n     * @param {Number} qty\n     * @param {Object} optionConfig\n     * @returns {Object}\n     */\n    function applyTierPrice(oneItemPrice, qty, optionConfig) {\n        var tiers = optionConfig.tierPrice,\n            magicKey = _.keys(oneItemPrice)[0],\n            tiersFirstKey = _.keys(optionConfig)[0],\n            lowest = false;\n\n        if (!tiers) {//tiers is undefined when options has only one option\n            tiers = optionConfig[tiersFirstKey].tierPrice;\n        }\n\n        tiers.sort(function (a, b) {//sorting based on \"price_qty\"\n            return a['price_qty'] - b['price_qty'];\n        });\n\n        _.each(tiers, function (tier, index) {\n            if (tier['price_qty'] > qty) {\n                return;\n            }\n\n            if (tier.prices[magicKey].amount < oneItemPrice[magicKey].amount) {\n                lowest = index;\n            }\n        });\n\n        if (lowest !== false) {\n            oneItemPrice = utils.deepClone(tiers[lowest].prices);\n        }\n\n        return oneItemPrice;\n    }\n});\n","Magento_Bundle/js/components/bundle-checkbox.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'Magento_Ui/js/form/element/single-checkbox',\n    'uiRegistry'\n], function (Checkbox, registry) {\n    'use strict';\n\n    return Checkbox.extend({\n        defaults: {\n            clearing: false,\n            parentContainer: '',\n            parentSelections: '',\n            changer: '',\n            exports: {\n                value: '${$.parentName}:isDefaultValue'\n            }\n        },\n\n        /**\n         * @inheritdoc\n         */\n        initObservable: function () {\n            this._super().\n                observe('elementTmpl');\n\n            return this;\n        },\n\n        /**\n         * @inheritdoc\n         */\n        initConfig: function () {\n            this._super();\n            this.imports.changeType = this.retrieveParentName(this.parentContainer) + '.' + this.changer + ':value';\n\n            return this;\n        },\n\n        /**\n         * @inheritdoc\n         */\n        onUpdate: function () {\n            if (this.prefer === 'radio' && this.checked() && !this.clearing) {\n                this.clearValues();\n            }\n\n            this._super();\n        },\n\n        /**\n         * Checkbox to radio type changer.\n         *\n         * @param {String} type - type to change.\n         */\n        changeType: function (type) {\n            var typeMap = registry.get(this.retrieveParentName(this.parentContainer) + '.' + this.changer).typeMap;\n\n            this.prefer = typeMap[type];\n            this.elementTmpl(this.templates[typeMap[type]]);\n        },\n\n        /**\n         * Clears values in components like this.\n         */\n        clearValues: function () {\n            var records = registry.get(this.retrieveParentName(this.parentSelections)),\n                index = this.index,\n                uid = this.uid;\n\n            records.elems.each(function (record) {\n                record.elems.filter(function (comp) {\n                    return comp.index === index && comp.uid !== uid;\n                }).each(function (comp) {\n                    comp.clearing = true;\n                    comp.clear();\n                    comp.clearing = false;\n                });\n            });\n        },\n\n        /**\n         * Retrieve name for the most global parent with provided index.\n         *\n         * @param {String} parent - parent name.\n         * @returns {String}\n         */\n        retrieveParentName: function (parent) {\n            return this.name.replace(new RegExp('^(.+?\\\\.)?' + parent + '\\\\..+'), '$1' + parent);\n        }\n    });\n});\n","Magento_Bundle/js/components/bundle-dynamic-rows.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'mageUtils',\n    'uiRegistry',\n    'Magento_Ui/js/dynamic-rows/dynamic-rows'\n], function (_, utils, registry, dynamicRows) {\n    'use strict';\n\n    return dynamicRows.extend({\n        defaults: {\n            label: '',\n            collapsibleHeader: true,\n            columnsHeader: false,\n            deleteProperty: false,\n            addButton: false\n        },\n\n        /**\n         * Set new data to dataSource,\n         * delete element\n         *\n         * @param {Array} data - record data\n         */\n        _updateData: function (data) {\n            var elems = _.clone(this.elems()),\n                path,\n                dataArr,\n                optionBaseData;\n\n            dataArr = this.recordData.splice(this.startIndex, this.recordData().length - this.startIndex);\n            dataArr.splice(0, this.pageSize);\n            elems = _.sortBy(this.elems(), function (elem) {\n                return ~~elem.index;\n            });\n\n            data.concat(dataArr).forEach(function (rec, idx) {\n                if (elems[idx]) {\n                    elems[idx].recordId = rec[this.identificationProperty];\n                }\n\n                if (!rec.position) {\n                    rec.position = this.maxPosition;\n                    this.setMaxPosition();\n                }\n\n                path = this.dataScope + '.' + this.index + '.' + (this.startIndex + idx);\n                optionBaseData = _.pick(rec, function (value) {\n                    return !_.isObject(value);\n                });\n                this.source.set(path, optionBaseData);\n                this.source.set(path + '.bundle_button_proxy', []);\n                this.source.set(path + '.bundle_selections', []);\n                this.removeBundleItemsFromOption(idx);\n                _.each(rec['bundle_selections'], function (obj, index) {\n                    this.source.set(path + '.bundle_button_proxy' + '.' + index, rec['bundle_button_proxy'][index]);\n                    this.source.set(path + '.bundle_selections' + '.' + index, obj);\n                }, this);\n            }, this);\n\n            this.elems(elems);\n        },\n\n        /**\n         *  Removes nested dynamic-rows-grid rendered records from option\n         *\n         * @param {Number|String} index - element index\n         */\n        removeBundleItemsFromOption: function (index) {\n            var bundleSelections = registry.get(this.name + '.' + index + '.' + this.bundleSelectionsName),\n                bundleSelectionsLength = (bundleSelections.elems() || []).length,\n                i;\n\n            if (bundleSelectionsLength) {\n                for (i = 0; i < bundleSelectionsLength; i++) {\n                    bundleSelections.elems()[0].destroy();\n                }\n            }\n        },\n\n        /**\n        * {@inheritdoc}\n        */\n        processingAddChild: function (ctx, index, prop) {\n            var recordIds = _.map(this.recordData(), function (rec) {\n                return parseInt(rec['record_id'], 10);\n            }),\n            maxRecordId = _.max(recordIds);\n\n            prop = maxRecordId > -1 ? maxRecordId + 1 : prop;\n            this._super(ctx, index, prop);\n        }\n    });\n});\n","Magento_Bundle/js/components/bundle-user-defined-checkbox.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'Magento_Ui/js/form/element/single-checkbox'\n], function (Checkbox) {\n    'use strict';\n\n    return Checkbox.extend({\n        defaults: {\n            listens: {\n                inputType: 'onInputTypeChange'\n            }\n        },\n\n        /**\n         * Handler for \"inputType\" property\n         *\n         * @param {String} data\n         */\n        onInputTypeChange: function (data) {\n            data === 'checkbox' || data === 'multi' ?\n                this.clear()\n                    .visible(false) :\n                this.visible(true);\n        }\n    });\n});\n","Magento_Bundle/js/components/bundle-option-qty.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'Magento_Ui/js/form/element/abstract'\n], function (Abstract) {\n    'use strict';\n\n    return Abstract.extend({\n        defaults: {\n            valueUpdate: 'input',\n            isInteger: true,\n            validation: {\n                'validate-number': true\n            }\n        },\n\n        /**\n         * @inheritdoc\n         */\n        setInitialValue: function () {\n            this.initialValue = this.getInitialValue();\n\n            if (this.initialValue === undefined || this.initialValue === '') {\n                this.initialValue = 1;\n            }\n\n            if (this.value.peek() !== this.initialValue) {\n                this.value(this.initialValue);\n            }\n\n            this.on('value', this.onUpdate.bind(this));\n            this.isUseDefault(this.disabled());\n\n            return this;\n        },\n\n        /**\n         * @inheritdoc\n         */\n        onUpdate: function () {\n            this.validation['validate-digits'] = this.isInteger;\n            this._super();\n        },\n\n        /**\n         * @inheritdoc\n         */\n        hasChanged: function () {\n            var notEqual = this.value() !== this.initialValue.toString();\n\n            return !this.visible() ? false : notEqual;\n        }\n    });\n});\n","Magento_Bundle/js/components/bundle-record.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'Magento_Ui/js/dynamic-rows/record',\n    'uiRegistry'\n], function (Record, registry) {\n    'use strict';\n\n    return Record.extend({\n        /**\n         * @param {String} val - type of Input Type\n         */\n        onTypeChanged: function (val) {\n            var columnVisibility  = !(val === 'multi' || val === 'checkbox');\n\n            registry.async(this.name + '.' + 'selection_can_change_qty')(function (elem) {\n                elem.visible(columnVisibility);\n            });\n        }\n    });\n});\n","Magento_Bundle/js/components/bundle-dynamic-rows-grid.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'underscore',\n    'Magento_Ui/js/dynamic-rows/dynamic-rows-grid',\n    'uiLayout',\n    'rjsResolver'\n], function (_, dynamicRowsGrid, layout, resolver) {\n    'use strict';\n\n    return dynamicRowsGrid.extend({\n        defaults: {\n            label: '',\n            columnsHeader: false,\n            columnsHeaderAfterRender: true,\n            addButton: false,\n            isDefaultFieldScope: 'is_default',\n            defaultRecords: {\n                use: [],\n                moreThanOne: false,\n                state: {}\n            },\n            listens: {\n                inputType: 'onInputTypeChange',\n                isDefaultValue: 'onIsDefaultValue',\n                pageSize: 'onPageSizeChange'\n            },\n            sizesConfig: {\n                component: 'Magento_Ui/js/grid/paging/sizes',\n                name: '${ $.name }_sizes',\n                options: {\n                    '20': {\n                        value: 20,\n                        label: 20\n                    },\n                    '30': {\n                        value: 30,\n                        label: 30\n                    },\n                    '50': {\n                        value: 50,\n                        label: 50\n                    },\n                    '100': {\n                        value: 100,\n                        label: 100\n                    },\n                    '200': {\n                        value: 200,\n                        label: 200\n                    }\n                },\n                storageConfig: {\n                    provider: '${ $.storageConfig.provider }',\n                    namespace: '${ $.storageConfig.namespace }'\n                },\n                enabled: false\n            },\n            links: {\n                options: '${ $.sizesConfig.name }:options',\n                pageSize: '${ $.sizesConfig.name }:value'\n            },\n            modules: {\n                sizes: '${ $.sizesConfig.name }'\n            }\n        },\n\n        /**\n         * Initializes paging component.\n         *\n         * @returns {Paging} Chainable.\n         */\n        initialize: function () {\n            this._super()\n                .initSizes();\n\n            return this;\n        },\n\n        /**\n         * Initializes sizes component.\n         *\n         * @returns {Paging} Chainable.\n         */\n        initSizes: function () {\n            if (this.sizesConfig.enabled) {\n                layout([this.sizesConfig]);\n            }\n\n            return this;\n        },\n\n        /**\n         * Handler for type select.\n         *\n         * @param {String} inputType - changed.\n         */\n        onInputTypeChange: function (inputType) {\n            if (this.defaultRecords.moreThanOne && (inputType === 'radio' || inputType === 'select')) {\n                _.each(this.defaultRecords.use, function (index, counter) {\n                    this.source.set(\n                        this.dataScope + '.bundle_selections.' + index + '.' + this.isDefaultFieldScope,\n                        counter ? '0' : '1'\n                    );\n                }.bind(this));\n            }\n        },\n\n        /**\n         * Handler for is_default field.\n         *\n         * @param {Object} data - changed data.\n         */\n        onIsDefaultValue: function (data) {\n            var cb,\n                use = 0;\n\n            this.defaultRecords.use = [];\n\n            cb = function (elem, key) {\n\n                if (~~elem) {\n                    this.defaultRecords.use.push(key);\n                    use++;\n                }\n\n                this.defaultRecords.moreThanOne = use > 1;\n            }.bind(this);\n\n            _.each(data, cb);\n        },\n\n        /**\n         * Initialize elements from grid\n         *\n         * @param {Array} data\n         *\n         * @returns {Object} Chainable.\n         */\n        initElements: function (data) {\n            var newData = this.getNewData(data),\n                recordIndex;\n\n            this.parsePagesData(data);\n\n            if (newData.length) {\n                if (this.insertData().length) {\n                    recordIndex = data.length - newData.length - 1;\n\n                    _.each(newData, function (newRecord) {\n                        this.processingAddChild(newRecord, ++recordIndex, newRecord[this.identificationProperty]);\n                    }, this);\n                }\n            }\n\n            return this;\n        },\n\n        /**\n         * Mapping value from grid\n         *\n         * @param {Array} data\n         */\n        mappingValue: function (data) {\n            if (_.isEmpty(data)) {\n                return;\n            }\n\n            this._super();\n        },\n\n        /**\n         * Handles changes of the page size.\n         */\n        onPageSizeChange: function () {\n            resolver(function () {\n                if (this.elems().length) {\n                    this.reload();\n                }\n            }, this);\n        }\n    });\n});\n","Magento_Bundle/js/components/bundle-input-type.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n/**\n * @deprecated Not used anymore\n * @see Magento_Bundle/js/components/bundle-record\n * @see Magento_Bundle/js/components/bundle-checkbox\n */\ndefine([\n    'Magento_Ui/js/form/element/select',\n    'uiRegistry'\n], function (Select, registry) {\n    'use strict';\n\n    return Select.extend({\n        defaults: {\n            previousType: '',\n            parentContainer: '',\n            selections: '',\n            targetIndex: '',\n            typeMap: {}\n        },\n\n        /**\n         * @inheritdoc\n         */\n        onUpdate: function () {\n            var type = this.typeMap[this.value()];\n\n            if (type !== this.previousType) {\n                this.previousType = type;\n                this.processSelections(type === 'radio');\n            }\n\n            this._super();\n        },\n\n        /**\n         * Toggle 'User Defined' column and clears values\n         * @param {Boolean} isRadio\n         */\n        processSelections: function (isRadio) {\n            var records = registry.get(this.retrieveParentName(this.parentContainer) + '.' + this.selections),\n                checkedFound = false;\n\n            records.elems.each(function (record) {\n                record.elems.filter(function (comp) {\n                    return comp.index === this.userDefinedIndex;\n                }, this).each(function (comp) {\n                    comp.visible(isRadio);\n                });\n\n                if (isRadio) {\n                    record.elems.filter(function (comp) {\n                        return comp.index === this.isDefaultIndex;\n                    }, this).each(function (comp) {\n                        if (comp.checked()) {\n                            if (checkedFound) {\n                                comp.clearing = true;\n                                comp.clear();\n                                comp.clearing = false;\n                            }\n\n                            checkedFound = true;\n                        }\n                    });\n                }\n            }, this);\n        },\n\n        /**\n         * Retrieve name for the most global parent with provided index.\n         *\n         * @param {String} parent - parent name.\n         * @returns {String}\n         */\n        retrieveParentName: function (parent) {\n            return this.name.replace(new RegExp('^(.+?\\\\.)?' + parent + '\\\\..+'), '$1' + parent);\n        }\n    });\n});\n","Magento_LoginAsCustomerAssistance/js/not-allowed-popup.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'uiComponent',\n    'Magento_Ui/js/modal/confirm',\n    'mage/translate'\n], function (Component, confirm, $t) {\n\n    'use strict';\n\n    return Component.extend({\n        /**\n         * Initialize Component\n         */\n        initialize: function () {\n            var self = this,\n                content;\n\n            this._super();\n\n            content = '<div class=\"message message-warning\">' + self.content + '</div>';\n\n            /**\n             * Not Allowed popup\n             *\n             * @returns {Boolean}\n             */\n            window.lacNotAllowedPopup = function () {\n                confirm({\n                    title: self.title,\n                    content: content,\n                    modalClass: 'confirm lac-confirm',\n                    buttons: [\n                        {\n                            text: $t('Close'),\n                            class: 'action-secondary action-dismiss',\n\n                            /**\n                             * Click handler.\n                             */\n                            click: function (event) {\n                                this.closeModal(event);\n                            }\n                        }\n                    ]\n                });\n\n                return false;\n            };\n        }\n    });\n});\n","Smartwave_Dailydeals/js/script.js":"require([\r\n    'jquery',\r\n    'jquery_chosen'\r\n], function ($) {\r\n    'use strict';\r\n\r\n    $(document).ready(function(){\r\n    \tsetTimeout(function(){\r\n    \t\t$(\"#dailydeal_sw_product_sku\").chosen();\r\n    \t},1000);\r\n    });\r\n\r\n});","Smartwave_Dailydeals/js/chosen.jquery.min.js":"/* Chosen v1.8.7 | (c) 2011-2018 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */\n\n(function(){var t,e,s,i,n=function(t,e){return function(){return t.apply(e,arguments)}},r=function(t,e){function s(){this.constructor=t}for(var i in e)o.call(e,i)&&(t[i]=e[i]);return s.prototype=e.prototype,t.prototype=new s,t.__super__=e.prototype,t},o={}.hasOwnProperty;(i=function(){function t(){this.options_index=0,this.parsed=[]}return t.prototype.add_node=function(t){return\"OPTGROUP\"===t.nodeName.toUpperCase()?this.add_group(t):this.add_option(t)},t.prototype.add_group=function(t){var e,s,i,n,r,o;for(e=this.parsed.length,this.parsed.push({array_index:e,group:!0,label:t.label,title:t.title?t.title:void 0,children:0,disabled:t.disabled,classes:t.className}),o=[],s=0,i=(r=t.childNodes).length;s<i;s++)n=r[s],o.push(this.add_option(n,e,t.disabled));return o},t.prototype.add_option=function(t,e,s){if(\"OPTION\"===t.nodeName.toUpperCase())return\"\"!==t.text?(null!=e&&(this.parsed[e].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:t.value,text:t.text,html:t.innerHTML,title:t.title?t.title:void 0,selected:t.selected,disabled:!0===s?s:t.disabled,group_array_index:e,group_label:null!=e?this.parsed[e].label:null,classes:t.className,style:t.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},t}()).select_to_array=function(t){var e,s,n,r,o;for(r=new i,s=0,n=(o=t.childNodes).length;s<n;s++)e=o[s],r.add_node(e);return r.parsed},e=function(){function t(e,s){this.form_field=e,this.options=null!=s?s:{},this.label_click_handler=n(this.label_click_handler,this),t.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers(),this.on_ready())}return t.prototype.set_default_values=function(){return this.click_test_action=function(t){return function(e){return t.test_active_click(e)}}(this),this.activate_action=function(t){return function(e){return t.activate_field(e)}}(this),this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.is_rtl=this.options.rtl||/\\bchosen-rtl\\b/.test(this.form_field.className),this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&\"\"===this.form_field.options[0].text&&this.options.allow_single_deselect,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null==this.options.enable_split_word_search||this.options.enable_split_word_search,this.group_search=null==this.options.group_search||this.options.group_search,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null==this.options.single_backstroke_delete||this.options.single_backstroke_delete,this.max_selected_options=this.options.max_selected_options||Infinity,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null==this.options.display_selected_options||this.options.display_selected_options,this.display_disabled_options=null==this.options.display_disabled_options||this.options.display_disabled_options,this.include_group_label_in_selected=this.options.include_group_label_in_selected||!1,this.max_shown_results=this.options.max_shown_results||Number.POSITIVE_INFINITY,this.case_sensitive_search=this.options.case_sensitive_search||!1,this.hide_results_on_select=null==this.options.hide_results_on_select||this.options.hide_results_on_select},t.prototype.set_default_text=function(){return this.form_field.getAttribute(\"data-placeholder\")?this.default_text=this.form_field.getAttribute(\"data-placeholder\"):this.is_multiple?this.default_text=this.options.placeholder_text_multiple||this.options.placeholder_text||t.default_multiple_text:this.default_text=this.options.placeholder_text_single||this.options.placeholder_text||t.default_single_text,this.default_text=this.escape_html(this.default_text),this.results_none_found=this.form_field.getAttribute(\"data-no_results_text\")||this.options.no_results_text||t.default_no_result_text},t.prototype.choice_label=function(t){return this.include_group_label_in_selected&&null!=t.group_label?\"<b class='group-name'>\"+this.escape_html(t.group_label)+\"</b>\"+t.html:t.html},t.prototype.mouse_enter=function(){return this.mouse_on_container=!0},t.prototype.mouse_leave=function(){return this.mouse_on_container=!1},t.prototype.input_focus=function(t){if(this.is_multiple){if(!this.active_field)return setTimeout(function(t){return function(){return t.container_mousedown()}}(this),50)}else if(!this.active_field)return this.activate_field()},t.prototype.input_blur=function(t){if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(t){return function(){return t.blur_test()}}(this),100)},t.prototype.label_click_handler=function(t){return this.is_multiple?this.container_mousedown(t):this.activate_field()},t.prototype.results_option_build=function(t){var e,s,i,n,r,o,h;for(e=\"\",h=0,n=0,r=(o=this.results_data).length;n<r&&(s=o[n],i=\"\",\"\"!==(i=s.group?this.result_add_group(s):this.result_add_option(s))&&(h++,e+=i),(null!=t?t.first:void 0)&&(s.selected&&this.is_multiple?this.choice_build(s):s.selected&&!this.is_multiple&&this.single_set_selected_text(this.choice_label(s))),!(h>=this.max_shown_results));n++);return e},t.prototype.result_add_option=function(t){var e,s;return t.search_match&&this.include_option_in_results(t)?(e=[],t.disabled||t.selected&&this.is_multiple||e.push(\"active-result\"),!t.disabled||t.selected&&this.is_multiple||e.push(\"disabled-result\"),t.selected&&e.push(\"result-selected\"),null!=t.group_array_index&&e.push(\"group-option\"),\"\"!==t.classes&&e.push(t.classes),s=document.createElement(\"li\"),s.className=e.join(\" \"),t.style&&(s.style.cssText=t.style),s.setAttribute(\"data-option-array-index\",t.array_index),s.innerHTML=t.highlighted_html||t.html,t.title&&(s.title=t.title),this.outerHTML(s)):\"\"},t.prototype.result_add_group=function(t){var e,s;return(t.search_match||t.group_match)&&t.active_options>0?((e=[]).push(\"group-result\"),t.classes&&e.push(t.classes),s=document.createElement(\"li\"),s.className=e.join(\" \"),s.innerHTML=t.highlighted_html||this.escape_html(t.label),t.title&&(s.title=t.title),this.outerHTML(s)):\"\"},t.prototype.results_update_field=function(){if(this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing)return this.winnow_results()},t.prototype.reset_single_select_options=function(){var t,e,s,i,n;for(n=[],t=0,e=(s=this.results_data).length;t<e;t++)(i=s[t]).selected?n.push(i.selected=!1):n.push(void 0);return n},t.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},t.prototype.results_search=function(t){return this.results_showing?this.winnow_results():this.results_show()},t.prototype.winnow_results=function(t){var e,s,i,n,r,o,h,l,c,_,a,u,d,p,f;for(this.no_results_clear(),_=0,e=(h=this.get_search_text()).replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g,\"\\\\$&\"),c=this.get_search_regex(e),i=0,n=(l=this.results_data).length;i<n;i++)(r=l[i]).search_match=!1,a=null,u=null,r.highlighted_html=\"\",this.include_option_in_results(r)&&(r.group&&(r.group_match=!1,r.active_options=0),null!=r.group_array_index&&this.results_data[r.group_array_index]&&(0===(a=this.results_data[r.group_array_index]).active_options&&a.search_match&&(_+=1),a.active_options+=1),f=r.group?r.label:r.text,r.group&&!this.group_search||(u=this.search_string_match(f,c),r.search_match=null!=u,r.search_match&&!r.group&&(_+=1),r.search_match?(h.length&&(d=u.index,o=f.slice(0,d),s=f.slice(d,d+h.length),p=f.slice(d+h.length),r.highlighted_html=this.escape_html(o)+\"<em>\"+this.escape_html(s)+\"</em>\"+this.escape_html(p)),null!=a&&(a.group_match=!0)):null!=r.group_array_index&&this.results_data[r.group_array_index].search_match&&(r.search_match=!0)));return this.result_clear_highlight(),_<1&&h.length?(this.update_results_content(\"\"),this.no_results(h)):(this.update_results_content(this.results_option_build()),(null!=t?t.skip_highlight:void 0)?void 0:this.winnow_results_set_highlight())},t.prototype.get_search_regex=function(t){var e,s;return s=this.search_contains?t:\"(^|\\\\s|\\\\b)\"+t+\"[^\\\\s]*\",this.enable_split_word_search||this.search_contains||(s=\"^\"+s),e=this.case_sensitive_search?\"\":\"i\",new RegExp(s,e)},t.prototype.search_string_match=function(t,e){var s;return s=e.exec(t),!this.search_contains&&(null!=s?s[1]:void 0)&&(s.index+=1),s},t.prototype.choices_count=function(){var t,e,s;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,t=0,e=(s=this.form_field.options).length;t<e;t++)s[t].selected&&(this.selected_option_count+=1);return this.selected_option_count},t.prototype.choices_click=function(t){if(t.preventDefault(),this.activate_field(),!this.results_showing&&!this.is_disabled)return this.results_show()},t.prototype.keydown_checker=function(t){var e,s;switch(s=null!=(e=t.which)?e:t.keyCode,this.search_field_scale(),8!==s&&this.pending_backstroke&&this.clear_backstroke(),s){case 8:this.backstroke_length=this.get_search_field_value().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(t),this.mouse_on_container=!1;break;case 13:case 27:this.results_showing&&t.preventDefault();break;case 32:this.disable_search&&t.preventDefault();break;case 38:t.preventDefault(),this.keyup_arrow();break;case 40:t.preventDefault(),this.keydown_arrow()}},t.prototype.keyup_checker=function(t){var e,s;switch(s=null!=(e=t.which)?e:t.keyCode,this.search_field_scale(),s){case 8:this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0?this.keydown_backstroke():this.pending_backstroke||(this.result_clear_highlight(),this.results_search());break;case 13:t.preventDefault(),this.results_showing&&this.result_select(t);break;case 27:this.results_showing&&this.results_hide();break;case 9:case 16:case 17:case 18:case 38:case 40:case 91:break;default:this.results_search()}},t.prototype.clipboard_event_checker=function(t){if(!this.is_disabled)return setTimeout(function(t){return function(){return t.results_search()}}(this),50)},t.prototype.container_width=function(){return null!=this.options.width?this.options.width:this.form_field.offsetWidth+\"px\"},t.prototype.include_option_in_results=function(t){return!(this.is_multiple&&!this.display_selected_options&&t.selected)&&(!(!this.display_disabled_options&&t.disabled)&&!t.empty)},t.prototype.search_results_touchstart=function(t){return this.touch_started=!0,this.search_results_mouseover(t)},t.prototype.search_results_touchmove=function(t){return this.touch_started=!1,this.search_results_mouseout(t)},t.prototype.search_results_touchend=function(t){if(this.touch_started)return this.search_results_mouseup(t)},t.prototype.outerHTML=function(t){var e;return t.outerHTML?t.outerHTML:((e=document.createElement(\"div\")).appendChild(t),e.innerHTML)},t.prototype.get_single_html=function(){return'<a class=\"chosen-single chosen-default\">\\n  <span>'+this.default_text+'</span>\\n  <div><b></b></div>\\n</a>\\n<div class=\"chosen-drop\">\\n  <div class=\"chosen-search\">\\n    <input class=\"chosen-search-input\" type=\"text\" autocomplete=\"off\" />\\n  </div>\\n  <ul class=\"chosen-results\"></ul>\\n</div>'},t.prototype.get_multi_html=function(){return'<ul class=\"chosen-choices\">\\n  <li class=\"search-field\">\\n    <input class=\"chosen-search-input\" type=\"text\" autocomplete=\"off\" value=\"'+this.default_text+'\" />\\n  </li>\\n</ul>\\n<div class=\"chosen-drop\">\\n  <ul class=\"chosen-results\"></ul>\\n</div>'},t.prototype.get_no_results_html=function(t){return'<li class=\"no-results\">\\n  '+this.results_none_found+\" <span>\"+this.escape_html(t)+\"</span>\\n</li>\"},t.browser_is_supported=function(){return\"Microsoft Internet Explorer\"===window.navigator.appName?document.documentMode>=8:!(/iP(od|hone)/i.test(window.navigator.userAgent)||/IEMobile/i.test(window.navigator.userAgent)||/Windows Phone/i.test(window.navigator.userAgent)||/BlackBerry/i.test(window.navigator.userAgent)||/BB10/i.test(window.navigator.userAgent)||/Android.*Mobile/i.test(window.navigator.userAgent))},t.default_multiple_text=\"Select Some Options\",t.default_single_text=\"Select an Option\",t.default_no_result_text=\"No results match\",t}(),(t=jQuery).fn.extend({chosen:function(i){return e.browser_is_supported()?this.each(function(e){var n,r;r=(n=t(this)).data(\"chosen\"),\"destroy\"!==i?r instanceof s||n.data(\"chosen\",new s(this,i)):r instanceof s&&r.destroy()}):this}}),s=function(s){function n(){return n.__super__.constructor.apply(this,arguments)}return r(n,e),n.prototype.setup=function(){return this.form_field_jq=t(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex},n.prototype.set_up_html=function(){var e,s;return(e=[\"chosen-container\"]).push(\"chosen-container-\"+(this.is_multiple?\"multi\":\"single\")),this.inherit_select_classes&&this.form_field.className&&e.push(this.form_field.className),this.is_rtl&&e.push(\"chosen-rtl\"),s={\"class\":e.join(\" \"),title:this.form_field.title},this.form_field.id.length&&(s.id=this.form_field.id.replace(/[^\\w]/g,\"_\")+\"_chosen\"),this.container=t(\"<div />\",s),this.container.width(this.container_width()),this.is_multiple?this.container.html(this.get_multi_html()):this.container.html(this.get_single_html()),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find(\"div.chosen-drop\").first(),this.search_field=this.container.find(\"input\").first(),this.search_results=this.container.find(\"ul.chosen-results\").first(),this.search_field_scale(),this.search_no_results=this.container.find(\"li.no-results\").first(),this.is_multiple?(this.search_choices=this.container.find(\"ul.chosen-choices\").first(),this.search_container=this.container.find(\"li.search-field\").first()):(this.search_container=this.container.find(\"div.chosen-search\").first(),this.selected_item=this.container.find(\".chosen-single\").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior()},n.prototype.on_ready=function(){return this.form_field_jq.trigger(\"chosen:ready\",{chosen:this})},n.prototype.register_observers=function(){return this.container.on(\"touchstart.chosen\",function(t){return function(e){t.container_mousedown(e)}}(this)),this.container.on(\"touchend.chosen\",function(t){return function(e){t.container_mouseup(e)}}(this)),this.container.on(\"mousedown.chosen\",function(t){return function(e){t.container_mousedown(e)}}(this)),this.container.on(\"mouseup.chosen\",function(t){return function(e){t.container_mouseup(e)}}(this)),this.container.on(\"mouseenter.chosen\",function(t){return function(e){t.mouse_enter(e)}}(this)),this.container.on(\"mouseleave.chosen\",function(t){return function(e){t.mouse_leave(e)}}(this)),this.search_results.on(\"mouseup.chosen\",function(t){return function(e){t.search_results_mouseup(e)}}(this)),this.search_results.on(\"mouseover.chosen\",function(t){return function(e){t.search_results_mouseover(e)}}(this)),this.search_results.on(\"mouseout.chosen\",function(t){return function(e){t.search_results_mouseout(e)}}(this)),this.search_results.on(\"mousewheel.chosen DOMMouseScroll.chosen\",function(t){return function(e){t.search_results_mousewheel(e)}}(this)),this.search_results.on(\"touchstart.chosen\",function(t){return function(e){t.search_results_touchstart(e)}}(this)),this.search_results.on(\"touchmove.chosen\",function(t){return function(e){t.search_results_touchmove(e)}}(this)),this.search_results.on(\"touchend.chosen\",function(t){return function(e){t.search_results_touchend(e)}}(this)),this.form_field_jq.on(\"chosen:updated.chosen\",function(t){return function(e){t.results_update_field(e)}}(this)),this.form_field_jq.on(\"chosen:activate.chosen\",function(t){return function(e){t.activate_field(e)}}(this)),this.form_field_jq.on(\"chosen:open.chosen\",function(t){return function(e){t.container_mousedown(e)}}(this)),this.form_field_jq.on(\"chosen:close.chosen\",function(t){return function(e){t.close_field(e)}}(this)),this.search_field.on(\"blur.chosen\",function(t){return function(e){t.input_blur(e)}}(this)),this.search_field.on(\"keyup.chosen\",function(t){return function(e){t.keyup_checker(e)}}(this)),this.search_field.on(\"keydown.chosen\",function(t){return function(e){t.keydown_checker(e)}}(this)),this.search_field.on(\"focus.chosen\",function(t){return function(e){t.input_focus(e)}}(this)),this.search_field.on(\"cut.chosen\",function(t){return function(e){t.clipboard_event_checker(e)}}(this)),this.search_field.on(\"paste.chosen\",function(t){return function(e){t.clipboard_event_checker(e)}}(this)),this.is_multiple?this.search_choices.on(\"click.chosen\",function(t){return function(e){t.choices_click(e)}}(this)):this.container.on(\"click.chosen\",function(t){t.preventDefault()})},n.prototype.destroy=function(){return t(this.container[0].ownerDocument).off(\"click.chosen\",this.click_test_action),this.form_field_label.length>0&&this.form_field_label.off(\"click.chosen\"),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData(\"chosen\"),this.form_field_jq.show()},n.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field.disabled||this.form_field_jq.parents(\"fieldset\").is(\":disabled\"),this.container.toggleClass(\"chosen-disabled\",this.is_disabled),this.search_field[0].disabled=this.is_disabled,this.is_multiple||this.selected_item.off(\"focus.chosen\",this.activate_field),this.is_disabled?this.close_field():this.is_multiple?void 0:this.selected_item.on(\"focus.chosen\",this.activate_field)},n.prototype.container_mousedown=function(e){var s;if(!this.is_disabled)return!e||\"mousedown\"!==(s=e.type)&&\"touchstart\"!==s||this.results_showing||e.preventDefault(),null!=e&&t(e.target).hasClass(\"search-choice-close\")?void 0:(this.active_field?this.is_multiple||!e||t(e.target)[0]!==this.selected_item[0]&&!t(e.target).parents(\"a.chosen-single\").length||(e.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(\"\"),t(this.container[0].ownerDocument).on(\"click.chosen\",this.click_test_action),this.results_show()),this.activate_field())},n.prototype.container_mouseup=function(t){if(\"ABBR\"===t.target.nodeName&&!this.is_disabled)return this.results_reset(t)},n.prototype.search_results_mousewheel=function(t){var e;if(t.originalEvent&&(e=t.originalEvent.deltaY||-t.originalEvent.wheelDelta||t.originalEvent.detail),null!=e)return t.preventDefault(),\"DOMMouseScroll\"===t.type&&(e*=40),this.search_results.scrollTop(e+this.search_results.scrollTop())},n.prototype.blur_test=function(t){if(!this.active_field&&this.container.hasClass(\"chosen-container-active\"))return this.close_field()},n.prototype.close_field=function(){return t(this.container[0].ownerDocument).off(\"click.chosen\",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass(\"chosen-container-active\"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale(),this.search_field.blur()},n.prototype.activate_field=function(){if(!this.is_disabled)return this.container.addClass(\"chosen-container-active\"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},n.prototype.test_active_click=function(e){var s;return(s=t(e.target).closest(\".chosen-container\")).length&&this.container[0]===s[0]?this.active_field=!0:this.close_field()},n.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=i.select_to_array(this.form_field),this.is_multiple?this.search_choices.find(\"li.search-choice\").remove():(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass(\"chosen-container-single-nosearch\")):(this.search_field[0].readOnly=!1,this.container.removeClass(\"chosen-container-single-nosearch\"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},n.prototype.result_do_highlight=function(t){var e,s,i,n,r;if(t.length){if(this.result_clear_highlight(),this.result_highlight=t,this.result_highlight.addClass(\"highlighted\"),i=parseInt(this.search_results.css(\"maxHeight\"),10),r=this.search_results.scrollTop(),n=i+r,s=this.result_highlight.position().top+this.search_results.scrollTop(),(e=s+this.result_highlight.outerHeight())>=n)return this.search_results.scrollTop(e-i>0?e-i:0);if(s<r)return this.search_results.scrollTop(s)}},n.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass(\"highlighted\"),this.result_highlight=null},n.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger(\"chosen:maxselected\",{chosen:this}),!1):(this.container.addClass(\"chosen-with-drop\"),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.get_search_field_value()),this.winnow_results(),this.form_field_jq.trigger(\"chosen:showing_dropdown\",{chosen:this}))},n.prototype.update_results_content=function(t){return this.search_results.html(t)},n.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClass(\"chosen-with-drop\"),this.form_field_jq.trigger(\"chosen:hiding_dropdown\",{chosen:this})),this.results_showing=!1},n.prototype.set_tab_index=function(t){var e;if(this.form_field.tabIndex)return e=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field[0].tabIndex=e},n.prototype.set_label_behavior=function(){if(this.form_field_label=this.form_field_jq.parents(\"label\"),!this.form_field_label.length&&this.form_field.id.length&&(this.form_field_label=t(\"label[for='\"+this.form_field.id+\"']\")),this.form_field_label.length>0)return this.form_field_label.on(\"click.chosen\",this.label_click_handler)},n.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass(\"default\")):(this.search_field.val(\"\"),this.search_field.removeClass(\"default\"))},n.prototype.search_results_mouseup=function(e){var s;if((s=t(e.target).hasClass(\"active-result\")?t(e.target):t(e.target).parents(\".active-result\").first()).length)return this.result_highlight=s,this.result_select(e),this.search_field.focus()},n.prototype.search_results_mouseover=function(e){var s;if(s=t(e.target).hasClass(\"active-result\")?t(e.target):t(e.target).parents(\".active-result\").first())return this.result_do_highlight(s)},n.prototype.search_results_mouseout=function(e){if(t(e.target).hasClass(\"active-result\")||t(e.target).parents(\".active-result\").first())return this.result_clear_highlight()},n.prototype.choice_build=function(e){var s,i;return s=t(\"<li />\",{\"class\":\"search-choice\"}).html(\"<span>\"+this.choice_label(e)+\"</span>\"),e.disabled?s.addClass(\"search-choice-disabled\"):((i=t(\"<a />\",{\"class\":\"search-choice-close\",\"data-option-array-index\":e.array_index})).on(\"click.chosen\",function(t){return function(e){return t.choice_destroy_link_click(e)}}(this)),s.append(i)),this.search_container.before(s)},n.prototype.choice_destroy_link_click=function(e){if(e.preventDefault(),e.stopPropagation(),!this.is_disabled)return this.choice_destroy(t(e.target))},n.prototype.choice_destroy=function(t){if(this.result_deselect(t[0].getAttribute(\"data-option-array-index\")))return this.active_field?this.search_field.focus():this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.get_search_field_value().length<1&&this.results_hide(),t.parents(\"li\").first().remove(),this.search_field_scale()},n.prototype.results_reset=function(){if(this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.trigger_form_field_change(),this.active_field)return this.results_hide()},n.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find(\"abbr\").remove()},n.prototype.result_select=function(t){var e,s;if(this.result_highlight)return e=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger(\"chosen:maxselected\",{chosen:this}),!1):(this.is_multiple?e.removeClass(\"active-result\"):this.reset_single_select_options(),e.addClass(\"result-selected\"),s=this.results_data[e[0].getAttribute(\"data-option-array-index\")],s.selected=!0,this.form_field.options[s.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(s):this.single_set_selected_text(this.choice_label(s)),this.is_multiple&&(!this.hide_results_on_select||t.metaKey||t.ctrlKey)?t.metaKey||t.ctrlKey?this.winnow_results({skip_highlight:!0}):(this.search_field.val(\"\"),this.winnow_results()):(this.results_hide(),this.show_search_field_default()),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.trigger_form_field_change({selected:this.form_field.options[s.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,t.preventDefault(),this.search_field_scale())},n.prototype.single_set_selected_text=function(t){return null==t&&(t=this.default_text),t===this.default_text?this.selected_item.addClass(\"chosen-default\"):(this.single_deselect_control_build(),this.selected_item.removeClass(\"chosen-default\")),this.selected_item.find(\"span\").html(t)},n.prototype.result_deselect=function(t){var e;return e=this.results_data[t],!this.form_field.options[e.options_index].disabled&&(e.selected=!1,this.form_field.options[e.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.trigger_form_field_change({deselected:this.form_field.options[e.options_index].value}),this.search_field_scale(),!0)},n.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect)return this.selected_item.find(\"abbr\").length||this.selected_item.find(\"span\").first().after('<abbr class=\"search-choice-close\"></abbr>'),this.selected_item.addClass(\"chosen-single-with-deselect\")},n.prototype.get_search_field_value=function(){return this.search_field.val()},n.prototype.get_search_text=function(){return t.trim(this.get_search_field_value())},n.prototype.escape_html=function(e){return t(\"<div/>\").text(e).html()},n.prototype.winnow_results_set_highlight=function(){var t,e;if(e=this.is_multiple?[]:this.search_results.find(\".result-selected.active-result\"),null!=(t=e.length?e.first():this.search_results.find(\".active-result\").first()))return this.result_do_highlight(t)},n.prototype.no_results=function(t){var e;return e=this.get_no_results_html(t),this.search_results.append(e),this.form_field_jq.trigger(\"chosen:no_results\",{chosen:this})},n.prototype.no_results_clear=function(){return this.search_results.find(\".no-results\").remove()},n.prototype.keydown_arrow=function(){var t;return this.results_showing&&this.result_highlight?(t=this.result_highlight.nextAll(\"li.active-result\").first())?this.result_do_highlight(t):void 0:this.results_show()},n.prototype.keyup_arrow=function(){var t;return this.results_showing||this.is_multiple?this.result_highlight?(t=this.result_highlight.prevAll(\"li.active-result\")).length?this.result_do_highlight(t.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight()):void 0:this.results_show()},n.prototype.keydown_backstroke=function(){var t;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find(\"a\").first()),this.clear_backstroke()):(t=this.search_container.siblings(\"li.search-choice\").last()).length&&!t.hasClass(\"search-choice-disabled\")?(this.pending_backstroke=t,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass(\"search-choice-focus\")):void 0},n.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass(\"search-choice-focus\"),this.pending_backstroke=null},n.prototype.search_field_scale=function(){var e,s,i,n,r,o,h;if(this.is_multiple){for(r={position:\"absolute\",left:\"-1000px\",top:\"-1000px\",display:\"none\",whiteSpace:\"pre\"},s=0,i=(o=[\"fontSize\",\"fontStyle\",\"fontWeight\",\"fontFamily\",\"lineHeight\",\"textTransform\",\"letterSpacing\"]).length;s<i;s++)r[n=o[s]]=this.search_field.css(n);return(e=t(\"<div />\").css(r)).text(this.get_search_field_value()),t(\"body\").append(e),h=e.width()+25,e.remove(),this.container.is(\":visible\")&&(h=Math.min(this.container.outerWidth()-10,h)),this.search_field.width(h)}},n.prototype.trigger_form_field_change=function(t){return this.form_field_jq.trigger(\"input\",t),this.form_field_jq.trigger(\"change\",t)},n}()}).call(this);","Magento_Cms/js/folder-tree.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'jquery',\n    'jquery/ui',\n    'jquery/jstree/jquery.jstree'\n], function ($) {\n    'use strict';\n\n    $.widget('mage.folderTree', {\n        options: {\n            root: 'root',\n            rootName: 'Root',\n            url: '',\n            currentPath: ['root'],\n            tree: {\n                core: {\n                    themes: {\n                        dots: false\n                    },\n                    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers\n                    check_callback: true\n                    // jscs:enable requireCamelCaseOrUpperCaseIdentifiers\n                }\n            }\n        },\n\n        /** @inheritdoc */\n        _create: function () {\n            var options = this.options,\n                treeOptions = $.extend(\n                    true,\n                    {},\n                    options.tree,\n                    {\n                        core: {\n                            data: {\n                                url: options.url,\n                                type: 'POST',\n                                dataType: 'text',\n                                dataFilter: $.proxy(function (data) {\n                                    return this._convertData(JSON.parse(data));\n                                }, this),\n\n                                /**\n                                 * @param {HTMLElement} node\n                                 * @return {Object}\n                                 */\n                                data: function (node) {\n                                    return {\n                                        node: node.id === 'root' ? null : node.id,\n                                        'form_key': window.FORM_KEY\n                                    };\n                                }\n                            }\n                        }\n                    }\n                );\n\n            this.element.jstree(treeOptions)\n                .on('ready.jstree', $.proxy(this.treeLoaded, this))\n                .on('load_node.jstree', $.proxy(this._createRootNode, this));\n        },\n\n        /**\n         * Tree loaded.\n         */\n        treeLoaded: function () {\n            var path = this.options.currentPath,\n                tree = this.element,\n                lastExistentFolderEl,\n\n                /**\n                 * Recursively open folders specified in path array.\n                 */\n                recursiveOpen = function () {\n                    var folderEl = $('[data-id=\"' + path.pop() + '\"]');\n\n                    // if folder doesn't exist, select the last opened folder\n                    if (!folderEl.length) {\n                        tree.jstree('select_node', lastExistentFolderEl);\n\n                        return;\n                    }\n\n                    lastExistentFolderEl = folderEl;\n\n                    if (path.length) {\n                        tree.jstree('open_node', folderEl, recursiveOpen);\n                    } else {\n                        tree.jstree('open_node', folderEl, function () {\n                            tree.jstree('select_node', folderEl);\n                        });\n                    }\n                };\n\n            recursiveOpen();\n        },\n\n        /**\n         * Create tree root node\n         *\n         * @param {jQuery.Event} event\n         * @param {Object} data\n         * @private\n         */\n        _createRootNode: function (event, data) {\n            var rootNode, children;\n\n            // jscs:disable requireCamelCaseOrUpperCaseIdentifiers\n            if (data.node.id === '#') {\n                rootNode = {\n                    id: this.options.root,\n                    text: this.options.rootName,\n                    li_attr: {\n                        'data-id': this.options.root\n                    }\n                };\n                children = data.node.children;\n\n                data.instance.element.jstree().create_node(null, rootNode, 'first', function () {\n                    data.instance.element.jstree().move_node(children, rootNode.id);\n                });\n            }\n            // jscs:enable requireCamelCaseOrUpperCaseIdentifiers\n        },\n\n        /**\n         * @param {*} data\n         * @return {*}\n         * @private\n         */\n        _convertData: function (data) {\n            return $.map(data, function (node) {\n\n                return {\n                    id: node.id,\n                    text: node.text,\n                    path: node.path,\n                    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers\n                    li_attr: {\n                        'data-id': node.id\n                    },\n                    // jscs:enable requireCamelCaseOrUpperCaseIdentifiers\n                    children: node.children\n                };\n            });\n        }\n    });\n\n    return $.mage.folderTree;\n});\n","Magento_Marketplace/default.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'jquery'\n], function ($) {\n    'use strict';\n\n    return function (configFromPage) {\n        var url = configFromPage.url;\n\n        $.ajax({\n            method: 'GET',\n            url: url\n        }).done(function (data) {\n            $('div[data-role=partners-block]').html(data);\n        });\n    };\n});\n","Magento_Translation/js/i18n-config.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\n(function () {\n    'use strict';\n\n    require.config({\n        config: {\n            'Magento_Ui/js/lib/knockout/bindings/i18n': {\n                inlineTranslation: true\n            }\n        }\n    });\n})();\n","Magento_Translation/js/mage-translation-dictionary.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\n\ndefine([\n    'text!js-translation.json'\n], function (dict) {\n    'use strict';\n\n    return JSON.parse(dict);\n});\n","Magento_InventoryGroupedProductAdminUi/js/form/element/grid-column-quantity-per-source.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'mage/translate',\n    'Magento_Ui/js/grid/columns/column'\n], function ($t, Column) {\n    'use strict';\n\n    return Column.extend({\n        defaults: {\n            bodyTmpl: 'Magento_InventoryGroupedProductAdminUi/grid/column/quantity-per-source',\n            itemsToDisplay: 3,\n            showFullListDescription: $t('Show more...')\n        },\n\n        /**\n         * Get source items from product data.\n         *\n         * @param {Object} rowData\n         * @returns {Array}\n         */\n        getSourceItemsData: function (rowData) {\n            return rowData['quantity_per_source'];\n        }\n    });\n});\n","Magento_InventoryGroupedProductAdminUi/js/form/element/quantity-per-source.js":"/**\n * Copyright \u00a9 Magento, Inc. All rights reserved.\n * See COPYING.txt for license details.\n */\ndefine([\n    'mage/translate',\n    'Magento_Ui/js/form/element/abstract'\n], function ($t, Abstract) {\n    'use strict';\n\n    return Abstract.extend({\n        defaults: {\n            elementTmpl: 'Magento_InventoryGroupedProductAdminUi/dynamic-rows/cells/cell-source',\n            itemsToDisplay: 3,\n            isFullList: true,\n            showFullListDescription: $t('Show more...'),\n            listens: {\n                value: 'updateItems'\n            }\n        },\n\n        /**\n         * Observe elements.\n         *\n         * @returns {exports}\n         */\n        initObservable: function () {\n            this._super()\n                .observe(['items', 'isFullList']);\n\n            return this;\n        },\n\n        /**\n         * Prepare data to use.\n         *\n         * @param {Object} data\n         * @private\n         */\n        updateItems: function (data) {\n            this.isFullList(data.length > this.itemsToDisplay);\n            this.isFullList() ? this.items(data.slice(0, this.itemsToDisplay)) : this.items(data);\n        }\n    });\n});\n"}
}});